From ee7863e43228a3143398dc5bbb943c9a735a8fca Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 31 Mar 2026 15:52:55 +0530 Subject: 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 @@ }; gpcdma: dma-controller@8400000 { - compatible = "nvidia,tegra264-gpcdma", "nvidia,tegra186-gpcdma"; + compatible = "nvidia,tegra264-gpcdma"; reg = <0x0 0x08400000 0x0 0x210000>; interrupts = , , -- cgit v1.2.3 From e81f1079f9000892cf54b23a9572ad5d86036fca Mon Sep 17 00:00:00 2001 From: Catalin Iacob Date: Mon, 8 Jun 2026 17:29:16 +0300 Subject: scsi: core: Remove export for scsi_device_from_queue() Commit 1cea5180f2f8 ("block: remove pktcdvd driver") left behind an export that is now dead code. Remove it and move the declaration of scsi_device_from_queue() to drivers/scsi/scsi_priv.h. Signed-off-by: Catalin Iacob Link: https://patch.msgid.link/20260608-remove-pktcdvd-references-v4-1-72f88b04cc87@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 8 -------- drivers/scsi/scsi_priv.h | 1 + include/scsi/scsi_device.h | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 85eef401925a..b67f0dc79499 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2224,14 +2224,6 @@ struct scsi_device *scsi_device_from_queue(struct request_queue *q) return sdev; } -/* - * pktcdvd should have been integrated into the SCSI layers, but for historical - * reasons like the old IDE driver it isn't. This export allows it to safely - * probe if a given device is a SCSI one and only attach to that. - */ -#ifdef CONFIG_CDROM_PKTCDVD_MODULE -EXPORT_SYMBOL_GPL(scsi_device_from_queue); -#endif /** * scsi_block_requests - Utility function used by low-level drivers to prevent diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 7a193cc04e5b..37e5601be2b8 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -102,6 +102,7 @@ void scsi_eh_done(struct scsi_cmnd *scmd); /* scsi_lib.c */ extern void scsi_device_unbusy(struct scsi_device *sdev, struct scsi_cmnd *cmd); +extern struct scsi_device *scsi_device_from_queue(struct request_queue *q); extern void scsi_queue_insert(struct scsi_cmnd *cmd, enum scsi_qc_status reason); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 029f5115b2ea..8694eeadd753 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -408,7 +408,6 @@ void scsi_attach_vpd(struct scsi_device *sdev); void scsi_cdl_check(struct scsi_device *sdev); int scsi_cdl_enable(struct scsi_device *sdev, bool enable); -extern struct scsi_device *scsi_device_from_queue(struct request_queue *q); extern int __must_check scsi_device_get(struct scsi_device *); extern void scsi_device_put(struct scsi_device *); extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *, -- cgit v1.2.3 From ca978f8a93d4d36841839bf2847d29b88c2591d6 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 11 Jun 2026 08:30:45 -0400 Subject: scsi: xen: scsiback: Free unsubmitted command instead of double-putting it scsiback_get_pend_req() obtains a command tag and returns a vscsibk_pend whose embedded se_cmd has only been memset to 0, so its cmd_kref is 0; the se_cmd is initialised (kref_init() via target_init_cmd()) only later, in scsiback_cmd_exec(), on the successful VSCSIIF_ACT_SCSI_CDB path. The two error paths in scsiback_do_cmd_fn() taken before the command is submitted -- a failed scsiback_gnttab_data_map() and an unknown ring_req.act -- call transport_generic_free_cmd(&pending_req->se_cmd, 0), which kref_put()s a refcount of 0. That underflows it ("refcount_t: underflow; use-after-free") and, as the release function is not run, leaks the command tag. Impact: a pvSCSI guest can leak every command tag of a LUN's session, stopping the LUN, by submitting requests with a bad grant reference or an unknown request type; under panic_on_warn the refcount underflow panics the host. Add a helper that just returns the tag with target_free_tag() and sends the error response. It frees the tag while the v2p reference still pins the session, and snapshots the response fields beforehand because freeing the tag can let another ring reuse the pending_req slot. Fixes: 2dbcdf33dbf6 ("xen-scsiback: Convert to percpu_ida tag allocation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Juergen Gross Link: https://patch.msgid.link/20260611123046.2323342-2-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen --- drivers/xen/xen-scsiback.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index e33f95c91b09..f324732eba7f 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -611,6 +611,25 @@ static void scsiback_disconnect(struct vscsibk_info *info) xenbus_unmap_ring_vfree(info->dev, info->ring.sring); } +/* + * Send the error response for a request that did not reach the target core + * and return its tag. Free the tag before the response drops the v2p + * reference that keeps the session alive, and snapshot what the response + * needs since returning the tag can let the slot be reused. + */ +static void scsiback_resp_and_free(struct vscsibk_pend *pending_req, + int32_t result) +{ + struct vscsibk_info *info = pending_req->info; + struct v2p_entry *v2p = pending_req->v2p; + struct se_session *se_sess = v2p->tpg->tpg_nexus->tvn_se_sess; + u16 rqid = pending_req->rqid; + + target_free_tag(se_sess, &pending_req->se_cmd); + scsiback_send_response(info, NULL, result, 0, rqid); + kref_put(&v2p->kref, scsiback_free_translation_entry); +} + static void scsiback_device_action(struct vscsibk_pend *pending_req, enum tcm_tmreq_table act, int tag) { @@ -792,9 +811,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info, case VSCSIIF_ACT_SCSI_CDB: if (scsiback_gnttab_data_map(&ring_req, pending_req)) { scsiback_fast_flush_area(pending_req); - scsiback_do_resp_with_sense(NULL, - DID_ERROR << 16, 0, pending_req); - transport_generic_free_cmd(&pending_req->se_cmd, 0); + scsiback_resp_and_free(pending_req, + DID_ERROR << 16); } else { scsiback_cmd_exec(pending_req); } @@ -808,9 +826,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info, break; default: pr_err_ratelimited("invalid request\n"); - scsiback_do_resp_with_sense(NULL, DID_ERROR << 16, 0, - pending_req); - transport_generic_free_cmd(&pending_req->se_cmd, 0); + scsiback_resp_and_free(pending_req, DID_ERROR << 16); break; } -- cgit v1.2.3 From 66aefc277ebb796ec285d550305535dc3fc0179f Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 11 Jun 2026 08:30:46 -0400 Subject: scsi: xen: scsiback: Free the command tag on the TMR submit-failure path scsiback_device_action() obtains a command tag in scsiback_get_pend_req() and submits a task-management request with target_submit_tmr(). When target_submit_tmr() fails it returns < 0 and scsiback jumps to the err: label, which sends a response but frees nothing, leaking the tag. Impact: a pvSCSI guest can leak the command tags of a LUN's session, stopping the LUN, by issuing VSCSIIF_ACT_SCSI_ABORT or RESET requests whenever target_submit_tmr() fails. transport_generic_free_cmd() cannot be used here. By the time target_submit_tmr() returns an error it has already run __target_init_cmd() (so se_cmd->cmd_kref is one, not zero), and on its target_get_sess_cmd() error path it has freed se_cmd->se_tmr_req via core_tmr_release_req() while leaving SCF_SCSI_TMR_CDB set and the pointer dangling. Letting the command release run target_free_cmd_mem() would then double-free se_tmr_req. Use the same helper, which returns just the tag, on this path too. Fixes: 2dbcdf33dbf6 ("xen-scsiback: Convert to percpu_ida tag allocation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Juergen Gross Link: https://patch.msgid.link/20260611123046.2323342-3-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen --- drivers/xen/xen-scsiback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index f324732eba7f..c7036e0e41bd 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -658,7 +658,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req, return; err: - scsiback_do_resp_with_sense(NULL, err, 0, pending_req); + scsiback_resp_and_free(pending_req, err); } /* -- cgit v1.2.3 From 57a6ed0b41677ccc5e28cc0976e495c1dfa33747 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 14:55:56 +0200 Subject: scsi: bfa: Reduce kernel stack usage in bfa_fcs_lport_fdmi_build_portattr_block() bfa_fcs_fdmi_get_portattr() gets inlined into multiple places and has two fairly large variables on the stack, to the point of causing a warning in some randconfig builds: drivers/scsi/bfa/bfa_fcs_lport.c:2198:1: error: stack frame size (1560) exceeds limit (1280) in 'bfa_fcs_lport_fdmi_build_portattr_block' [-Werror,-Wframe-larger-than] 2198 | bfa_fcs_lport_fdmi_build_portattr_block(struct bfa_fcs_lport_fdmi_s *fdmi, | ^ drivers/scsi/bfa/bfa_fcs_lport.c:1856:1: error: stack frame size (1600) exceeds limit (1280) in 'bfa_fcs_lport_fdmi_build_rhba_pyld' [-Werror,-Wframe-larger-than] 1856 | bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi, u8 *pyld) | ^ Mark the inner function as noinline_for_stack to keep it separate from the other variables and prevent multiple copies of the same variable to get inlined here. Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260611125601.3385418-1-arnd@kernel.org Signed-off-by: Martin K. Petersen --- drivers/scsi/bfa/bfa_fcs_lport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c index 2df399c537c1..8c9d423129c0 100644 --- a/drivers/scsi/bfa/bfa_fcs_lport.c +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -2627,7 +2627,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi, } -static void +static noinline_for_stack void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_lport_fdmi_s *fdmi, struct bfa_fcs_fdmi_port_attr_s *port_attr) { -- cgit v1.2.3 From 7258770e5814f15e8308ebda82ac9acf6964ba8e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 15 Jun 2026 19:16:25 +0100 Subject: 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 @@ retry: 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 @@ retry: 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); -- cgit v1.2.3 From 0074b82cdfcb5fd13710a0ac308ade68ac6f6fbe Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 5 Jun 2026 05:59:15 +0900 Subject: 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 @@ retry: 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); -- cgit v1.2.3 From ff1022c3de46753eb7eba2f6efd990569e66ff95 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 17 Jun 2026 12:08:21 +0800 Subject: 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; } -- cgit v1.2.3 From 9f1667098c6ae7ec81a9a56859cfdacb822aa0d0 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Sun, 14 Jun 2026 22:13:24 -0700 Subject: 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) -- cgit v1.2.3 From e2cb1f4578625e71f461d5c1ce70984193389cbb Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 15 Jun 2026 14:11:16 +0100 Subject: 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); -- cgit v1.2.3 From ec40342aaca8162bc8ab2607076535ebab1838b8 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Fri, 12 Jun 2026 12:34:14 +0100 Subject: 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); } -- cgit v1.2.3 From 0e8e955b9bcf84a70f20079390e19971fec1586d Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Wed, 17 Jun 2026 15:49:07 +0100 Subject: 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 | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 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))) { + u64 mask; + /* - * 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. + * 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. */ - spsr = *vcpu_cpsr(vcpu); + 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 &= (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); + spsr |= *vcpu_cpsr(vcpu) & mask; spsr |= PSR_IL_BIT; } -- cgit v1.2.3 From fcda30b134d262a1b1826555352bd7349051486e Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Wed, 17 Jun 2026 14:12:36 +0100 Subject: 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 -- cgit v1.2.3 From e60d3f9b4c89ffff5472ddc2829d5e29bcb153fe Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 Jun 2026 21:23:14 +0200 Subject: 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) { -- cgit v1.2.3 From 813e034925814858cc52e7de321ec4848314e15d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Jun 2026 16:36:50 +0200 Subject: 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) -- cgit v1.2.3 From 265d5d4032c5f6eb089a6e6241d37fdbde7da180 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 4 Jun 2026 03:36:17 -0700 Subject: 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; } -- cgit v1.2.3 From 2684e02bac41c5220f6c1ab2bdcc957b71812977 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:02 -0700 Subject: 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; -- cgit v1.2.3 From 9f3e83345a56280efffe235c65593c7e544c0fcc Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:03 -0700 Subject: 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); -- cgit v1.2.3 From bb645aa0a4caeaf7f9cd32e9a948594d434c1a8f Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:04 -0700 Subject: 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 | 111 ++++++++++++++++++------------------------------ 1 file changed, 42 insertions(+), 69 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; + 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; + } - 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); + 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 -EPERM: - /* Hack to deal with POE until we get kernel support */ + 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; - case 0: - break; - } - } else { - WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr); + break; } return 1; -- cgit v1.2.3 From 4bd7dbe0b2243e6aa735cae4d5e1ff988b30b2a6 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:05 -0700 Subject: 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); -- cgit v1.2.3 From 265b58aba51b6aaaad81678fbc57fcdb2d4ed480 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Thu, 18 Jun 2026 16:42:06 -0700 Subject: 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); -- cgit v1.2.3 From daa71eca24fdfb43029830bd57ddaddf70c59b23 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:37 +0100 Subject: 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, -- cgit v1.2.3 From 1d695dc827957e9570d1b56abac1250d2d13bf0c Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:38 +0100 Subject: 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); } -- cgit v1.2.3 From add40af98b34764ff5603dce297160fde12d784c Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:39 +0100 Subject: 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 -- cgit v1.2.3 From a52d6d68ad30374dd794bff300d8538e35ee49a8 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:40 +0100 Subject: 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; -- cgit v1.2.3 From 7514f1785d526207af8512cc6ccb1c35c5c61767 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:41 +0100 Subject: 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; } -- cgit v1.2.3 From a69412287a33c931dca9e48d30c0dbf8cde0ffe6 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:42 +0100 Subject: 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); } -- cgit v1.2.3 From cbe2278aa3dd6832c544782c6cfed1fbc1f71a42 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Thu, 18 Jun 2026 13:16:43 +0100 Subject: 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 */ -- cgit v1.2.3 From d098bb75d14fde2f12155f1a95ec0168160867ce Mon Sep 17 00:00:00 2001 From: Bradley Morgan Date: Sun, 21 Jun 2026 21:31:55 +0000 Subject: 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); } -- cgit v1.2.3 From 314c243b201b678fa89226b1eaea51a71340454e Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 8 Jun 2026 10:21:50 +0200 Subject: 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 -- cgit v1.2.3 From 4cd5de72b6f8951cef6b45c177a582824bc13d46 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 24 Jun 2026 02:27:43 -0700 Subject: 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; } -- cgit v1.2.3 From 46d65096ce8d278abf4528e254878c14ddd0b459 Mon Sep 17 00:00:00 2001 From: Doehyun Baek Date: Sat, 20 Jun 2026 12:27:51 +0000 Subject: 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. -- cgit v1.2.3 From 5771e79e461e72140b752496b69275b9e6bd1a75 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Mon, 8 Jun 2026 18:55:57 +0200 Subject: 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 -- cgit v1.2.3 From 5c94a3ab6ed94ff1257631a49893a535098be0b6 Mon Sep 17 00:00:00 2001 From: Kuba Piecuch Date: Fri, 19 Jun 2026 13:23:59 +0000 Subject: 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 * -- cgit v1.2.3 From 115d1ce989747045bd7745c7ab020982660c7e42 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 24 Jun 2026 12:40:51 -1000 Subject: 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)); -- cgit v1.2.3 From 0dfa1e960f86e032007882b032c5cc7d14ebe73e Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Wed, 21 Jan 2026 16:15:34 +0530 Subject: 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 @@ #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_1: cpu@100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00100>; @@ -5393,7 +5393,7 @@ }; cpu0_2: cpu@200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00200>; @@ -5412,7 +5412,7 @@ }; cpu0_3: cpu@300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x00300>; @@ -5431,7 +5431,7 @@ }; cpu1_0: cpu@10000 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10000>; @@ -5450,7 +5450,7 @@ }; cpu1_1: cpu@10100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10100>; @@ -5469,7 +5469,7 @@ }; cpu1_2: cpu@10200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10200>; @@ -5488,7 +5488,7 @@ }; cpu1_3: cpu@10300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x10300>; @@ -5507,7 +5507,7 @@ }; cpu2_0: cpu@20000 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20000>; @@ -5526,7 +5526,7 @@ }; cpu2_1: cpu@20100 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20100>; @@ -5545,7 +5545,7 @@ }; cpu2_2: cpu@20200 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20200>; @@ -5564,7 +5564,7 @@ }; cpu2_3: cpu@20300 { - compatible = "arm,cortex-a78"; + compatible = "arm,cortex-a78ae"; device_type = "cpu"; reg = <0x20300>; -- cgit v1.2.3 From 806a66f926c2b6652aeb88983d01f25081b41a73 Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Wed, 21 Jan 2026 16:15:35 +0530 Subject: 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 @@ d-cache-sets = <256>; }; - cpu1: cpu@1 { + cpu1: cpu@10000 { compatible = "arm,neoverse-v3ae"; device_type = "cpu"; reg = <0x10000>; -- cgit v1.2.3 From 31620fc1c81078746c4794e0eb6de539ab37e695 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 16 Jun 2026 13:09:16 +0300 Subject: 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 --- .../ABI/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 @@ err_unlock: 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; -- cgit v1.2.3 From 6eba58568f6cc3ff8515a00b05e258d8cfb72b72 Mon Sep 17 00:00:00 2001 From: Jared Baldridge Date: Sat, 30 May 2026 18:19:48 -0400 Subject: 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 */ }, -- cgit v1.2.3 From fc591787785b9709a0bb65a7df3ba2537d611c47 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Tue, 2 Jun 2026 14:45:08 -0600 Subject: 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 }, -- cgit v1.2.3 From bd728c3d9b1cc0bb0fda6a7055c5c8b55d7477b2 Mon Sep 17 00:00:00 2001 From: Rodrigo Lugathe da Conceição Alves Date: Wed, 3 Jun 2026 08:36:26 -0300 Subject: 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 }, -- cgit v1.2.3 From 2c00e09e3f9f06f8434f5ea2ee6179ce46692ee6 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 2 Jun 2026 13:38:42 +0800 Subject: 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) { -- cgit v1.2.3 From 8af6812795869a66e9b26044f455b13deecdb69c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 8 Jun 2026 16:58:03 +0200 Subject: 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; } -- cgit v1.2.3 From d092d7edf8faefa3e27b9fc7f0e7904b06c833a2 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 1 Jun 2026 14:28:37 +0000 Subject: 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; -- cgit v1.2.3 From baa6b6068a3f2bf2ed525a1cb37975905dadc658 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 9 Jun 2026 17:29:05 +0200 Subject: 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. */ -- cgit v1.2.3 From 81fe702ff1760da32bcd3ef4494b2a33dbeced72 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 25 Jun 2026 12:45:47 +0800 Subject: 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 @@ flush_bio: } 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 @@ flush_bio: 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 -- cgit v1.2.3 From 3348f444a4ce43dd5c2d1aa41634cb6eff33aa64 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 13:26:27 +0800 Subject: 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 @@ cleanup_rings: } } + cdnsp_free_stream_ctx(pdev, pep); + cleanup_stream_rings: kfree(pep->stream_info.stream_rings); -- cgit v1.2.3 From 3137b243c93982fe3460335e12f9247739766e10 Mon Sep 17 00:00:00 2001 From: Tyler Baker Date: Tue, 9 Jun 2026 15:36:34 -0400 Subject: 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; } -- cgit v1.2.3 From bc0e4f16c44e50daa0b1ea729934baa3b4815dee Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sat, 23 May 2026 19:05:23 +0200 Subject: 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); -- cgit v1.2.3 From c602254ba4c10f60a73cd99d147874f86a3f485c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:09 +0200 Subject: 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 @@ out: 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 */ -- cgit v1.2.3 From ff002c153f9722caece3983cc23dc4d9d4652cb4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:10 +0200 Subject: 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"); } -- cgit v1.2.3 From 19bdfc7b3c179331eafa423d87e1336f43bbfeb8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:11 +0200 Subject: 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 @@ exit: 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)); } -- cgit v1.2.3 From 62fc8eb1b1481051f7bab4aa93d79809053dd09f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 22 Jun 2026 17:26:12 +0200 Subject: 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 @@ exit: 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)); } -- cgit v1.2.3 From 7b681dd5fbf60b24a13c14661e5b7735759fb491 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 22 Jun 2026 22:08:03 +0000 Subject: 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() */ -- cgit v1.2.3 From 1f0bdc2884b67de337215079bba166df0cdf4ac5 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Tue, 16 Jun 2026 13:20:11 +0000 Subject: 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[] = { -- cgit v1.2.3 From 82cfd4739011bdc7e87b5d585703427e89ddfaa5 Mon Sep 17 00:00:00 2001 From: Neill Kapron Date: Fri, 19 Jun 2026 04:06:03 +0000 Subject: 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; -- cgit v1.2.3 From 8bdcf96eb135aebacac319667f87db034fb38406 Mon Sep 17 00:00:00 2001 From: Neill Kapron Date: Fri, 19 Jun 2026 04:06:04 +0000 Subject: 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); } -- cgit v1.2.3 From e2674dfbed8a30d57e2bc872c4bfa6c3eec918bf Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Tue, 26 May 2026 14:09:44 -0300 Subject: 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 @@ err_fw_corrupted: 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); } } -- cgit v1.2.3 From f8f680609c2b3ab795ffcd6f21585b6dfc46d395 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 27 May 2026 23:08:32 +0800 Subject: 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; -- cgit v1.2.3 From 692c354bef03b77b30e57e61934da502c8a12d45 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 21:11:21 +0800 Subject: 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 = { -- cgit v1.2.3 From 0bddda5a11665c210339de76d27ebbd1a2e0b43c Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 17:33:25 +0800 Subject: 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; } -- cgit v1.2.3 From 8c6314489550fa81d41723a0ff33f655b5b6c7b6 Mon Sep 17 00:00:00 2001 From: "HE WEI (ギカク)" Date: Wed, 24 Jun 2026 18:09:52 +0900 Subject: 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 @@ read: 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; -- cgit v1.2.3 From 6860b467f569f732b11cbc588ae7e195e90e7e23 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sun, 21 Jun 2026 12:03:26 +0200 Subject: 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 @@ out_free: 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); -- cgit v1.2.3 From 3f4c3919baf0944ad96580467c302bc6c7758b00 Mon Sep 17 00:00:00 2001 From: Petr Wozniak Date: Sun, 21 Jun 2026 12:03:27 +0200 Subject: 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); -- cgit v1.2.3 From 30e542a36228db353e81efcd39e4dbc7a95c88c5 Mon Sep 17 00:00:00 2001 From: Anuj Gupta Date: Wed, 17 Jun 2026 21:20:51 +0530 Subject: 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; } -- cgit v1.2.3 From 464fe6c0cc9437bc91f6e880b666d37b424e3d7b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 25 Jun 2026 16:08:34 +0100 Subject: 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 -- cgit v1.2.3 From 946352b2f88fd2378f0341312e47dff1e8dc2fac Mon Sep 17 00:00:00 2001 From: Daniele Briguglio Date: Wed, 24 Jun 2026 14:39:14 +0200 Subject: clk: rockchip: rk3588: don't disable unused I2S MCLK output gates No in-tree board references these gates yet. Boards drive the codec MCLK through the parent I2S*_8CH_MCLKOUT, and now that the gates are managed clocks, clk_disable_unused() turns them off at boot. On a board that relied on firmware leaving the output enabled, that cuts the MCLK and analog audio stops working. Mark the four gates CLK_IGNORE_UNUSED so an unreferenced gate keeps the state firmware left. A board that wants the kernel to own the gate can reference I2S*_8CH_MCLKOUT_TO_IO from DT instead. Fixes: 02b9b0bb6269 ("clk: rockchip: rk3588: add GATE_GRF clocks for I2S MCLK output to IO") Reported-by: Diederik de Haas Closes: https://lore.kernel.org/linux-rockchip/DJGDSS875DDO.22TYPVYK5X8KZ@cknow-tech.com/ Tested-by: Diederik de Haas Signed-off-by: Daniele Briguglio Tested-by: Ricardo Pardini # on R58X-Pro+NanoPC-T6 Reviewed-by: Sebastian Reichel Link: https://patch.msgid.link/20260624123914.1767374-1-hello@superkali.me Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3588.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c index 2ba9976654cf..86953f9ffee3 100644 --- a/drivers/clk/rockchip/clk-rk3588.c +++ b/drivers/clk/rockchip/clk-rk3588.c @@ -895,7 +895,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { MUX(I2S2_2CH_MCLKOUT, "i2s2_2ch_mclkout", i2s2_2ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(30), 2, 1, MFLAGS), GATE_GRF(I2S2_2CH_MCLKOUT_TO_IO, "i2s2_2ch_mclkout_to_io", "i2s2_2ch_mclkout", - 0, RK3588_SYSGRF_SOC_CON6, 2, GFLAGS, grf_type_sys), + CLK_IGNORE_UNUSED, RK3588_SYSGRF_SOC_CON6, 2, GFLAGS, grf_type_sys), COMPOSITE(CLK_I2S3_2CH_SRC, "clk_i2s3_2ch_src", gpll_aupll_p, 0, RK3588_CLKSEL_CON(30), 8, 1, MFLAGS, 3, 5, DFLAGS, @@ -912,7 +912,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { MUX(I2S3_2CH_MCLKOUT, "i2s3_2ch_mclkout", i2s3_2ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(32), 2, 1, MFLAGS), GATE_GRF(I2S3_2CH_MCLKOUT_TO_IO, "i2s3_2ch_mclkout_to_io", "i2s3_2ch_mclkout", - 0, RK3588_SYSGRF_SOC_CON6, 7, GFLAGS, grf_type_sys), + CLK_IGNORE_UNUSED, RK3588_SYSGRF_SOC_CON6, 7, GFLAGS, grf_type_sys), GATE(PCLK_ACDCDIG, "pclk_acdcdig", "pclk_audio_root", 0, RK3588_CLKGATE_CON(7), 11, GFLAGS), GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_audio_root", 0, @@ -942,7 +942,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { MUX(I2S0_8CH_MCLKOUT, "i2s0_8ch_mclkout", i2s0_8ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(28), 2, 2, MFLAGS), GATE_GRF(I2S0_8CH_MCLKOUT_TO_IO, "i2s0_8ch_mclkout_to_io", "i2s0_8ch_mclkout", - 0, RK3588_SYSGRF_SOC_CON6, 0, GFLAGS, grf_type_sys), + CLK_IGNORE_UNUSED, RK3588_SYSGRF_SOC_CON6, 0, GFLAGS, grf_type_sys), GATE(HCLK_PDM1, "hclk_pdm1", "hclk_audio_root", 0, RK3588_CLKGATE_CON(9), 6, GFLAGS), @@ -2229,7 +2229,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { MUX(I2S1_8CH_MCLKOUT, "i2s1_8ch_mclkout", i2s1_8ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_PMU_CLKSEL_CON(9), 2, 2, MFLAGS), GATE_GRF(I2S1_8CH_MCLKOUT_TO_IO, "i2s1_8ch_mclkout_to_io", "i2s1_8ch_mclkout", - 0, RK3588_SYSGRF_SOC_CON6, 1, GFLAGS, grf_type_sys), + CLK_IGNORE_UNUSED, RK3588_SYSGRF_SOC_CON6, 1, GFLAGS, grf_type_sys), GATE(PCLK_PMU1, "pclk_pmu1", "pclk_pmu0_root", CLK_IS_CRITICAL, RK3588_PMU_CLKGATE_CON(1), 0, GFLAGS), GATE(CLK_DDR_FAIL_SAFE, "clk_ddr_fail_safe", "clk_pmu0", CLK_IGNORE_UNUSED, -- cgit v1.2.3 From 667d0fb32149f023b8b34a1f6f3d384556eafb5a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 23 Jun 2026 12:00:22 -0700 Subject: driver core: add missing kernel-doc for union members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use of __SYSFS_FUNCTION_ALTERNATIVE() adds an anonymous union (or struct if CONFIG_CFI=y).* Describe the additional struct/union members to avoid docs build warnings. Warning: include/linux/device.h:117 struct member 'show_const' not described in 'device_attribute' Warning: include/linux/device.h:117 struct member 'store_const' not described in 'device_attribute' *: kernel-doc ignores CONFIG_ symbols in source files; it is using the first definition of __SYSFS_FUNCTION_ALTERNATIVE(), which is struct instead of union. Fixes: 434506b86a6c ("driver core: Allow the constification of device attributes") Signed-off-by: Randy Dunlap Reviewed-by: Thomas Weißschuh Link: https://patch.msgid.link/20260623190023.407781-1-rdunlap@infradead.org Signed-off-by: Danilo Krummrich --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/device.h b/include/linux/device.h index 7b2baffdd2f5..aee79fd6b32b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -99,7 +99,9 @@ struct device_type { * struct device_attribute - Interface for exporting device attributes. * @attr: sysfs attribute definition. * @show: Show handler. + * @show_const: Show handler (read-only). * @store: Store handler. + * @store_const: Store handler (read-only). */ struct device_attribute { struct attribute attr; -- cgit v1.2.3 From 52c7084c8fe57c259e50ff0a7d4f99ccecfc4c7a Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 11 Jun 2026 19:00:44 +0000 Subject: arm64: dts: qcom: glymur: fix PCIe SMMU interrupts The PCIe SMMUv3 wired interrupts are routed to GIC extended SPI INTIDs 4100, 4098 and 4096. Describe them as ESPIs with the ESPI-relative interrupt numbers instead of regular SPIs 964, 962 and 960. Assisted-by: Codex:GPT-5.5 Fixes: 41b6e8db400c ("arm64: dts: qcom: Introduce Glymur base dtsi") Signed-off-by: Bjorn Andersson Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260611-glymur-pcie-smmu-espi-v1-1-5effbaa3e1af@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/glymur.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index 20b49af7298e..c7edfa8156f3 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -6874,9 +6874,9 @@ pcie_smmu: iommu@15480000 { compatible = "arm,smmu-v3"; reg = <0x0 0x15480000 0x0 0x20000>; - interrupts = , - , - ; + interrupts = , + , + ; interrupt-names = "eventq", "cmdq-sync", "gerror"; dma-coherent; #iommu-cells = <1>; -- cgit v1.2.3 From a74a98f956ef8e075f28d58507bc5dad7f937fb4 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 11 Jun 2026 17:22:37 +0000 Subject: arm64: dts: qcom: glymur: fix QUP serial engine IRQs The Geni serial-engine interrupts from QUP wrapper 0 all fall in ESPI INTIDs space. While some of the i2c instances has gotten their interrupt specifiers corrected, even the other functions on the same serial-engines are wrong. Ensure that all the serial engine interrupts for QUP wrapper 0 matches the datasheet. Assisted-by: Codex:GPT-5.5 Fixes: 41b6e8db400c ("arm64: dts: qcom: Introduce Glymur base dtsi") Signed-off-by: Bjorn Andersson Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260611-glymur-geni-irqs-v1-1-269428faeb6d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/glymur.dtsi | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index c7edfa8156f3..129be417ac42 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -1876,7 +1876,7 @@ spi0: spi@b80000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b80000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -1903,7 +1903,7 @@ i2c1: i2c@b84000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x00b84000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -1930,7 +1930,7 @@ spi1: spi@b84000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b84000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -1957,7 +1957,7 @@ i2c2: i2c@b88000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x00b88000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -1984,7 +1984,7 @@ spi2: spi@b88000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b88000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2011,7 +2011,7 @@ uart2: serial@b88000 { compatible = "qcom,geni-uart"; reg = <0x0 0x00b88000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2056,7 +2056,7 @@ spi3: spi@b8c000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b8c000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2110,7 +2110,7 @@ spi4: spi@b90000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b90000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2164,7 +2164,7 @@ spi5: spi@b94000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b94000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2191,7 +2191,7 @@ i2c6: i2c@b98000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x00b98000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2218,7 +2218,7 @@ spi6: spi@b98000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b98000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2245,7 +2245,7 @@ i2c7: i2c@b9c000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x00b9c000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2272,7 +2272,7 @@ spi7: spi@b9c000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b9c000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS -- cgit v1.2.3 From 2ef00630c5c0b7b2c08aba7643f47594952d357e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 23 Jun 2026 13:56:45 +0200 Subject: clk: qcom: dispcc-eliza: Fix disp_cc_mdss_mdp_clk_src RCG stall on Eliza EVK Eliza EVK (eliza-cqs-evk.dts) does not have display enabled, however its Display Clock Controller is enabled and references parent clocks from DSI PHYs, which causes clock reparenting issues during probe (init) and warning on Eliza EVK: disp_cc_mdss_mdp_clk_src: rcg didn't update its configuration. WARNING: drivers/clk/qcom/clk-rcg2.c:136 at update_config+0xd4/0xe4, CPU#1: udevd/273 ... update_config (drivers/clk/qcom/clk-rcg2.c:136 (discriminator 2)) (P) clk_rcg2_shared_disable (drivers/clk/qcom/clk-rcg2.c:1471) clk_rcg2_shared_init (drivers/clk/qcom/clk-rcg2.c:1540) __clk_register (drivers/clk/clk.c:3959 drivers/clk/clk.c:4368) devm_clk_hw_register (drivers/clk/clk.c:4448 (discriminator 1) drivers/clk/clk.c:4672 (discriminator 1)) devm_clk_register_regmap (drivers/clk/qcom/clk-regmap.c:104) qcom_cc_really_probe (drivers/clk/qcom/common.c:418) qcom_cc_probe (drivers/clk/qcom/common.c:445) disp_cc_eliza_probe (dispcc-eliza.c:?) dispcc_eliza platform_probe (drivers/base/platform.c:1432) Fixes: 0e66f10942b5 ("clk: qcom: dispcc-eliza: Add Eliza display clock controller support") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Reviewed-by: Taniya Das Link: https://lore.kernel.org/r/20260623115644.392477-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/dispcc-eliza.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/dispcc-eliza.c b/drivers/clk/qcom/dispcc-eliza.c index 479f26e0dde2..e31c24125274 100644 --- a/drivers/clk/qcom/dispcc-eliza.c +++ b/drivers/clk/qcom/dispcc-eliza.c @@ -757,7 +757,7 @@ static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { .parent_data = disp_cc_parent_data_11, .num_parents = ARRAY_SIZE(disp_cc_parent_data_11), .flags = CLK_SET_RATE_PARENT, - .ops = &clk_rcg2_shared_ops, + .ops = &clk_rcg2_shared_no_init_park_ops, }, }; -- cgit v1.2.3 From 650c88738ae8976f46ba71b24dd8aa311adc6fde Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Wed, 17 Jun 2026 14:51:24 +0000 Subject: optee: ffa: Add NULL check in optee_ffa_lend_protmem Sashiko (locally) reports a possible null dereference under memory pressure due to the lack of validation of the allocated pointer. Fix that by adding the missing check. Fixes: 2b78d79cdf96 ("optee: FF-A: dynamic protected memory allocation") Signed-off-by: Mostafa Saleh Signed-off-by: Sebastian Ene Reviewed-by: Sumit Garg Reviewed-by: Sebastian Ene Signed-off-by: Jens Wiklander --- drivers/tee/optee/ffa_abi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index b4372fa268d0..633715b98625 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -698,6 +698,9 @@ static int optee_ffa_lend_protmem(struct optee *optee, struct tee_shm *protmem, int rc; mem_attr = kzalloc_objs(*mem_attr, ma_count); + if (!mem_attr) + return -ENOMEM; + for (n = 0; n < ma_count; n++) { mem_attr[n].receiver = mem_attrs[n] & U16_MAX; mem_attr[n].attrs = mem_attrs[n] >> 16; -- cgit v1.2.3 From 24ca1fea8f2753bf33e1d458ec1ae5d9b7796a65 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:12:29 +0200 Subject: 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 */ -- cgit v1.2.3 From 5c1ea24b53bf3bfb859f0a05573997487975da23 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:11:10 +0200 Subject: 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) -- cgit v1.2.3 From 46c8beeccd8ab2c863827254a85ea877654a3534 Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 22:27:00 +0545 Subject: HID: letsketch: fix UAF on inrange_timer at driver unbind letsketch_driver does not provide a .remove callback, but letsketch_probe() arms a per-device timer: timer_setup(&data->inrange_timer, letsketch_inrange_timeout, 0); The timer is re-armed from letsketch_raw_event() with a 100 ms timeout on every pen-in-range report, and its callback dereferences data->input_tablet to deliver a synthetic BTN_TOOL_PEN release. letsketch_data is allocated with devm_kzalloc(), and its input_dev fields are devm-allocated via letsketch_setup_input_tablet(). On device unbind (USB unplug or rmmod), the HID core runs its default teardown and devm cleanup frees both letsketch_data and the input devices. Because no .remove callback exists, nothing drains the timer first: if raw_event armed it within ~100 ms of the unbind, the pending timer fires on freed memory. This is a UAF read of data and of data->input_tablet, followed by input_report_key() / input_sync() into the freed input_dev. The same problem can occur on the probe error path: if hid_hw_start() enabled I/O on an always-poll-quirk device and then failed, raw_event may have armed the timer before devm releases data. Fix by adding a .remove callback that calls hid_hw_stop() first. hid_hw_stop() synchronously kills the URBs that deliver raw_event(), so once it returns no path can re-arm the timer. timer_shutdown_sync() then drains any in-flight callback and permanently disables further mod_timer() calls. Apply the same timer_shutdown_sync() in the probe error path so the timer is guaranteed not to outlive data. Fixes: 33a5c2793451 ("HID: Add new Letsketch tablet driver") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina --- drivers/hid/hid-letsketch.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c index 11e21f988723..b52e93a91ae5 100644 --- a/drivers/hid/hid-letsketch.c +++ b/drivers/hid/hid-letsketch.c @@ -296,13 +296,42 @@ static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id * ret = letsketch_setup_input_tablet(data); if (ret) - return ret; + goto err_shutdown_timer; ret = letsketch_setup_input_tablet_pad(data); if (ret) - return ret; + goto err_shutdown_timer; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + goto err_shutdown_timer; - return hid_hw_start(hdev, HID_CONNECT_HIDRAW); + return 0; + +err_shutdown_timer: + /* + * Drain any pending callback and permanently disable the timer + * before devm releases data: if hid_hw_start() enabled I/O on an + * always-poll-quirk device and then failed, raw_event may have + * armed the timer already. + */ + timer_shutdown_sync(&data->inrange_timer); + return ret; +} + +static void letsketch_remove(struct hid_device *hdev) +{ + struct letsketch_data *data = hid_get_drvdata(hdev); + + /* + * hid_hw_stop() synchronously kills the URBs that deliver + * raw_event(), so once it returns no path can re-arm + * inrange_timer. timer_shutdown_sync() then drains any + * in-flight callback and permanently disables further + * mod_timer() calls before devm releases data. + */ + hid_hw_stop(hdev); + timer_shutdown_sync(&data->inrange_timer); } static const struct hid_device_id letsketch_devices[] = { @@ -315,6 +344,7 @@ static struct hid_driver letsketch_driver = { .name = "letsketch", .id_table = letsketch_devices, .probe = letsketch_probe, + .remove = letsketch_remove, .raw_event = letsketch_raw_event, }; module_hid_driver(letsketch_driver); -- cgit v1.2.3 From 75fe87e19d8aff81eb2c64d15d244ab8da4de945 Mon Sep 17 00:00:00 2001 From: Manish Khadka Date: Fri, 15 May 2026 23:17:52 +0545 Subject: HID: appleir: fix UAF on pending key_up_timer in remove() appleir_remove() runs hid_hw_stop() before timer_delete_sync(). hid_hw_stop() synchronously unregisters the HID input device via hid_disconnect() -> hidinput_disconnect() -> input_unregister_device(), which drops the last reference and frees the underlying input_dev when no userspace handle holds it open. key_up_tick() reads appleir->input_dev and calls input_report_key() / input_sync() on it. The timer is armed from appleir_raw_event() with a HZ/8 (~125 ms) timeout on every keydown and key-repeat report. If a key was pressed shortly before the device is disconnected, the timer can fire after hid_hw_stop() has freed input_dev but before the teardown drains it. A simple reorder is not sufficient. Putting the timer drain first still leaves a window where a USB URB completion (raw_event) running during hid_hw_stop() can call mod_timer() and re-arm the timer, which then fires after hidinput_disconnect() has freed input_dev. The same URB-completion window also lets raw_event() reach key_up(), key_down() and battery_flat() directly, all of which dereference appleir->input_dev. Introduce a 'removing' flag on struct appleir, gated by the existing spinlock. appleir_remove() sets the flag under the lock and then shuts down the timer with timer_shutdown_sync(), which both drains any in-flight callback and permanently disables further mod_timer() calls. appleir_raw_event() and key_up_tick() bail out early if the flag is set, so no path can arm or run the timer, or dereference appleir->input_dev, after remove() has started tearing down. The keyrepeat and flatbattery branches of appleir_raw_event() previously called into the input layer without holding the spinlock; take it now so the flag check is well-defined. This incidentally closes a pre-existing read-side race on appleir->current_key in the keyrepeat branch. This bug is structurally a sibling of commit 4db2af929279 ("HID: appletb-kbd: fix UAF in inactivity-timer cleanup path") and has been present since the driver was introduced. Fixes: 9a4a5574ce42 ("HID: appleir: add support for Apple ir devices") Cc: stable@vger.kernel.org Signed-off-by: Manish Khadka Signed-off-by: Jiri Kosina --- drivers/hid/hid-appleir.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index 5e8ced7bc05a..adaa44a858ed 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -109,9 +109,10 @@ struct appleir { struct hid_device *hid; unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; struct timer_list key_up_timer; /* timer for key up */ - spinlock_t lock; /* protects .current_key */ + spinlock_t lock; /* protects .current_key, .removing */ int current_key; /* the currently pressed key */ int prev_key_idx; /* key index in a 2 packets message */ + bool removing; /* set during teardown; gates input_dev access */ }; static int get_key(int data) @@ -172,7 +173,7 @@ static void key_up_tick(struct timer_list *t) unsigned long flags; spin_lock_irqsave(&appleir->lock, flags); - if (appleir->current_key) { + if (!appleir->removing && appleir->current_key) { key_up(hid, appleir, appleir->current_key); appleir->current_key = 0; } @@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, int index; spin_lock_irqsave(&appleir->lock, flags); + if (appleir->removing) { + spin_unlock_irqrestore(&appleir->lock, flags); + goto out; + } /* * If we already have a key down, take it up before marking * this one down @@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, appleir->prev_key_idx = 0; if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { - key_down(hid, appleir, appleir->current_key); - /* - * Remote doesn't do key up, either pull them up, in the test - * above, or here set a timer which pulls them up after 1/8 s - */ - mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + spin_lock_irqsave(&appleir->lock, flags); + if (!appleir->removing) { + key_down(hid, appleir, appleir->current_key); + /* + * Remote doesn't do key up, either pull them up, in + * the test above, or here set a timer which pulls them + * up after 1/8 s + */ + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + } + spin_unlock_irqrestore(&appleir->lock, flags); goto out; } if (!memcmp(data, flatbattery, sizeof(flatbattery))) { - battery_flat(appleir); + spin_lock_irqsave(&appleir->lock, flags); + if (!appleir->removing) + battery_flat(appleir); + spin_unlock_irqrestore(&appleir->lock, flags); /* Fall through */ } @@ -318,8 +331,20 @@ fail: static void appleir_remove(struct hid_device *hid) { struct appleir *appleir = hid_get_drvdata(hid); + unsigned long flags; + + /* + * Mark the driver as tearing down so that any concurrent raw_event + * (e.g. from a USB URB completion that hid_hw_stop() has not yet + * killed) and the key_up_timer softirq stop touching input_dev + * before hid_hw_stop() frees it via hidinput_disconnect(). + */ + spin_lock_irqsave(&appleir->lock, flags); + appleir->removing = true; + spin_unlock_irqrestore(&appleir->lock, flags); + + timer_shutdown_sync(&appleir->key_up_timer); hid_hw_stop(hid); - timer_delete_sync(&appleir->key_up_timer); } static const struct hid_device_id appleir_devices[] = { -- cgit v1.2.3 From 0021eb09041f021c079be1022934a280f7f176c0 Mon Sep 17 00:00:00 2001 From: Georgiy Osokin Date: Sun, 17 May 2026 15:06:39 +0300 Subject: HID: picolcd: prevent NULL pointer dereference in picolcd_send_and_wait() In picolcd_send_and_wait(), an integer overflow of the signed loop counter 'k' can theoretically lead to a NULL pointer dereference of 'raw_data'. If the loop executes more than INT_MAX times, 'k' becomes negative, making the condition 'k < size' true even when 'size' is 0. Change the type of 'k' to 'unsigned int' to prevent the overflow and eliminate the out-of-bounds access. Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool. [jkosina@suse.com: extended hash length] Fixes: fabdbf2fd22fa17 ("HID: picoLCD: split driver code") Signed-off-by: Georgiy Osokin Signed-off-by: Jiri Kosina --- drivers/hid/hid-picolcd_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 2cc01e1bc1a8..d73e97c8b853 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -72,7 +72,8 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, struct picolcd_pending *work; struct hid_report *report = picolcd_out_report(report_id, hdev); unsigned long flags; - int i, j, k; + int i, j; + unsigned int k; if (!report || !data) return NULL; -- cgit v1.2.3 From af1a9b65ebe8a948eda805c14b78d4d0767cb1b5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 16 Jun 2026 11:26:56 +0000 Subject: HID: core: Fix OOB read in hid_get_report for numbered reports When a caller passes a size of 0 to hid_report_raw_event() for a numbered report, the function originally called hid_get_report() before performing any size validation. Inside hid_get_report(), if the report is numbered (report_enum->numbered is true), it unconditionally dereferences data[0] to extract the report ID. With a size of 0, this results in an out-of-bounds read or kernel panic. Fix this by moving the numbered report size validation check before the call to hid_get_report(), ensuring that size is at least 1 before dereferencing the data pointer. Fixes: 2c85c61d1332 ("HID: pass the buffer size to hid_report_raw_event") Signed-off-by: Lee Jones Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 41a79e43c82b..cf123347a2af 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2045,6 +2045,13 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 * u8 *cdata = data; int ret = 0; + if (report_enum->numbered && (size < 1 || bufsize < 1)) { + hid_warn_ratelimited(hid, + "Event data for numbered report is too short (%d vs %zu)\n", + size, bufsize); + return -EINVAL; + } + report = hid_get_report(report_enum, data); if (!report) return 0; -- cgit v1.2.3 From 590cc4d782487632a52f37c2171bee1eeea29627 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Thu, 18 Jun 2026 15:37:37 +0900 Subject: HID: logitech-dj: Fix maxfield check in DJ short report validation Commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") added validation for the DJ short output report, but the error path dereferences rep->field[0] even when rep->maxfield is zero. Commit 8b9a097eb2fc ("HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report") made the check conditional on rep being present, but a crafted descriptor can still create report ID 0x20 with only padding output items. hid-core registers the report, ignores the padding field, and leaves rep->maxfield as zero. In that case the validation enters the rep->maxfield < 1 branch and then dereferences rep->field[0]->report_count while printing the error message, causing a NULL pointer dereference during probe. This is reproducible with uhid by emulating a Logitech receiver with a padding-only DJ short output report: BUG: KASAN: null-ptr-deref in logi_dj_probe+0xb1/0x754 [hid_logitech_dj] Read of size 4 at addr 0000000000000028 by task kworker/4:1/129 ... Call Trace: logi_dj_probe+0xb1/0x754 [hid_logitech_dj] hid_device_probe+0x329/0x3f0 [hid] really_probe+0x162/0x570 __device_attach+0x137/0x2c0 bus_probe_device+0x38/0xc0 device_add+0xa56/0xce0 hid_add_device+0x19c/0x280 [hid] uhid_device_add_worker+0x2c/0xb0 [uhid] Reject the zero-field report before printing the field report_count. Fixes: b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 381e4dc5aba7..9c574ab8b60b 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1907,8 +1907,13 @@ static int logi_dj_probe(struct hid_device *hdev, output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - if (rep && (rep->maxfield < 1 || - rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1)) { + if (rep && rep->maxfield < 1) { + hid_err(hdev, "Expected size of DJ short report is %d, but got 0", + DJREPORT_SHORT_LENGTH - 1); + return -EINVAL; + } + + if (rep && rep->field[0]->report_count != DJREPORT_SHORT_LENGTH - 1) { hid_err(hdev, "Expected size of DJ short report is %d, but got %d", DJREPORT_SHORT_LENGTH - 1, rep->field[0]->report_count); return -EINVAL; -- cgit v1.2.3 From 7705b4140d188ce22656f6e541ae7ef834c7e11a Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 18 Jun 2026 15:06:35 +0800 Subject: HID: lg-g15: cancel pending work on remove to fix a use-after-free lg_g15_data is allocated with devm and holds a work item. The report handlers schedule that work straight from device input. lg_g15_event() and lg_g15_v2_event() do it on the backlight cycle key, and lg_g510_leds_event() does it too. The worker dereferences the lg_g15_data back through container_of. The driver had no remove callback and never cancelled the work. So if a report scheduled the work and the keyboard was then unplugged, devres freed lg_g15_data while the work was still pending or running, and the worker touched freed memory. This is a use-after-free. It is reachable as a race on device unplug. Add a remove callback that cancels the work before devres frees the state. g15->work is only initialized for the models that schedule it (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so guard the cancel on g15->work.func to avoid cancelling a work that was never set up. The g15 NULL test mirrors the one already in lg_g15_raw_event(). Fixes: 97b741aba918 ("HID: lg-g15: Add keyboard and LCD backlight control") Cc: stable@vger.kernel.org Suggested-by: Hans de Goede Signed-off-by: Maoyi Xie Reviewed-by: Hans de Goede Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg-g15.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index 1a88bc44ada4..02ef3e2094b4 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -1374,11 +1374,27 @@ static const struct hid_device_id lg_g15_devices[] = { }; MODULE_DEVICE_TABLE(hid, lg_g15_devices); +static void lg_g15_remove(struct hid_device *hdev) +{ + struct lg_g15_data *g15 = hid_get_drvdata(hdev); + + /* + * g15->work is only initialized for the models that schedule it + * (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so only + * cancel it when it was set up. + */ + if (g15 && g15->work.func) + cancel_work_sync(&g15->work); + + hid_hw_stop(hdev); +} + static struct hid_driver lg_g15_driver = { .name = "lg-g15", .id_table = lg_g15_devices, .raw_event = lg_g15_raw_event, .probe = lg_g15_probe, + .remove = lg_g15_remove, }; module_hid_driver(lg_g15_driver); -- cgit v1.2.3 From 83a3dfc018943b05b6daf3a6f891833e1aabfa1f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Jun 2026 17:08:15 +0200 Subject: 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; } -- cgit v1.2.3 From 53716a4d745f1dac7aff33f3d1494b701eb2f888 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 2 Jun 2026 21:54:06 +0000 Subject: 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); -- cgit v1.2.3 From 18a4e5cf633fad5c40ac9d936c51bf38db68796d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:01 +0800 Subject: 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); } -- cgit v1.2.3 From f9a82544c7174851f5c7524622f5966dcafd3a47 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 10 Jun 2026 15:52:02 +0800 Subject: 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); } } -- cgit v1.2.3 From 534eb6940a89ff7ca3f2ab6582f3548ca97674c3 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 10 Jun 2026 11:37:16 +0100 Subject: 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. -- cgit v1.2.3 From 608045a91d9176d66b2114d0006bc8b57dff2ca9 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Mon, 15 Jun 2026 16:32:25 +0200 Subject: rust: allow `suspicious_runtime_symbol_definitions` lint for Rust >= 1.98 Starting with Rust 1.98.0 (expected 2026-08-20), Rust is introducing a couple new lints, `invalid_runtime_symbol_definitions` (deny-by-default) and `suspicious_runtime_symbol_definitions` (warn-by-default), which check the signature of items whose symbol name is a runtime symbol expected by `core`. Our build hits the second one, i.e. the warning: error: suspicious definition of the runtime `strlen` symbol used by the standard library --> rust/bindings/bindings_generated.rs:20018:5 | 20018 | pub fn strlen(s: *const ffi::c_char) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected `unsafe extern "C" fn(*const i8) -> usize` found `unsafe extern "C" fn(*const u8) -> usize` = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "strlen")]`, or `#[link_name = "strlen"]` = help: allow this lint if the signature is compatible = note: `-D suspicious-runtime-symbol-definitions` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(suspicious_runtime_symbol_definitions)]` error: suspicious definition of the runtime `strlen` symbol used by the standard library --> rust/uapi/uapi_generated.rs:14236:5 | 14236 | pub fn strlen(s: *const ffi::c_char) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected `unsafe extern "C" fn(*const i8) -> usize` found `unsafe extern "C" fn(*const u8) -> usize` = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "strlen")]`, or `#[link_name = "strlen"]` = help: allow this lint if the signature is compatible = note: `-D suspicious-runtime-symbol-definitions` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(suspicious_runtime_symbol_definitions)]` Thus `allow` the lint in `bindings` and `uapi`. A more targeted alternative to avoid `allow`ing it would be to pass `--blocklist-function strlen` to `bindgen`, but we would perhaps need to adjust if other C headers end up adding more (or Rust checking more). Since it is just the less critical one that we hit, and since eventually this should be properly fixed by getting upstream Rust to provide a flag like GCC/Clang's `-funsigned-char` [2][3], just `allow` it for now. Cc: Urgau Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Link: https://github.com/rust-lang/rust/pull/155521 [1] Link: https://github.com/rust-lang/rust/issues/138446 [2] Link: https://github.com/Rust-for-Linux/linux/issues/355 [3] Reviewed-by: Gary Guo Reviewed-by: Alice Ryhl Reviewed-by: Tamir Duberstein Link: https://patch.msgid.link/20260615143225.471756-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- init/Kconfig | 3 +++ rust/bindings/lib.rs | 4 ++++ rust/uapi/lib.rs | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/init/Kconfig b/init/Kconfig index 5230d4879b1c..10f2013b5321 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -190,6 +190,9 @@ config RUSTC_HAS_FILE_WITH_NUL config RUSTC_HAS_FILE_AS_C_STR def_bool RUSTC_VERSION >= 109100 +config RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS + def_bool RUSTC_VERSION >= 109800 + config PAHOLE_VERSION int default "$(PAHOLE_VERSION)" diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs index 854e7c471434..812f8e5a08d5 100644 --- a/rust/bindings/lib.rs +++ b/rust/bindings/lib.rs @@ -27,6 +27,10 @@ #[allow(clippy::ref_as_ptr)] #[allow(clippy::undocumented_unsafe_blocks)] #[cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))] +#[cfg_attr( + CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + allow(suspicious_runtime_symbol_definitions) +)] mod bindings_raw { use pin_init::{MaybeZeroable, Zeroable}; diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs index b8a515de31ca..797ead5b5626 100644 --- a/rust/uapi/lib.rs +++ b/rust/uapi/lib.rs @@ -24,6 +24,10 @@ unsafe_op_in_unsafe_fn )] #![cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))] +#![cfg_attr( + CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + allow(suspicious_runtime_symbol_definitions) +)] #![feature(cfi_encoding)] // Manual definition of blocklisted types. -- cgit v1.2.3 From ff4c5a0de1f2ef7737a8688a86e19301e567020d Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Fri, 26 Jun 2026 02:28:45 +0100 Subject: 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); } -- cgit v1.2.3 From d2e52d610b9b09694261632340b801a421e0b0c5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 6 Jun 2026 16:01:55 -0400 Subject: 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); -- cgit v1.2.3 From cceca8cb776b03e83985578f84bd730c30d57f70 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 13 Jun 2026 23:31:52 -0700 Subject: 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); -- cgit v1.2.3 From 75c0c09541b49daa08fddbc2c18c2232f4eab7d8 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Sun, 14 Jun 2026 01:10:52 -0700 Subject: 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); } } } -- cgit v1.2.3 From 82d9a2b45b170f0c52ac61e0e3e23f212cd065f0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:32:32 +0800 Subject: 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; -- cgit v1.2.3 From 1a8b2951ba3c35824ff4df0ea7d9f7a554c964a2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 14:33:32 +0800 Subject: 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; } -- cgit v1.2.3 From 4b45d7836b9526b8776af5f29219615be9417230 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Tue, 16 Jun 2026 03:14:39 -0700 Subject: 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++) -- cgit v1.2.3 From 5d7a08125e69630dc1168d3e9ca5626ded3da807 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 02:37:49 +0800 Subject: 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; -- cgit v1.2.3 From 97f9e509d839f7e5b9a32105ae322e5223c86786 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Jun 2026 22:58:27 +0200 Subject: 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 -- cgit v1.2.3 From ae9d8058f13238cad5f5d16f676da91187684581 Mon Sep 17 00:00:00 2001 From: Nikolay Ivchenko Date: Sat, 20 Jun 2026 20:06:03 +0300 Subject: 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; } } -- cgit v1.2.3 From a74e31d0a2d52c65d9a6647c87b54bf78c0d9317 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sun, 21 Jun 2026 04:26:25 +0530 Subject: 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; -- cgit v1.2.3 From 66fb31358108d10245b9e4ef0eef3e7d9747055e Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 09:53:17 +0800 Subject: 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); -- cgit v1.2.3 From 16f7ec8d5dc100eafd2c8e06cd30340a30b104a1 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 21:57:29 +0800 Subject: 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; -- cgit v1.2.3 From b33ab1dd80f5c1742f49eb6ec7b337c5ffcf3d32 Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Fri, 12 Jun 2026 13:39:16 +0200 Subject: 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) */ -- cgit v1.2.3 From cb36d81e751173c4b59b5e561c596c925144ea48 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 25 Jun 2026 13:23:56 -1000 Subject: 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); -- cgit v1.2.3 From 6697091b386a4e2830bdd38512c87a4befff2b32 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 2 Jun 2026 16:45:39 +0000 Subject: 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 -- cgit v1.2.3 From adf4bc07f814da8329278d32600147f5a150938c Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 3 Jun 2026 20:16:40 +0800 Subject: 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) -- cgit v1.2.3 From e74c0d0eef7e1fa9fd387b81b2787b4581e0b11c Mon Sep 17 00:00:00 2001 From: Ariana Lazar Date: Thu, 4 Jun 2026 16:46:47 +0300 Subject: 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); -- cgit v1.2.3 From ce0e1cae26096fe959a0da5563a6d6d5a801d5fb Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 13 Jun 2026 02:18:39 -0500 Subject: 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; -- cgit v1.2.3 From 44a5fd874bb6873bdaec59f722c1d57832fbc9df Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:46 +0530 Subject: 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); -- cgit v1.2.3 From fbe67ff37a6fd855a6c097f84f3738bd13d0a898 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:48 +0530 Subject: 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; -- cgit v1.2.3 From 38b72267b7e22768a1f26d9935de4e1752a1dc85 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 14 Jun 2026 12:45:49 +0530 Subject: 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; -- cgit v1.2.3 From e561b35633f450ee607e87a6401d97f156a0cd54 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 12 Jun 2026 19:58:10 -0500 Subject: 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; -- cgit v1.2.3 From 3ee2128b6f0eb0be7b6cb8f6e0f1f113a65201a0 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Fri, 12 Jun 2026 19:58:11 -0500 Subject: 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; -- cgit v1.2.3 From f784fcea450617055d2d12eec5b2f6e0e38bf878 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 10 Jun 2026 16:29:09 +0800 Subject: 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 @@ done_proc: } 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. -- cgit v1.2.3 From 3ce8d099e0afc5a7da75a2007a67f67c4f5a4af1 Mon Sep 17 00:00:00 2001 From: Zhang Lixu Date: Wed, 10 Jun 2026 16:29:10 +0800 Subject: 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 -- cgit v1.2.3 From 6e1b9bff1202da55c464e36bd34a2b6863d7fe30 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 4 Jun 2026 09:42:46 +0800 Subject: 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) -- cgit v1.2.3 From cd5a6a5096b246e10600da3ac47a1274ce9573c8 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 4 Jun 2026 09:42:47 +0800 Subject: 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; -- cgit v1.2.3 From 55052184ac9011db2ea983e54d6c21f0b1079a12 Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Tue, 16 Jun 2026 15:02:04 +0200 Subject: 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 --- drivers/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; } -- cgit v1.2.3 From cf1afec09e9f004a62c54c471863209ed249fca7 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 8 Jun 2026 11:33:53 +0100 Subject: 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; } -- cgit v1.2.3 From 9d78a98796f215d9973e1e53871b2d63420f3608 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 8 Jun 2026 11:48:58 +0100 Subject: 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 @@ update_flags: 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); -- cgit v1.2.3 From a2d8d5647ed854e38f941741aea45b9eb15a6350 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 10 Jun 2026 10:29:26 -0700 Subject: 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; -- cgit v1.2.3 From ce6050bafb4e33377dc17fcc357736bfc351180c Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Thu, 11 Jun 2026 15:16:21 +0100 Subject: 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 @@ again: * corruption, e.g. bad reloc tree key offset. */ ret = -EINVAL; + btrfs_put_root(root); goto out; } ret = merge_reloc_root(rc, root); -- cgit v1.2.3 From 3dcd50730814e5220072d2b26d0587af6bfb6dbe Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Thu, 11 Jun 2026 14:40:17 -0700 Subject: 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; } -- cgit v1.2.3 From 1cb15b153c636096740519f62040ec6dc3e50aef Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 8 Jun 2026 08:23:49 +0930 Subject: 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; -- cgit v1.2.3 From 3dc22abc21f5892406c09202fa2627196cc96967 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 11 Jun 2026 07:56:12 +0930 Subject: 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", -- cgit v1.2.3 From 9e37d187e1e62bd3d450429203f98402cefb3ef2 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Mon, 22 Jun 2026 10:22:24 +0200 Subject: 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); } /* -- cgit v1.2.3 From c0041b502e579a5c52e5cae918b90678f03faddd Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Sun, 21 Jun 2026 18:41:48 +0930 Subject: 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); -- cgit v1.2.3 From 51ed53630915c9cc290036fe4f430849e23bbf8e Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 18 May 2026 02:58:36 +0000 Subject: clk: spacemit: k3: fix USB2 bus clock 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: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Reported-by: Junzhong Pan Link: https://patch.msgid.link/20260518-06-clk-reset-usb-fix-v1-1-14fc235e692b@kernel.org Signed-off-by: Yixun Lan --- drivers/clk/spacemit/ccu-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index cb0c4277f72a..03de04144963 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -777,7 +777,7 @@ static const struct clk_parent_data sdh2_parents[] = { CCU_MUX_DIV_GATE_FC_DEFINE(sdh2_clk, sdh2_parents, APMU_SDH2_CLK_RES_CTRL, 8, 3, BIT(11), 5, 3, BIT(4), 0); -CCU_GATE_DEFINE(usb2_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(0), 0); +CCU_GATE_DEFINE(usb2_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(1), 0); CCU_GATE_DEFINE(usb3_porta_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(4), 0); CCU_GATE_DEFINE(usb3_portb_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(8), 0); CCU_GATE_DEFINE(usb3_portc_bus_clk, CCU_PARENT_HW(axi_clk), APMU_USB_CLK_RES_CTRL, BIT(12), 0); -- cgit v1.2.3 From ee89db004238bd0b034f2a6176e175561658750b Mon Sep 17 00:00:00 2001 From: Florian Fuchs Date: Mon, 29 Jun 2026 18:33:42 -0700 Subject: 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); -- cgit v1.2.3 From 1006b2f57f77325bfbf5bd36685efe60334fa360 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 30 Jun 2026 11:18:13 +0800 Subject: 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; -- cgit v1.2.3 From 6bfc8d01ac4068eced509f8fc74d0cd205e4dcec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 29 Jun 2026 14:45:26 +0200 Subject: 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) -- cgit v1.2.3 From 8ae5f8e4836667fcaffdf2e3c6068b0a8b364dd8 Mon Sep 17 00:00:00 2001 From: Unnathi Chalicheemala Date: Wed, 17 Jun 2026 16:35:00 -0700 Subject: 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; } -- cgit v1.2.3 From 05e5ffde9b666ee6d3ec225efeda9c038fa74ac8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 3 Jun 2026 17:17:51 +0200 Subject: 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 -- cgit v1.2.3 From 9d12eb98582fec2578d17e025b13740dcfb57d8e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 17 Mar 2026 08:32:52 +0000 Subject: dmaengine: switchtec-dma: fix FIELD_GET misuse when programming SE threshold FIELD_GET(SE_THRESH_MASK, thresh) extracts bits [31:23] from thresh and right-shifts them, which is the inverse of the intended operation. Since thresh is derived from se_buf_len / 2 (at most 255), bits [31:23] are always zero, so the SE threshold is never actually programmed into the register. Use FIELD_PREP() instead to correctly left-shift thresh into bits [31:23] of the valid_en_se register, consistent with the FIELD_PREP usage for the perf tuner config just above. Fixes: 30eba9df76ad ("dmaengine: switchtec-dma: Implement hardware initialization and cleanup") Signed-off-by: David Carlier Review-by: Logan Gunthorpe Reviewed-by: Frank Li Link: https://patch.msgid.link/20260317083252.13224-1-devnexen@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/switchtec_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c index 3ef928640615..71d9868ce613 100644 --- a/drivers/dma/switchtec_dma.c +++ b/drivers/dma/switchtec_dma.c @@ -1099,7 +1099,7 @@ static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev, dev_dbg(&pdev->dev, "Channel %d: SE buffer count %d\n", i, se_buf_len); thresh = se_buf_len / 2; - valid_en_se |= FIELD_GET(SE_THRESH_MASK, thresh); + valid_en_se |= FIELD_PREP(SE_THRESH_MASK, thresh); writel(valid_en_se, &swdma_chan->mmio_chan_fw->valid_en_se); /* request irqs */ -- cgit v1.2.3 From 226f4a490d1a938fc838d8f8c46a4eca864c0d78 Mon Sep 17 00:00:00 2001 From: Qianyu Luo Date: Thu, 25 Jun 2026 13:55:08 +0800 Subject: 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 { -- cgit v1.2.3 From b7d9c359e5cf867f7eb23df3bb1c6b9e58af24da Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 29 Jun 2026 12:55:48 -1000 Subject: 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); -- cgit v1.2.3 From 88496c4ac5a6ade75619f4b1015706a8b924d50a Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 12:27:58 +0800 Subject: 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; -- cgit v1.2.3 From eb94f5a41a193a425e09a63cb75dffd151d8f42e Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Tue, 30 Jun 2026 11:08:56 +0800 Subject: 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 (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)) { + 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); -- cgit v1.2.3 From 859e02a369ab328a77dfcabf59562100e55f9c5c Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Tue, 16 Jun 2026 17:24:53 +0530 Subject: pinctrl: qcom: Unconditionally mark gpio as wakeup enable GPIO interrupts that are wakeup capable need to be forwarded to wakeup capable parent irqchip. This is done via writing to it's wakeup_enable bit. Currently the bit is set only for PDC irqchip by checking skip_wake_irqs. skip_wake_irqs is set to differentiate between parent irqchips MPM and PDC. It is set when the parent irqchip is PDC to inform pinctrl about skipping the IRQ setting up at TLMM. However, the functionality to forward GPIO interrupts during SoC low power mode is needed regardless of which parent irqchip it is. Without the functionality it is impossible for MPM irqchip to detect the GPIO interrupt during SoC low power mode since for MPM irqchip the skip_wake_irqs is always false. Remove skip_wake_irqs condition when setting wakeup enable bit to allow forwarding GPIO interrupts for SoCs using MPM irqchip too. Fixes: 76b446f5b86e ("pinctrl: qcom: handle intr_target_reg wakeup_present/enable bits") Signed-off-by: Sneh Mankad Reviewed-by: Maulik Shah Reviewed-by: Linus Walleij Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260616-enable_wakeup_capable_gpios-v3-1-fb59647d89cb@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/pinctrl/qcom/pinctrl-msm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 11db6564c44d..fd0add4e71da 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1240,12 +1240,12 @@ static int msm_gpio_irq_reqres(struct irq_data *d) /* * If the wakeup_enable bit is present and marked as available for the * requested GPIO, it should be enabled when the GPIO is marked as - * wake irq in order to allow the interrupt event to be transfered to - * the PDC HW. + * wake irq in order to allow the interrupt event to be transferred to + * the PDC/MPM HW. * While the name implies only the wakeup event, it's also required for * the interrupt event. */ - if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { + if (g->intr_wakeup_present_bit) { u32 intr_cfg; raw_spin_lock_irqsave(&pctrl->lock, flags); @@ -1273,7 +1273,7 @@ static void msm_gpio_irq_relres(struct irq_data *d) unsigned long flags; /* Disable the wakeup_enable bit if it has been set in msm_gpio_irq_reqres() */ - if (test_bit(d->hwirq, pctrl->skip_wake_irqs) && g->intr_wakeup_present_bit) { + if (g->intr_wakeup_present_bit) { u32 intr_cfg; raw_spin_lock_irqsave(&pctrl->lock, flags); -- cgit v1.2.3 From c9a71daaecb2fb1d8c704545cc0b1c920b9bf5d7 Mon Sep 17 00:00:00 2001 From: Chi Wang Date: Fri, 19 Jun 2026 15:42:44 +0800 Subject: 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 @@ main_queue: * 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; -- cgit v1.2.3 From a2d30022b7c316ad845d1b696e724058b88e5a4e Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:08 +0200 Subject: 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 -- cgit v1.2.3 From 84486e3bbda18a2df1ed74ca78e1e14bde9a941b Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:09 +0200 Subject: 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 -- cgit v1.2.3 From 9efcc9ba9b2e940cc01e63d132ae741e4c5d09c7 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Thu, 25 Jun 2026 21:38:10 +0200 Subject: 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 -- cgit v1.2.3 From 437a8d2aa1aa442c4a176fdf4700a9b3bb0c8794 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 26 Jun 2026 15:08:05 +0200 Subject: pinctrl: qcom: sc8280xp: Add missing wakeup entries for GPIO143/151 Pins 143 and 151 were not included in the PDC wakeup map. They are normally used for PCIe2A and PCIe3a PERST# respectively, so they're unlikely to be excercised in practice, but still add them for the sake of completeness. Fixes: c0e4c71a9e7c ("pinctrl: qcom: Introduce sc8280xp TLMM driver") Signed-off-by: Konrad Dybcio Link: https://patch.msgid.link/20260626-topic-8280_pinctrl_wakeup-v1-1-2ccb267148f5@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/pinctrl/qcom/pinctrl-sc8280xp.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c index 4056b9fa32f8..e018bd11626c 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc8280xp.c +++ b/drivers/pinctrl/qcom/pinctrl-sc8280xp.c @@ -1881,16 +1881,17 @@ static const struct msm_gpio_wakeirq_map sc8280xp_pdc_map[] = { { 126, 200 }, { 127, 225 }, { 128, 262 }, { 129, 201 }, { 130, 209 }, { 131, 173 }, { 132, 202 }, { 136, 210 }, { 138, 171 }, { 139, 226 }, { 140, 227 }, { 142, 228 }, - { 144, 229 }, { 145, 230 }, { 146, 231 }, { 148, 232 }, - { 149, 233 }, { 150, 234 }, { 152, 235 }, { 154, 212 }, - { 157, 213 }, { 161, 219 }, { 170, 236 }, { 171, 221 }, - { 174, 222 }, { 175, 237 }, { 176, 223 }, { 177, 170 }, - { 180, 238 }, { 181, 239 }, { 182, 240 }, { 183, 241 }, - { 184, 242 }, { 185, 243 }, { 190, 178 }, { 193, 184 }, - { 196, 185 }, { 198, 186 }, { 200, 174 }, { 201, 175 }, - { 205, 176 }, { 206, 177 }, { 208, 187 }, { 210, 198 }, - { 211, 199 }, { 212, 204 }, { 215, 205 }, { 220, 188 }, - { 221, 194 }, { 223, 195 }, { 225, 196 }, { 227, 197 }, + { 143, 261 }, { 144, 229 }, { 145, 230 }, { 146, 231 }, + { 148, 232 }, { 149, 233 }, { 150, 234 }, { 151, 264 }, + { 152, 235 }, { 154, 212 }, { 157, 213 }, { 161, 219 }, + { 170, 236 }, { 171, 221 }, { 174, 222 }, { 175, 237 }, + { 176, 223 }, { 177, 170 }, { 180, 238 }, { 181, 239 }, + { 182, 240 }, { 183, 241 }, { 184, 242 }, { 185, 243 }, + { 190, 178 }, { 193, 184 }, { 196, 185 }, { 198, 186 }, + { 200, 174 }, { 201, 175 }, { 205, 176 }, { 206, 177 }, + { 208, 187 }, { 210, 198 }, { 211, 199 }, { 212, 204 }, + { 215, 205 }, { 220, 188 }, { 221, 194 }, { 223, 195 }, + { 225, 196 }, { 227, 197 }, }; static struct msm_pinctrl_soc_data sc8280xp_pinctrl = { -- cgit v1.2.3 From 2d044049421dd48212b28646a850749d4a2d57fa Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:13 +0000 Subject: HID: bpf: Fix hid_bpf_get_data() range check hid_bpf_get_data() returns a pointer into the HID-BPF context data when the caller-provided offset and size fit inside ctx->allocated_size. The current check adds rdwr_buf_size and offset before comparing the result against ctx->allocated_size. Since both values are unsigned, a very large size can wrap the sum below ctx->allocated_size and make the helper return a pointer even though the requested range is not contained in the backing buffer. Use check_add_overflow() to reject wrapped range ends before comparing the requested range end against ctx->allocated_size. Fixes: 658ee5a64fcf ("HID: bpf: allocate data memory for device_event BPF programs") Signed-off-by: Yiyang Chen Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/hid_bpf_dispatch.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index d0130658091b..536f6d01fd14 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "hid_bpf_dispatch.h" const struct hid_ops *hid_ops; @@ -296,10 +297,12 @@ __bpf_kfunc __u8 * hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size) { struct hid_bpf_ctx_kern *ctx_kern; + size_t end; ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); - if (rdwr_buf_size + offset > ctx->allocated_size) + if (check_add_overflow(rdwr_buf_size, offset, &end) || + end > ctx->allocated_size) return NULL; return ctx_kern->data + offset; -- cgit v1.2.3 From 5aad55011a37d999cf99d14bafb6a093a1a70466 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:14 +0000 Subject: selftests/hid: Load only requested struct_ops maps The HID selftest skeleton contains several struct_ops maps, but each test usually wants to load only the programs named by that test. load_programs() disabled auto-attach for all maps, but left struct_ops autocreate enabled. libbpf can enable autoload for programs referenced by autocreated struct_ops maps, so an unrelated program can be loaded and fail even when the current test does not use it. Disable autocreate for all struct_ops maps by default, then re-enable it only for the maps selected by the test before loading the skeleton. Signed-off-by: Yiyang Chen Fixes: f64c1a459339 ("selftests/hid: disable struct_ops auto-attach") Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/hid_bpf.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 1e979fb3542b..269256e1decd 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -86,6 +86,20 @@ static void load_programs(const struct test_program programs[], self->skel = hid__open(); ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open"); + /* + * Disable all struct_ops maps by default so libbpf does not autoload + * programs referenced by maps that are unrelated to the current test. + */ + bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) { + if (bpf_map__type(iter_map) == BPF_MAP_TYPE_STRUCT_OPS) { + err = bpf_map__set_autocreate(iter_map, false); + ASSERT_OK(err) TH_LOG("can not disable struct_ops map '%s'", + bpf_map__name(iter_map)); + } + + bpf_map__set_autoattach(iter_map, false); + } + for (int i = 0; i < progs_count; i++) { struct bpf_program *prog; struct bpf_map *map; @@ -102,6 +116,10 @@ static void load_programs(const struct test_program programs[], ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'", programs[i].name + 4); + err = bpf_map__set_autocreate(map, true); + ASSERT_OK(err) TH_LOG("can not enable struct_ops map '%s'", + programs[i].name + 4); + /* hid_id is the first field of struct hid_bpf_ops */ ops_hid_id = bpf_map__initial_value(map, NULL); ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data"); @@ -109,13 +127,6 @@ static void load_programs(const struct test_program programs[], *ops_hid_id = self->hid.hid_id; } - /* we disable the auto-attach feature of all maps because we - * only want the tested one to be manually attached in the next - * call to bpf_map__attach_struct_ops() - */ - bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) - bpf_map__set_autoattach(iter_map, false); - err = hid__load(self->skel); ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err); -- cgit v1.2.3 From eebbef7c468a5cb58c4772849ee5066441166cf0 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Tue, 23 Jun 2026 06:23:15 +0000 Subject: selftests/hid: Cover hid_bpf_get_data() size overflow Add a HID-BPF regression check for hid_bpf_get_data() requests whose size would overflow when added to the offset. The new rdesc fixup callback asks for offset 2 and size ~0ULL, then records whether the helper returns NULL. A vulnerable kernel returns a non-NULL pointer because the runtime check wraps the addition. A fixed kernel rejects the request. The callback records the helper result without dereferencing any returned pointer. The callback reports the helper result through BSS and returns 0 intentionally. hid_rdesc_fixup return values are consumed as report descriptor fixup results, so a positive test-result value would be interpreted as a replacement report descriptor size. Also add KHDR_INCLUDES to the HID selftest build so hid_bpf.c sees the current kernel UAPI HID definitions on systems whose installed headers do not provide enum hid_report_type. Fixes: 658ee5a64fcf ("HID: bpf: allocate data memory for device_event BPF programs") Signed-off-by: Yiyang Chen Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 2 +- tools/testing/selftests/hid/hid_bpf.c | 11 +++++++++++ tools/testing/selftests/hid/progs/hid.c | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 96071b4800e8..2f423de83147 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -24,7 +24,7 @@ CXX ?= $(CROSS_COMPILE)g++ HOSTPKG_CONFIG := pkg-config -CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) +CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) $(KHDR_INCLUDES) CFLAGS += -I$(OUTPUT)/tools/include LDLIBS += -lelf -lz -lrt -lpthread diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 269256e1decd..b851339308c2 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -898,6 +898,17 @@ TEST_F(hid_bpf, test_rdesc_fixup) ASSERT_EQ(rpt_desc.value[4], 0x42); } +TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow) +{ + const struct test_program progs[] = { + { .name = "hid_rdesc_fixup_get_data_overflow" }, + }; + + LOAD_PROGRAMS(progs); + + ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1); +} + static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 5ecc845ef792..b21fbb13c926 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -13,6 +13,7 @@ struct attach_prog_args { __u64 callback_check = 52; __u64 callback2_check = 52; +__u64 get_data_overflow_check; SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) @@ -240,6 +241,20 @@ struct hid_bpf_ops rdesc_fixup = { .hid_rdesc_fixup = (void *)hid_rdesc_fixup, }; +SEC("?struct_ops.s/hid_rdesc_fixup") +int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx) +{ + if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */)) + get_data_overflow_check = 1; + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops rdesc_fixup_get_data_overflow = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow, +}; + SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) { -- cgit v1.2.3 From ab1150115e68a46b687eb38c1ab92782018c9f2c Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 1 Jul 2026 12:57:33 +0800 Subject: dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA When terminating DMA transfers, active descriptors are not properly reclaimed. Only cyclic descriptors were handled, leaving non-cyclic descriptors and their LLI chains to be permanently leaked. Fix by using vchan_terminate_vdesc() which handles both cyclic and non-cyclic descriptors by adding them to desc_terminated queue for proper cleanup. Add pchan->desc != pchan->done check to prevent double-adding completed descriptors, which would corrupt the list. Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") Signed-off-by: Hongling Zeng Acked-by: Jernej Skrabec Suggested-by: Frank Li Reviewed-by: Frank Li Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn Signed-off-by: Vinod Koul --- drivers/dma/sun6i-dma.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index a9a254dbf8cb..f47a326dd7ff 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -945,16 +945,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) spin_lock_irqsave(&vchan->vc.lock, flags); - if (vchan->cyclic) { - vchan->cyclic = false; - if (pchan && pchan->desc) { - struct virt_dma_desc *vd = &pchan->desc->vd; - struct virt_dma_chan *vc = &vchan->vc; + if (pchan && pchan->desc && pchan->desc != pchan->done) { + struct virt_dma_desc *vd = &pchan->desc->vd; - list_add_tail(&vd->node, &vc->desc_completed); - } + vchan_terminate_vdesc(vd); } + vchan->cyclic = false; vchan_get_all_descriptors(&vchan->vc, &head); if (pchan) { -- cgit v1.2.3 From 3dd63dba8f9cb6990a40af7ed66ee0159f475819 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Wed, 1 Jul 2026 17:21:55 +0900 Subject: 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); -- cgit v1.2.3 From df645b745941ea829b39134ac342f730f4d9d978 Mon Sep 17 00:00:00 2001 From: Yi Xie Date: Tue, 30 Jun 2026 17:12:06 +0800 Subject: 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. -- cgit v1.2.3 From 15cd3ccf9b179f0f76948d0901be3b15028bc768 Mon Sep 17 00:00:00 2001 From: Melbin K Mathew Date: Wed, 1 Jul 2026 10:11:45 +0200 Subject: 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) { -- cgit v1.2.3 From f5ad2ead846e3a00d040d64c7eaf67b65629f51d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Jun 2026 22:15:59 +0200 Subject: 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 @@ out: 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; -- cgit v1.2.3 From 1ca22c6aa006b05143367268066fb74e32cfe66b Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Mon, 18 May 2026 02:58:37 +0000 Subject: 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)), -- cgit v1.2.3 From ab45ecfab540653f1ff4a8d2f2da055c82cf1640 Mon Sep 17 00:00:00 2001 From: Tanmay Kathpalia Date: Sat, 27 Jun 2026 13:14:46 -0700 Subject: 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 -- cgit v1.2.3 From 1a8c89f8c112c75e84ff9a140f969e372aed0c9a Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Wed, 17 Jun 2026 11:16:27 +0800 Subject: 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; -- cgit v1.2.3 From 71827776667f4e4677a4fa806bcfb24d4b8dd9d7 Mon Sep 17 00:00:00 2001 From: Robby Cai Date: Fri, 19 Jun 2026 15:31:15 +0800 Subject: 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: -- cgit v1.2.3 From 34db32102439d948e0c9aea060e48f979aae827d Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 18 Jun 2026 10:08:11 +0200 Subject: MAINTAINERS: Drop Karthikeyan Mitran from Mobiveil PCIe entry Karthikeyan's Mobiveil email address is now bouncing and there is no sign of him [1] willing to carry out maintainership duties for this driver. Drop him from the MAINTAINERS entry for Mobiveil PCIe. Link: https://lore.kernel.org/linux-pci/ahWB7yRchlW_KjEm@wunner.de Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Cc: Lukas Wunner Cc: Mitran Karthikeyan Link: https://patch.msgid.link/20260618080811.26337-1-manivannan.sadhasivam@oss.qualcomm.com --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..c20756e28d60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20686,7 +20686,6 @@ F: include/linux/switchtec.h F: include/uapi/linux/switchtec_ioctl.h PCI DRIVER FOR MOBIVEIL PCIE IP -M: Karthikeyan Mitran M: Hou Zhiqiang L: linux-pci@vger.kernel.org S: Supported -- cgit v1.2.3 From 8813b0612275cc61fe9e6603d0ee019247ade6be Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Thu, 2 Jul 2026 00:13:19 +0700 Subject: HID: multitouch: fix out-of-bounds bit access on mt_io_flags mt_io_flags is a single unsigned long, but mt_process_slot(), mt_release_pending_palms() and mt_release_contacts() use it as a per-slot bitmap indexed by the slot number. That slot number is only bounded by td->maxcontacts, which is taken from the device's ContactCountMaximum feature report and can be up to 255, not by BITS_PER_LONG. As a result, a multitouch device that advertises a large contact count makes set_bit()/clear_bit() operate past the mt_io_flags word and corrupt the adjacent members of struct mt_device. The sticky-fingers release timer is the easiest way to reach this. mt_release_contacts() runs for (i = 0; i < mt->num_slots; i++) clear_bit(i, &td->mt_io_flags); with num_slots == maxcontacts. For maxcontacts around 250 the loop clears the bits that overlap td->applications.next, zeroing that list head, and the list_for_each_entry() that immediately follows then dereferences NULL. The kernel panics from timer (softirq) context. On a KASAN build this shows up as a general protection fault in mt_release_contacts() with a null-ptr-deref at offset 0x58, which is offsetof(struct mt_application, num_received). The state is reachable from an untrusted USB or Bluetooth HID multitouch device; no local privileges are required. Store the per-slot active state in a separately allocated bitmap sized for maxcontacts, the same pattern already used for pending_palm_slots, and keep only MT_IO_FLAGS_RUNNING in mt_io_flags. The two "mt_io_flags & MT_IO_SLOTS_MASK" arming checks become bitmap_empty(td->active_slots, td->maxcontacts). Move MT_IO_FLAGS_RUNNING back to bit 0. It was bumped to bit 32 by the same commit to leave the low byte for the slot bits; with the slot bits gone it fits in bit 0 again, which also keeps it within the unsigned long on 32-bit. Fixes: 46f781e0d151 ("HID: multitouch: fix sticky fingers") Cc: stable@vger.kernel.org Signed-off-by: Trung Nguyen Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-multitouch.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 0495152091e3..edb37b4c867e 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -31,6 +31,7 @@ * [1] https://gitlab.freedesktop.org/libevdev/hid-tools */ +#include #include #include #include @@ -97,8 +98,7 @@ enum report_mode { TOUCHPAD_REPORT_ALL = TOUCHPAD_REPORT_BUTTONS | TOUCHPAD_REPORT_CONTACTS, }; -#define MT_IO_SLOTS_MASK GENMASK(7, 0) /* reserve first 8 bits for slot tracking */ -#define MT_IO_FLAGS_RUNNING 32 +#define MT_IO_FLAGS_RUNNING 0 static const bool mtrue = true; /* default for true */ static const bool mfalse; /* default for false */ @@ -174,10 +174,9 @@ struct mt_device { struct timer_list release_timer; /* to release sticky fingers */ struct hid_haptic_device *haptic; /* haptic related configuration */ struct hid_device *hdev; /* hid_device we're attached to */ - unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) - * first 8 bits are reserved for keeping the slot - * states, this is fine because we only support up - * to 250 slots (MT_MAX_MAXCONTACT) + unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) */ + unsigned long *active_slots; /* bitmap of slots with an active + * contact, sized for maxcontacts */ __u8 inputmode_value; /* InputMode HID feature value */ __u8 maxcontacts; @@ -1036,7 +1035,7 @@ static void mt_release_pending_palms(struct mt_device *td, for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) { clear_bit(slotnum, app->pending_palm_slots); - clear_bit(slotnum, &td->mt_io_flags); + clear_bit(slotnum, td->active_slots); input_mt_slot(input, slotnum); input_mt_report_slot_inactive(input); @@ -1247,9 +1246,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); - set_bit(slotnum, &td->mt_io_flags); + set_bit(slotnum, td->active_slots); } else { - clear_bit(slotnum, &td->mt_io_flags); + clear_bit(slotnum, td->active_slots); } return 0; @@ -1384,7 +1383,7 @@ static void mt_touch_report(struct hid_device *hid, * defect. */ if (app->quirks & MT_QUIRK_STICKY_FINGERS) { - if (td->mt_io_flags & MT_IO_SLOTS_MASK) + if (!bitmap_empty(td->active_slots, td->maxcontacts)) mod_timer(&td->release_timer, jiffies + msecs_to_jiffies(100)); else @@ -1443,6 +1442,15 @@ static int mt_touch_input_configured(struct hid_device *hdev, if (td->is_pressurepad) __set_bit(INPUT_PROP_PRESSUREPAD, input->propbit); + if (!td->active_slots) { + td->active_slots = devm_kcalloc(&td->hdev->dev, + BITS_TO_LONGS(td->maxcontacts), + sizeof(long), + GFP_KERNEL); + if (!td->active_slots) + return -ENOMEM; + } + app->pending_palm_slots = devm_kcalloc(&hi->input->dev, BITS_TO_LONGS(td->maxcontacts), sizeof(long), @@ -2062,7 +2070,7 @@ static void mt_release_contacts(struct hid_device *hid) for (i = 0; i < mt->num_slots; i++) { input_mt_slot(input_dev, i); input_mt_report_slot_inactive(input_dev); - clear_bit(i, &td->mt_io_flags); + clear_bit(i, td->active_slots); } input_mt_sync_frame(input_dev); input_sync(input_dev); @@ -2085,7 +2093,7 @@ static void mt_expired_timeout(struct timer_list *t) */ if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) return; - if (td->mt_io_flags & MT_IO_SLOTS_MASK) + if (!bitmap_empty(td->active_slots, td->maxcontacts)) mt_release_contacts(hdev); clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } -- cgit v1.2.3 From b6eb022890c78285f55381589c1536bd66b8eaeb Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Thu, 2 Jul 2026 00:13:20 +0700 Subject: selftests/hid: multitouch: test a large ContactCountMaximum Add a regression test for the out-of-bounds bit operations on struct mt_device.mt_io_flags. A HID multitouch device can advertise a ContactCountMaximum far larger than the number of contacts a single report describes, up to 255. The driver used to keep the per-slot active state in the bits of a single unsigned long and index set_bit()/clear_bit() by the slot number, so such a device drove those operations out of bounds. The sticky-fingers release timer made it fatal: mt_release_contacts() cleared one bit per slot and overwrote the adjacent members of struct mt_device. The new device advertises a ContactCountMaximum of 250 while exposing only a few finger collections (a large contact count cannot be expressed with one finger collection per contact within the HID descriptor size limit). The test sends a single contact and lets the 100ms sticky-fingers timer release it. A kernel without the fix panics in mt_release_contacts(); a fixed kernel reports the release cleanly. Signed-off-by: Trung Nguyen Signed-off-by: Benjamin Tissoires --- .../testing/selftests/hid/tests/test_multitouch.py | 114 +++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/tools/testing/selftests/hid/tests/test_multitouch.py b/tools/testing/selftests/hid/tests/test_multitouch.py index fa4fb2054bd4..7897340118b4 100644 --- a/tools/testing/selftests/hid/tests/test_multitouch.py +++ b/tools/testing/selftests/hid/tests/test_multitouch.py @@ -513,6 +513,79 @@ class SmartTechDigitizer(Digitizer): return absinfo is not None and absinfo.resolution == 3 +class MinWin8TSParallelBigContactMax(Digitizer): + """A parallel Win8 touchscreen that advertises a ContactCountMaximum much + larger than the number of contacts it actually reports. + + Such firmware makes the driver allocate that many input slots (up to 255) + while the input report only carries a few contacts. This is what used to + drive the per-slot bit operations on mt_io_flags out of bounds. The number + of contacts a HID report can describe is limited by the descriptor size, + so a large ContactCountMaximum can only be expressed this way, decoupled + from the number of finger collections.""" + + def __init__(self, n_fingers=5, contact_max=250): + self.phys_max = 120, 90 + rdesc_finger_str = f""" + Usage Page (Digitizers) + Usage (Finger) + Collection (Logical) + Report Size (1) + Report Count (1) + Logical Minimum (0) + Logical Maximum (1) + Usage (Tip Switch) + Input (Data,Var,Abs) + Report Size (7) + Logical Maximum (127) + Input (Cnst,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Id) + Input (Data,Var,Abs) + Report Size (16) + Unit Exponent (-1) + Unit (SILinear: cm) + Logical Maximum (4095) + Physical Minimum (0) + Physical Maximum ({self.phys_max[0]}) + Usage Page (Generic Desktop) + Usage (X) + Input (Data,Var,Abs) + Physical Maximum ({self.phys_max[1]}) + Usage (Y) + Input (Data,Var,Abs) + End Collection +""" + rdesc_str = f""" + Usage Page (Digitizers) + Usage (Touch Screen) + Collection (Application) + Report ID (1) + {rdesc_finger_str * n_fingers} + Unit Exponent (-4) + Unit (SILinear: s) + Logical Maximum (65535) + Physical Maximum (65535) + Usage Page (Digitizers) + Usage (Scan Time) + Input (Data,Var,Abs) + Report Size (8) + Logical Maximum (255) + Usage (Contact Count) + Input (Data,Var,Abs) + Report ID (2) + Logical Maximum ({contact_max}) + Usage (Contact Max) + Feature (Data,Var,Abs) + End Collection + {Digitizer.msCertificationBlob(68)} +""" + super().__init__( + f"uhid test parallel big contact max {contact_max}", rdesc_str + ) + + class BaseTest: class TestMultitouch(base.BaseTestCase.TestUhid): kernel_modules = [KERNEL_MODULE] @@ -1735,6 +1808,47 @@ class TestMinWin8TSParallel(BaseTest.TestWin8Multitouch): return MinWin8TSParallel(10) +class TestMinWin8TSParallelBigContactMax(base.BaseTestCase.TestUhid): + """Regression test for the out-of-bounds bit operations on + struct mt_device.mt_io_flags. + + A Win8 touchscreen may advertise a ContactCountMaximum much larger than + the number of contacts it reports. The driver used to keep the per-slot + active state in the bits of a single unsigned long while indexing + set_bit()/clear_bit() by the slot number, so such a device drove those bit + operations out of bounds. The sticky-fingers release timer made it fatal: + mt_release_contacts() cleared one bit per slot, overwrote the adjacent + struct mt_device members and panicked the kernel. + + Send a single contact, let the 100ms sticky-fingers timer release it, and + check that the kernel reports the release cleanly instead of crashing.""" + + kernel_modules = [KERNEL_MODULE] + + def create_device(self): + return MinWin8TSParallelBigContactMax() + + def test_sticky_fingers_release_big_contact_max(self): + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + assert evdev.num_slots == uhdev.max_contacts + + t0 = Touch(1, 5, 10) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + + # do not release the contact; the sticky-fingers timer must do it + # after 100ms, which is where the out-of-bounds release used to hit + time.sleep(0.2) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + class TestMinWin8TSHybrid(BaseTest.TestWin8Multitouch): def create_device(self): return MinWin8TSHybrid() -- cgit v1.2.3 From 4cd774c1feb3f720265c512174c5c3312eca1be2 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 10 Apr 2026 02:38:51 +0530 Subject: arm64: dts: qcom: purwa: Fix GPU IOMMU property Purwa's GPU does not support SID 1, which is typically used for LPAC-related traffic. Remove SID 1 from the GPU node's iommus property to accurately describe the hardware. This fixes the splat below, seen with some versions of Gunyah hypervisor: Internal error: synchronous external abort: 0000000096000010 [#1] SMP CPU: 0 UID: 0 PID: 80 Comm: kworker/u33:2 Tainted: G M Tainted: [M]=MACHINE_CHECK Hardware name: Qualcomm Technologies, Inc. Purwa IoT EVK (DT) Workqueue: events_unbound deferred_probe_work_func pstate: 21400005 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) pc : arm_smmu_write_s2cr+0x9c/0xbc lr : arm_smmu_master_install_s2crs+0x78/0xa4 sp : ffff80008039b570 x29: ffff80008039b570 x28: 0000000000000000 x27: ffffaddd62f1ab78 x26: ffff00080a4ff280 x25: 0000000000000018 x24: ffff00080b896480 x23: ffff00080ba9b7a0 x22: ffff00080bb05160 x21: 0000000000000000 x20: 0000000000000000 x19: 0000000000000001 x18: 00000000ffffffff x17: 0000000000000000 x16: 0000000000000000 x15: ffff80008039b1d0 x14: ffff80010039b37d x13: 00746c7561662d74 x12: 0000000000000000 x11: ffff00080b7fbd98 x10: ffffffffffffffc0 x9 : ffffffffffffffff x8 : 0000000000000228 x7 : 0000000000000e87 x6 : 0000000000000000 x5 : 0000000000000000 x4 : ffff00080a4ff280 x3 : 0000000000000000 x2 : ffff800082a40c04 x1 : 0000000000000000 x0 : ffff800082a40000 Call trace: arm_smmu_write_s2cr+0x9c/0xbc (P) arm_smmu_master_install_s2crs+0x78/0xa4 arm_smmu_attach_dev+0xb0/0x1d8 __iommu_device_set_domain+0x84/0x11c __iommu_group_set_domain_internal+0x60/0x120 __iommu_attach_group+0x88/0x9c iommu_attach_device+0x6c/0xa0 msm_iommu_new.part.0+0x84/0xe4 [msm] msm_iommu_gpu_new+0x3c/0x104 [msm] adreno_iommu_create_vm+0x24/0xc8 [msm] a6xx_create_vm+0x48/0x78 [msm] msm_gpu_init+0x2d8/0x508 [msm] adreno_gpu_init+0x208/0x324 [msm] a6xx_gpu_init+0x604/0x8cc [msm] adreno_bind+0xb4/0x124 [msm] component_bind_all+0x114/0x23c msm_drm_init+0x1b0/0x1ec [msm] msm_drm_bind+0x30/0x3c [msm] try_to_bring_up_aggregate_device+0x164/0x1d0 __component_add+0xa4/0x16c component_add+0x14/0x20 msm_dp_display_probe_tail+0x4c/0xac [msm] msm_dp_auxbus_done_probe+0x14/0x20 [msm] dp_aux_ep_probe+0x4c/0xf4 [drm_dp_aux_bus] really_probe+0xbc/0x29c __driver_probe_device+0x78/0x12c driver_probe_device+0x3c/0x15c __device_attach_driver+0xb8/0x134 bus_for_each_drv+0x88/0xe8 __device_attach+0xa0/0x190 device_initial_probe+0x50/0x54 bus_probe_device+0x38/0xa4 deferred_probe_work_func+0x88/0xc0 process_one_work+0x148/0x28c worker_thread+0x2cc/0x3d4 kthread+0x12c/0x204 ret_from_fork+0x10/0x20 ---[ end trace 0000000000000000 ]--- Fixes: 1aa0b4e36436 ("arm64: dts: qcom: x1p42100: Add GPU support") Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260410-purwa-gpu-dt-fix-v1-1-4637892156cf@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/purwa.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/purwa.dtsi b/arch/arm64/boot/dts/qcom/purwa.dtsi index 9ab4f26b35f2..5b17840fb62f 100644 --- a/arch/arm64/boot/dts/qcom/purwa.dtsi +++ b/arch/arm64/boot/dts/qcom/purwa.dtsi @@ -47,6 +47,8 @@ &gpu { compatible = "qcom,adreno-43030c00", "qcom,adreno"; + iommus = <&adreno_smmu 0 0x0>; + nvmem-cells = <&gpu_speed_bin>; nvmem-cell-names = "speed_bin"; -- cgit v1.2.3 From d291245e2fb1eba55d751f88613a41e292958a96 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 7 Apr 2026 22:15:19 +0200 Subject: arm64: dts: qcom: monaco: Add default GIC address cells Add missing address-cells 0 to GIC interrupt node to silence W=1 warning: monaco.dtsi:2326.4-2329.30: Warning (interrupt_map): /soc@0/pci@1c00000:interrupt-map: Missing property '#address-cells' in node /soc@0/interrupt-controller@17a00000, using 0 as fallback Value '0' is correct because: 1. GIC interrupt controller does not have children, 2. interrupt-map property (in PCI node) consists of five components and the fourth component 'parent unit address', which size is defined by '#address-cells' of the node pointed to by the interrupt-parent component, is not used (=0). Fixes: 46a7c01e7e9d ("arm64: dts: qcom: qcs8300: enable pcie0") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Manivannan Sadhasivam Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260407201518.24949-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/monaco.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index e4c8466f941b..a350a8ba48fa 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -7384,6 +7384,7 @@ interrupt-controller; #redistributor-regions = <1>; redistributor-stride = <0x0 0x20000>; + #address-cells = <0>; }; watchdog@17c10000 { -- cgit v1.2.3 From 41d237b1546af5d5cf877175930cd23163422c83 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Wed, 15 Apr 2026 23:45:18 +0000 Subject: arm64: dts: qcom: sm8650: Fix IPA IMEM slice Downstream the IPA IMEM slice for SM8650 is described as: qcom,additional-mapping = <0x14683000 0x14683000 0x2000>; Update upstream ipa_modem_tables to reflect downstream. Signed-off-by: Alexander Koskovich Fixes: 581fc5d5ade6 ("arm64: dts: qcom: sm8650: Explicitly describe the IPA IMEM slice") Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260415-fix-8650-ipa-modem-tables-v1-1-95f8f425e416@pm.me Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sm8650.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi index 160ead25ecf7..65c4a2b46d21 100644 --- a/arch/arm64/boot/dts/qcom/sm8650.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi @@ -7091,8 +7091,8 @@ ranges = <0 0 0x14680000 0x2c000>; - ipa_modem_tables: modem-tables@8000 { - reg = <0x8000 0x2000>; + ipa_modem_tables: modem-tables@3000 { + reg = <0x3000 0x2000>; }; }; -- cgit v1.2.3 From 96f65d01f3132a163a8c0e84aca00f2acdc75e9b Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Mon, 20 Apr 2026 01:32:49 +0800 Subject: arm64: dts: qcom: sc8280xp: add several missing pdc map entries pdc 215, 256, 257 are missing, but we can find tlmm pin 103, 84, 90 are mapped to them respectively, so add the map entries from pdc to gic. These entries are reversed from .data section of qcgpio.sys Signed-off-by: Pengyu Luo Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260419173251.1180026-1-mitltlatltl@gmail.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index a2bd6b10e475..b0de9e262f29 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -5356,7 +5356,7 @@ <211 699 1>, <212 705 1>, <213 450 1>, - <214 643 1>, + <214 643 2>, <216 646 5>, <221 390 5>, <226 700 3>, @@ -5379,7 +5379,7 @@ <252 798 1>, <253 765 1>, <254 763 1>, - <255 454 1>, + <255 454 3>, <258 139 1>, <259 786 2>, <261 370 2>, -- cgit v1.2.3 From 6267f93eac9aa6963797c5bc1a18de9571ccac26 Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Mon, 20 Apr 2026 01:32:50 +0800 Subject: arm64: dts: qcom: sc8280xp: gaokun3: correct EC interrupt pin Unlike other sc8280xp platforms, on this platform, in dsdt, gpio 0x2c0 is not mapped to gpio107, it is gpio103, so fix it. I found this until I did a trigger track, irq is regularly triggerd every several millisecs. In the past, since here gpio107 was low forever, ec irq would keep to be triggered and polling the event every several millisecs. About how to get the map, please check openbsd driver for it https://github.com/openbsd/src/blob/master/sys/dev/acpi/qcgpio.c Signed-off-by: Pengyu Luo Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260419173251.1180026-2-mitltlatltl@gmail.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts index f3c00be67081..cd575e20d90a 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts @@ -637,7 +637,7 @@ compatible = "huawei,gaokun3-ec"; reg = <0x38>; - interrupts-extended = <&tlmm 107 IRQ_TYPE_LEVEL_LOW>; + interrupts-extended = <&tlmm 103 IRQ_TYPE_LEVEL_LOW>; #address-cells = <1>; #size-cells = <0>; -- cgit v1.2.3 From 44c74d27d1b9aaa99fa8a83640c1223575262b80 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Thu, 18 Jun 2026 13:55:14 -0400 Subject: 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, -- cgit v1.2.3 From 63a76e3a587c4143e8e24e8a6b0c232fa0676034 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 13:42:59 +0800 Subject: 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 -- cgit v1.2.3 From 7dc4de2aa6316f1d044cde21f5acfec5f3ec6b47 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 13:44:07 +0800 Subject: 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]; -- cgit v1.2.3 From 56acfeb10019e200ab6787d01f8d7cbe0f01526f Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 25 Jun 2026 23:53:36 +0000 Subject: 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 @@ err_af: 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; -- cgit v1.2.3 From 1e56f30a73f304fe26a272742c398aedd88a1a6c Mon Sep 17 00:00:00 2001 From: Guzebing Date: Sun, 21 Jun 2026 09:40:30 +0800 Subject: 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); -- cgit v1.2.3 From 4b0363cb1f3ec42b0b1346e5ab0b8a3dceeee9be Mon Sep 17 00:00:00 2001 From: Sarthak Sharma Date: Mon, 8 Jun 2026 16:02:24 +0530 Subject: selftests/mm: fix ksft_process_madv.sh test category ksft_process_madv.sh currently runs run_vmtests.sh with the mmap category. Update it to run the process_madv category, since ksft_mmap.sh already runs the mmap category tests. This avoids running mmap tests twice and ensures that process_madv tests are run through the kselftest harness. Link: https://lore.kernel.org/20260608103224.344101-1-sarthak.sharma@arm.com Fixes: 6ce964c02f1c ("selftests/mm: have the harness run each test category separately") Signed-off-by: Sarthak Sharma Reviewed-by: Mark Brown Reviewed-by: Dev Jain Acked-by: David Hildenbrand (Arm) Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/ksft_process_madv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/ksft_process_madv.sh b/tools/testing/selftests/mm/ksft_process_madv.sh index 2c3137ae8bc8..edad2d2d888f 100755 --- a/tools/testing/selftests/mm/ksft_process_madv.sh +++ b/tools/testing/selftests/mm/ksft_process_madv.sh @@ -1,4 +1,4 @@ #!/bin/sh -e # SPDX-License-Identifier: GPL-2.0 -./run_vmtests.sh -t mmap +./run_vmtests.sh -t process_madv -- cgit v1.2.3 From 65476d31d8056e859c48580f82295ce159196ffe Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 Jun 2026 16:56:58 +0800 Subject: mm: shrinker: fix shrinker_info teardown race with expansion expand_shrinker_info() iterates all visible memcgs under shrinker_mutex, including memcgs that have not finished ->css_online() yet. Once pn->shrinker_info has been published, teardown must stay serialized with expand_shrinker_info() until that memcg is either fully online or no longer visible to iteration. Today alloc_shrinker_info() breaks that rule by dropping shrinker_mutex before freeing a partially initialized shrinker_info array, which may cause the following race: CPU0 CPU1 ==== ==== css_create --> list_add_tail_rcu(&css->sibling, &parent_css->children); online_css --> mem_cgroup_css_online --> alloc_shrinker_info --> alloc node0 info rcu_assign_pointer(C->node0->shrinker_info, old0) alloc node1 info -> FAIL -> goto err mutex_unlock(shrinker_mutex) shrinker_alloc() --> shrinker_memcg_alloc --> mutex_lock(shrinker_mutex) expand_shrinker_info --> mem_cgroup_iter see the memcg expand_one_shrinker_info --> old0 = C->node0->shrinker_info memcpy(new->unit, old0->unit, ...); free_shrinker_info --> kvfree(old0); /* double free !! */ kvfree_rcu(old0, rcu); The same problem exists later in mem_cgroup_css_online(). If alloc_shrinker_info() succeeds but a subsequent objcg allocation fails, the free_objcg -> free_shrinker_info() unwind path tears down the already published pn->shrinker_info arrays without shrinker_mutex. The expand_one_shrinker_info() can race with that teardown in the same way, leading to use-after-free or double-free of the old shrinker_info. Fix this by serializing shrinker_info teardown with shrinker_mutex, and by keeping alloc_shrinker_info() error cleanup inside the locked section. Link: https://lore.kernel.org/20260617085658.27096-1-qi.zheng@linux.dev Fixes: 307bececcd12 ("mm: shrinker: add a secondary array for shrinker_info::{map, nr_deferred}") Signed-off-by: Qi Zheng Acked-by: Muchun Song Cc: Dave Chinner Cc: Qi Zheng Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton --- mm/shrinker.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mm/shrinker.c b/mm/shrinker.c index 7082d01c8c9d..a70aab124a0e 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -59,12 +59,14 @@ static inline int shrinker_unit_alloc(struct shrinker_info *new, return 0; } -void free_shrinker_info(struct mem_cgroup *memcg) +static void __free_shrinker_info(struct mem_cgroup *memcg) { struct mem_cgroup_per_node *pn; struct shrinker_info *info; int nid; + lockdep_assert_held(&shrinker_mutex); + for_each_node(nid) { pn = memcg->nodeinfo[nid]; info = rcu_dereference_protected(pn->shrinker_info, true); @@ -74,6 +76,13 @@ void free_shrinker_info(struct mem_cgroup *memcg) } } +void free_shrinker_info(struct mem_cgroup *memcg) +{ + mutex_lock(&shrinker_mutex); + __free_shrinker_info(memcg); + mutex_unlock(&shrinker_mutex); +} + int alloc_shrinker_info(struct mem_cgroup *memcg) { int nid, ret = 0; @@ -98,8 +107,8 @@ int alloc_shrinker_info(struct mem_cgroup *memcg) return ret; err: + __free_shrinker_info(memcg); mutex_unlock(&shrinker_mutex); - free_shrinker_info(memcg); return -ENOMEM; } -- cgit v1.2.3 From e30453c61e185e914fde83c650e268067b140218 Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Wed, 17 Jun 2026 17:00:52 +0800 Subject: mm: shrinker: fix NULL pointer dereference in debugfs shrinker_debugfs_add() creates both "count" and "scan" debugfs files unconditionally. That assumes every shrinker implements both count_objects() and scan_objects(), which is not guaranteed. For example, the xen-backend shrinker sets count_objects() but leaves scan_objects() NULL, so writing to its scan file calls through a NULL function pointer and panics the kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. Call Trace: shrinker_debugfs_scan_write+0x12e/0x270 full_proxy_write+0x5f/0x90 vfs_write+0xde/0x420 ? filp_flush+0x75/0x90 ? filp_close+0x1d/0x30 ? do_dup2+0xb8/0x120 ksys_write+0x68/0xf0 ? filp_flush+0x75/0x90 do_syscall_64+0xb3/0x5b0 entry_SYSCALL_64_after_hwframe+0x76/0x7e The count path has the same issue in principle if a shrinker omits count_objects(). To fix it, only create "count" and "scan" debugfs files when the corresponding callbacks are present. Link: https://lore.kernel.org/20260617090052.27325-1-qi.zheng@linux.dev Fixes: bbf535fd6f06 ("mm: shrinkers: add scan interface for shrinker debugfs") Signed-off-by: Qi Zheng Reviewed-by: Muchun Song Cc: Dave Chinner Cc: Qi Zheng Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton --- mm/shrinker_debug.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c index cda4e86428c8..cafb56630132 100644 --- a/mm/shrinker_debug.c +++ b/mm/shrinker_debug.c @@ -183,10 +183,12 @@ int shrinker_debugfs_add(struct shrinker *shrinker) } shrinker->debugfs_entry = entry; - debugfs_create_file("count", 0440, entry, shrinker, - &shrinker_debugfs_count_fops); - debugfs_create_file("scan", 0220, entry, shrinker, - &shrinker_debugfs_scan_fops); + if (shrinker->count_objects) + debugfs_create_file("count", 0440, entry, shrinker, + &shrinker_debugfs_count_fops); + if (shrinker->scan_objects) + debugfs_create_file("scan", 0220, entry, shrinker, + &shrinker_debugfs_scan_fops); return 0; } -- cgit v1.2.3 From d58fdbe37a829fd2e5803dd4e5a72992dd8c5368 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 17 Jun 2026 17:56:47 -0700 Subject: mm/damon/sysfs-schemes: fix dir put orders in access_pattern_add_dirs() Patch series "mm/damon/sysfs-schemes: fix wrong directories put orders in error paths". Error paths of damon_sysfs_access_pattern_add_dirs() and damon_sysfs_scheme_add_dirs() functions put references to directories in wrong orders. As a result, uninitialized memory dereference and/or memory leak can happen. Fix those. This patch (of 2): In access_pattern_add_dirs(), error handling path puts references starting from setup failed directories. If the failure happpened from the initial allication in the setup functions, uninitialized memory dereference happen. The allocation failures will not commonly happen, but the consequence is quite bad. Fix the wrong reference put orders. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260618005650.83868-2-sj@kernel.org Link: https://lore.kernel.org/20260617060005.86852-1-sj@kernel.org [1] Fixes: 7e84b1f8212a ("mm/damon/sysfs: support DAMON-based Operation Schemes") Signed-off-by: SeongJae Park Cc: # 5.18.x Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 329cfd0bbe9f..7c00aa78b2f5 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1993,22 +1993,19 @@ static int damon_sysfs_access_pattern_add_dirs( err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->sz, "sz"); if (err) - goto put_sz_out; + return err; err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->nr_accesses, "nr_accesses"); if (err) - goto put_nr_accesses_sz_out; + goto put_sz_out; err = damon_sysfs_access_pattern_add_range_dir(access_pattern, &access_pattern->age, "age"); if (err) - goto put_age_nr_accesses_sz_out; + goto put_nr_accesses_sz_out; return 0; -put_age_nr_accesses_sz_out: - kobject_put(&access_pattern->age->kobj); - access_pattern->age = NULL; put_nr_accesses_sz_out: kobject_put(&access_pattern->nr_accesses->kobj); access_pattern->nr_accesses = NULL; -- cgit v1.2.3 From 05ea83ee88ca70f8932906d9f2617ff996f45b50 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Wed, 17 Jun 2026 17:56:48 -0700 Subject: mm/damon/sysfs-schemes: put stats for scheme_add_dirs() internal error damon_sysfs_scheme_add_dirs() setup the tried_regions directory after the stats directory setup is completed. When the tried_regions directory setup is failed, the setup function ensures the reference for the tried regions directory is released. Hence the error path should put references on setup succeeded directory objects, starting from the stats directory. However, the error path is putting the tried_regions directory instead of the stats directory. As a direct result, the stats directory object is leaked. Worse yet, if the tried_regions directory setup failed from the initial allocation, the scheme->tried_regions field remains uninitialized. The following kobject_put(&scheme->tried_regions->kobj) call in the error path will dereference the uninitialized memory. The setup failures should not be common. But once it happens, the consequence is quite bad. Fix this issue by correctly putting the stats directory instead of the tried_regions directory. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260618005650.83868-3-sj@kernel.org Link: https://lore.kernel.org/20260617005223.96813-1-sj@kernel.org [1] Fixes: 5181b75f438d ("mm/damon/sysfs-schemes: implement schemes/tried_regions directory") Signed-off-by: SeongJae Park Cc: # 6.2.x Signed-off-by: Andrew Morton --- mm/damon/sysfs-schemes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 7c00aa78b2f5..0134111c3c1f 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -2513,12 +2513,12 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme) goto put_filters_watermarks_quotas_access_pattern_out; err = damon_sysfs_scheme_set_tried_regions(scheme); if (err) - goto put_tried_regions_out; + goto put_stats_out; return 0; -put_tried_regions_out: - kobject_put(&scheme->tried_regions->kobj); - scheme->tried_regions = NULL; +put_stats_out: + kobject_put(&scheme->stats->kobj); + scheme->stats = NULL; put_filters_watermarks_quotas_access_pattern_out: kobject_put(&scheme->ops_filters->kobj); scheme->ops_filters = NULL; -- cgit v1.2.3 From 7da7d599b8a83271c464adfd5ef160202b470570 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Mon, 22 Jun 2026 11:30:42 -0400 Subject: mm/compaction: handle free_pages_prepare() properly in compaction_free() free_pages_prepare() can fail but compaction_free() does not handle the failure case. Failed pages should not be added back to cc->freepages for future use, since they can be either PageHWPoison or free_page_is_bad() and might cause data corruption. Link: https://lore.kernel.org/20260622-handle_free_pages_prepare_in_compaction_free-v1-1-fcf3b14abcf7@nvidia.com Fixes: 733aea0b3a7b ("mm/compaction: add support for >0 order folio memory compaction.") Signed-off-by: Zi Yan Reviewed-by: Vlastimil Babka (SUSE) Acked-by: Johannes Weiner Reviewed-by: Baolin Wang Reviewed-by: Lance Yang Cc: Brendan Jackman Cc: Jiaqi Yan Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Signed-off-by: Andrew Morton --- mm/compaction.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mm/compaction.c b/mm/compaction.c index b776f35ad020..f08765ade014 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1875,15 +1875,14 @@ static void compaction_free(struct folio *dst, unsigned long data) int order = folio_order(dst); struct page *page = &dst->page; - if (folio_put_testzero(dst)) { - free_pages_prepare(page, order); + if (folio_put_testzero(dst) && free_pages_prepare(page, order)) { list_add(&dst->lru, &cc->freepages[order]); cc->nr_freepages += 1 << order; } cc->nr_migratepages += 1 << order; /* - * someone else has referenced the page, we cannot take it back to our - * free list. + * someone else has referenced the page or free_pages_prepare() fails, + * we cannot take it back to our free list. */ } -- cgit v1.2.3 From 1cb6cf6f2b38d56f9e5e9e7c80c5d482c51874f3 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Mon, 22 Jun 2026 16:59:13 +0100 Subject: MAINTAINERS: add Lance as an rmap reviewer Lance has been doing excellent work reviewing rmap series and has proven himself to be a great member of the community in general, so add him as an rmap reviewer. Link: https://lore.kernel.org/20260622155913.280355-1-ljs@kernel.org Signed-off-by: Lorenzo Stoakes Acked-by: Vlastimil Babka (SUSE) Acked-by: David Hildenbrand (Arm) Acked-by: SeongJae Park Acked-by: Harry Yoo (Oracle) Acked-by: Dev Jain Acked-by: Barry Song Acked-by: Lance Yang Cc: Jann Horn Cc: Liam R. Howlett Cc: Rik van Riel Signed-off-by: Andrew Morton --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..eed632c30e01 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17205,6 +17205,7 @@ R: Liam R. Howlett R: Vlastimil Babka R: Harry Yoo R: Jann Horn +R: Lance Yang L: linux-mm@kvack.org S: Maintained F: include/linux/rmap.h -- cgit v1.2.3 From 35d4a3cf70a855b50e53189ac2f8463e20a02046 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 23 Jun 2026 06:58:31 -0700 Subject: mm/damon/ops-common: handle extreme intervals in damon_hot_score() Fix three issues in damon_hot_score() that comes from wrong handling of extreme (zero or too high) monitoring intervals user setup. When the user sets sampling interval zero, damon_max_nr_accesses(), which is called from damon_hot_score(), causes a divide-by-zero. Needless to say, it is a problem. When the user sets the aggregation interval zero, the function returns zero. It is wrong, since the real maximum nr_acceses in the setup should be one. Worse yet, it can cause another divide-by-zero from its caller, damon_hot_score(), since it uses damon_max_nr_accesses() return value as a denominator. When the user sets the aggregation interval very high, damon_hot_score() could return a value out of [0, DAMOS_MAX_SCORE] range. Since the return value is used as an index to the regions_score_histogram array, which is DAMOS_MAX_SCORE+1 size, it causes out of bounds array access. The issues can be relatively easily reproduced like below. The sysfs write permission is required, though. # ./damo start --damos_action lru_prio --damos_quota_space 100M \ --damos_quota_interval 1s # cd /sys/kernel/mm/damon/admin/kdamonds/0 # echo 0 > contexts/0/monitoring_attrs/intervals/sample_us # echo 0 > contexts/0/monitoring_attrs/intervals/aggr_us # echo commit > state # dmesg [...] [ 131.329762] Oops: divide error: 0000 [#1] SMP NOPTI [...] [ 131.336089] RIP: 0010:damon_hot_score+0x27/0xd0 [...] Fix the divide-by-zero intervals problems by explicitly handling the zero intervals in damon_max_nr_accesses(). Fix the out-of-bound array access by applying [0, DAMOS_MAX_SCORE] bounds before returning from damon_hot_score(). The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260623135834.67189-1-sj@kernel.org Link: https://lore.kernel.org/20260619202459.145010-1-sj@kernel.org [1] Fixes: 198f0f4c58b9 ("mm/damon/vaddr,paddr: support pageout prioritization") Signed-off-by: SeongJae Park Cc: # 5.16.x Signed-off-by: Andrew Morton --- include/linux/damon.h | 8 ++++++-- mm/damon/ops-common.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 6f7edb3590ef..888570f55b41 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1065,9 +1065,13 @@ static inline bool damon_target_has_pid(const struct damon_ctx *ctx) static inline unsigned int damon_max_nr_accesses(const struct damon_attrs *attrs) { - /* {aggr,sample}_interval are unsigned long, hence could overflow */ - return min(attrs->aggr_interval / attrs->sample_interval, + unsigned long sample_interval; + unsigned long max_nr_accesses; + + sample_interval = attrs->sample_interval ? : 1; + max_nr_accesses = min(attrs->aggr_interval / sample_interval, (unsigned long)UINT_MAX); + return max_nr_accesses ? : 1; } diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 5c93ef2bb8a9..d1842e2b00ef 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -143,6 +143,7 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r, * Transform it to fit in [0, DAMOS_MAX_SCORE] */ hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE; + hotness = max(min(hotness, DAMOS_MAX_SCORE), 0); return hotness; } -- cgit v1.2.3 From ffd017237cfe99e6e5602ab14179b0e6878a0840 Mon Sep 17 00:00:00 2001 From: Ketan Date: Tue, 23 Jun 2026 02:48:04 +0530 Subject: mm: page_ext: add count limit to page_ext_iter_next to prevent invalid PFN access The page_ext iteration API does not validate if the PFN still belongs to a valid section while advancing the iterator. When dynamically adding memory in the hotplug path, it can lead to a NULL pointer dereference during page_ext_lookup at the boundary of the last valid section when iterator count equals __pgcount. The for_each_page_ext() macro calls page_ext_iter_next() as its loop increment. for_each_page_ext() does a "__page_ext = page_ext_iter_next(&__iter)" at the end. This causes page_ext_iter_next() to increment iter->index past __pgcount and call page_ext_lookup(start_pfn + __pgcount). During memory hotplug (online), the PFN at start_pfn + __pgcount may belong to a section that has not yet been initialized, causing page_ext_lookup() to trigger a NULL pointer dereference. [ 14.555124][ T846] Call trace: [ 14.555125][ T846] lookup_page_ext+0x6c/0x108 (P) [ 14.555127][ T846] page_ext_lookup+0x30/0x3c [ 14.555129][ T846] __reset_page_owner+0x11c/0x260 [ 14.571201][ T846] __free_pages_ok+0x5e8/0x8e0 [ 14.571204][ T846] __free_pages_core+0x78/0xf0 [ 14.571206][ T846] generic_online_page+0x14/0x24 [ 14.597782][ T846] online_pages+0x178/0x30c [ 14.597784][ T846] memory_block_change_state+0x284/0x32c [ 14.597787][ T846] memory_subsys_online+0x4c/0x64 [ 14.597789][ T846] device_online+0x88/0xb0 [ 14.597791][ T846] online_memory_block+0x30/0x40 [ 14.597793][ T846] walk_memory_blocks+0xac/0xe8 [ 14.597794][ T846] add_memory_resource+0x280/0x298 [ 14.656161][ T846] add_memory+0x60/0x98 Move the iteration boundary enforcement inside the iterator functions, so callers cannot inadvertently access beyond the requested range. Link: https://lore.kernel.org/20260623-page_ext-v3-1-a89799a5367c@oss.qualcomm.com Fixes: 9039b9096ea2 ("mm: page_ext: add an iteration API for page extensions") Signed-off-by: Ketan Kishore Suggested-by: David Hildenbrand Suggested-by: Matthew Wilcox Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Brendan Jackman Cc: Johannes Weiner Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Luiz Capitulino Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- include/linux/page_ext.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/include/linux/page_ext.h b/include/linux/page_ext.h index 61e876e255e8..f23d4b218da0 100644 --- a/include/linux/page_ext.h +++ b/include/linux/page_ext.h @@ -120,14 +120,18 @@ struct page_ext_iter { * page_ext_iter_begin() - Prepare for iterating through page extensions. * @iter: page extension iterator. * @pfn: PFN of the page we're interested in. + * @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no page_ext exists for this page. */ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter, - unsigned long pfn) + unsigned long pfn, unsigned long count) { + if (!count) + return NULL; + iter->index = 0; iter->start_pfn = pfn; iter->page_ext = page_ext_lookup(pfn); @@ -138,19 +142,22 @@ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter, /** * page_ext_iter_next() - Get next page extension * @iter: page extension iterator. + * @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no next page_ext exists. */ -static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter) +static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter, + unsigned long count) { unsigned long pfn; if (WARN_ON_ONCE(!iter->page_ext)) return NULL; - iter->index++; + if (++iter->index >= count) + return NULL; pfn = iter->start_pfn + iter->index; if (page_ext_iter_next_fast_possible(pfn)) @@ -183,9 +190,9 @@ static inline struct page_ext *page_ext_iter_get(const struct page_ext_iter *ite * IMPORTANT: must be called with RCU read lock taken. */ #define for_each_page_ext(__page, __pgcount, __page_ext, __iter) \ - for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page));\ - __page_ext && __iter.index < __pgcount; \ - __page_ext = page_ext_iter_next(&__iter)) + for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page), __pgcount); \ + __page_ext; \ + __page_ext = page_ext_iter_next(&__iter, __pgcount)) #else /* !CONFIG_PAGE_EXTENSION */ struct page_ext; -- cgit v1.2.3 From 81401cebfc1598306b0a981b5f9ee5b58c1aac52 Mon Sep 17 00:00:00 2001 From: Jinjiang Tu Date: Fri, 26 Jun 2026 09:32:52 +0800 Subject: fs/proc: fix KPF_KSM reported for all anonymous pages Reading /proc/kpageflags for any anonymous page returns KPF_KSM set, even when KSM is not in use. As a result, tools misclassify all anonymous pages as KSM merged. In stable_page_flags(), if the page is anonymous, then use (mapping & FOLIO_MAPPING_KSM) check to identify if the anonymous page is KSM page. However, FOLIO_MAPPING_KSM is FOLIO_MAPPING_ANON | FOLIO_MAPPING_ANON_KSM, (mapping & FOLIO_MAPPING_KSM) check returns true for all anonymous pages. To fix it, use FOLIO_MAPPING_ANON_KSM instead. Link: https://lore.kernel.org/20260629033122.774318-1-tujinjiang@huawei.com Link: https://lore.kernel.org/20260626013252.2846774-1-tujinjiang@huawei.com Fixes: dee3d0bef2b0 ("proc: rewrite stable_page_flags()") Signed-off-by: Jinjiang Tu Acked-by: David Hildenbrand (Arm) Acked-by: Zi Yan Reviewed-by: Xu Xin Cc: Chengming Zhou Cc: Kefeng Wang Cc: Luiz Capitulino Cc: Matthew Wilcox (Oracle) Cc: Miaohe Lin Cc: Nanyong Sun Cc: Svetly Todorov Cc: Signed-off-by: Andrew Morton --- fs/proc/page.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/page.c b/fs/proc/page.c index f9b2c2c906cd..7d9387143435 100644 --- a/fs/proc/page.c +++ b/fs/proc/page.c @@ -173,7 +173,7 @@ u64 stable_page_flags(const struct page *page) u |= 1 << KPF_MMAP; if (is_anon) { u |= 1 << KPF_ANON; - if (mapping & FOLIO_MAPPING_KSM) + if ((mapping & FOLIO_MAPPING_FLAGS) == FOLIO_MAPPING_KSM) u |= 1 << KPF_KSM; } -- cgit v1.2.3 From dccf636bf1e68c3fda92f0c9e1018ab7e0ac8b2c Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sun, 28 Jun 2026 18:11:18 +0800 Subject: selftests/mm: pagemap_ioctl: use the correct page size for transact_test() There are several places in transact_test() where we use the hardcoded 0x1000 (4k) as page size, which is not always correct for architectures supporting multiple page sizes. Switch to use the correct page size. Otherwise ./ksft_pagemap.sh on a 16k-page-size arm64 box fails with $ ./ksft_pagemap.sh [...] # ok 96 mprotect_tests Both pages written after remap and mprotect # ok 97 mprotect_tests Clear and make the pages written # Bail out! ioctl failed # # Planned tests != run tests (117 != 97) # # Totals: pass:97 fail:0 xfail:0 xpass:0 skip:0 error:0 # [FAIL] not ok 1 pagemap_ioctl # exit=1 # SUMMARY: PASS=0 SKIP=0 FAIL=1 1..1 Link: https://lore.kernel.org/20260628101118.35861-1-zenghui.yu@linux.dev Fixes: 46fd75d4a3c9 ("selftests: mm: add pagemap ioctl tests") Signed-off-by: Zenghui Yu Cc: Muhammad Usama Anjum Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zenghui Yu Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/pagemap_ioctl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index 762306177ad8..6f8971d5b3ce 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -1368,7 +1368,7 @@ void *thread_proc(void *mem) ksft_exit_fail_msg("pthread_barrier_wait\n"); for (i = 0; i < access_per_thread; ++i) - __atomic_add_fetch(m + i * (0x1000 / sizeof(*m)), 1, __ATOMIC_SEQ_CST); + __atomic_add_fetch(m + i * (page_size / sizeof(*m)), 1, __ATOMIC_SEQ_CST); ret = pthread_barrier_wait(&end_barrier); if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD) @@ -1403,15 +1403,15 @@ static void transact_test(int page_size) if (pthread_barrier_init(&end_barrier, NULL, nthreads + 1)) ksft_exit_fail_msg("pthread_barrier_init\n"); - mem = mmap(NULL, 0x1000 * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, + mem = mmap(NULL, page_size * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (mem == MAP_FAILED) ksft_exit_fail_msg("Error mmap %s.\n", strerror(errno)); - wp_init(mem, 0x1000 * nthreads * pages_per_thread); - wp_addr_range(mem, 0x1000 * nthreads * pages_per_thread); + wp_init(mem, page_size * nthreads * pages_per_thread); + wp_addr_range(mem, page_size * nthreads * pages_per_thread); - memset(mem, 0, 0x1000 * nthreads * pages_per_thread); + memset(mem, 0, page_size * nthreads * pages_per_thread); count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size); ksft_test_result(count > 0, "%s count %u\n", __func__, count); @@ -1420,7 +1420,7 @@ static void transact_test(int page_size) finish = 0; for (i = 0; i < nthreads; ++i) - pthread_create(&th, NULL, thread_proc, mem + 0x1000 * i * pages_per_thread); + pthread_create(&th, NULL, thread_proc, mem + page_size * i * pages_per_thread); extra_pages = 0; for (i = 0; i < iter_count; ++i) { -- cgit v1.2.3 From fd5295afae916fb300890875ca53c527537d0c06 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Sun, 28 Jun 2026 22:31:11 +0800 Subject: selftests/mm: hmm-tests: include linux/mman.h to access MADV_COLLAPSE The following compilation error occurs with an old version of glibc due to a recent commit adding MADV_COLLAPSE testing: [root@localhost mm]# getconf GNU_LIBC_VERSION glibc 2.34 [root@localhost mm]# make CC hmm-tests hmm-tests.c: In function 'hmm_migrate_anon_huge_fault': hmm-tests.c:2355:27: error: 'MADV_COLLAPSE' undeclared (first use in this function); did you mean 'MADV_COLD'? 2355 | ret = madvise(map, size, MADV_COLLAPSE); | ^~~~~~~~~~~~~ | MADV_COLD hmm-tests.c:2355:27: note: each undeclared identifier is reported only once for each function it appears in make: *** [../lib.mk:225: /root/code/linux/tools/testing/selftests/mm/hmm-tests] Error 1 Include linux/mman.h (which provides the definition of MADV_COLLAPSE) to fix the build error. Link: https://lore.kernel.org/20260628143111.36863-1-zenghui.yu@linux.dev Fixes: e3d8707358ea ("selftests/mm/hmm-tests: test pagemap reads of PMD device-private entries") Signed-off-by: Zenghui Yu Reviewed-by: Lorenzo Stoakes Reviewed-by: Dev Jain Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/hmm-tests.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index e4c49699f3f7..2f2b9879d100 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From b9faea04ac04ec81588022c08cda877749291109 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Sun, 28 Jun 2026 11:02:03 -0400 Subject: mailmap: add entries for Radu Rendec I have used multiple email addresses for my kernel contributions, and some of them are no longer active. Add all to .mailmap for clarity. Link: https://lore.kernel.org/20260628150203.4105796-1-radu@rendec.net Signed-off-by: Radu Rendec Cc: Jakub Kicinski Signed-off-by: Andrew Morton --- .mailmap | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.mailmap b/.mailmap index 23eb9a4b04f4..be1f61db17a8 100644 --- a/.mailmap +++ b/.mailmap @@ -708,6 +708,10 @@ Qi Zheng Quentin Monnet Quentin Monnet Quentin Perret +Radu Rendec +Radu Rendec +Radu Rendec +Radu Rendec Rae Moar Rafael J. Wysocki Rajeev Nandan -- cgit v1.2.3 From 48a926cb6788afa2a528c2f59cead87f1e3d6e30 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Sun, 28 Jun 2026 15:08:03 -0700 Subject: mm/damon: add a kernel-doc comment for damon_ctx->probes The two fields of damon_ctx struct dont have their kernel-doc comments. That causes kernel document builds to warn. Fix those. This patch (of 2): Fix below document build warning: WARNING: ../include/linux/damon.h:909 struct member 'probes' not described in 'damon_ctx' Link: https://lore.kernel.org/20260628220808.98931-1-sj@kernel.org Link: https://lore.kernel.org/20260628220808.98931-2-sj@kernel.org Fixes: 18c777859f28 ("mm/damon/core: embed damon_probe objects in damon_ctx") Signed-off-by: SJ Park Reported-by: Randy Dunlap Closes: https://lore.kernel.org/4df95955-b255-4e5a-90c4-35db02f3111f@infradead.org Signed-off-by: Andrew Morton --- include/linux/damon.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index 888570f55b41..fdac40cd55d4 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -843,6 +843,7 @@ struct damon_attrs { * including damon_call() and damos_walk(). * * @ops: Set of monitoring operations for given use cases. + * @probes: Head of probes (&damon_probe) list. * @addr_unit: Scale factor for core to ops address conversion. * @min_region_sz: Minimum region size. * @pause: Pause kdamond main loop. -- cgit v1.2.3 From 968a672b99387c75585f5dfa623ac405f3e351e5 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Sun, 28 Jun 2026 15:08:04 -0700 Subject: mm/damon: add a kernel-doc comment for damon_ctx->rnd_state Fix below kernel document build warning: WARNING: ../include/linux/damon.h:909 struct member 'rnd_state' not described in 'damon_ctx' Link: https://lore.kernel.org/20260628220808.98931-3-sj@kernel.org Fixes: 9012c4e647df ("mm/damon: replace damon_rand() with a per-ctx lockless PRNG") Signed-off-by: SJ Park Reported-by: Randy Dunlap Closes: https://lore.kernel.org/4df95955-b255-4e5a-90c4-35db02f3111f@infradead.org Signed-off-by: Andrew Morton --- include/linux/damon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index fdac40cd55d4..02ac34537df9 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -849,6 +849,7 @@ struct damon_attrs { * @pause: Pause kdamond main loop. * @adaptive_targets: Head of monitoring targets (&damon_target) list. * @schemes: Head of schemes (&damos) list. + * @rnd_state: Per-ctx PRNG state for damon_rand(). */ struct damon_ctx { struct damon_attrs attrs; @@ -906,7 +907,6 @@ struct damon_ctx { struct list_head adaptive_targets; struct list_head schemes; - /* Per-ctx PRNG state for damon_rand(); kdamond is the sole consumer. */ struct rnd_state rnd_state; }; -- cgit v1.2.3 From 45a9591ec5c236d0eb2cf08e540d85392c0df773 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 29 Jun 2026 15:59:28 +0200 Subject: mm: a second pagecache maintainer As MM is slowly transitioning towards a more distributed maintainership model, we agreed with Matthew that I will be a co-maintainer in case he is not available. Link: https://lore.kernel.org/20260629135927.2586391-2-jack@suse.cz Signed-off-by: Jan Kara Acked-by: David Hildenbrand (Arm) Acked-by: Matthew Wilcox (Oracle) Acked-by: Lorenzo Stoakes Cc: Christian Brauner Signed-off-by: Andrew Morton --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index eed632c30e01..c760f7d889e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20406,7 +20406,7 @@ F: kernel/padata.c PAGE CACHE M: Matthew Wilcox (Oracle) -R: Jan Kara +M: Jan Kara L: linux-fsdevel@vger.kernel.org L: linux-mm@kvack.org S: Supported -- cgit v1.2.3 From 7746d72c64054976887928d64d2caf25c5a6dcc0 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 29 Jun 2026 07:44:31 -0700 Subject: samples/damon/mtier: fail early if address range parameters are invalid The comment on top of `struct damon_region` clearly says that For any use case, @ar should be non-zero positive size. which is now verified in damon_verify_new_region() if the kernel is built with DAMON_DEBUG_SANITY. The WARN_ONCE() can be triggered if the mtier sample module is enabled before node{0,1}_{start,end}_addr have been properly initialized, which is obviously not good. ------------[ cut here ]------------ start 0 >= end 0 WARNING: mm/damon/core.c:217 at damon_new_region+0xf4/0x118, CPU#59: bash/341468 Call trace: damon_new_region+0xf4/0x118 (P) damon_set_regions+0xfc/0x3c0 damon_sample_mtier_build_ctx+0xe8/0x3a8 damon_sample_mtier_start+0x1c/0x90 damon_sample_mtier_enable_store+0x98/0xb0 param_attr_store+0xb4/0x128 module_attr_store+0x2c/0x50 sysfs_kf_write+0x58/0x90 kernfs_fop_write_iter+0x16c/0x238 vfs_write+0x2c0/0x370 ksys_write+0x74/0x118 __arm64_sys_write+0x24/0x38 invoke_syscall+0xa8/0x118 el0_svc_common.constprop.0+0x48/0xf0 do_el0_svc+0x24/0x38 el0_svc+0x54/0x370 el0t_64_sync_handler+0xa0/0xe8 el0t_64_sync+0x1ac/0x1b0 ---[ end trace 0000000000000000 ]--- Note that the same issue can happen if detect_node_addresses is true, and node 0 or 1 is memoryless. Fix it together by checking the validity of parameters right before damon_new_region() and fail early if they're invalid. Link: https://lore.kernel.org/20260629144432.133962-1-sj@kernel.org Fixes: 82a08bde3cf7 ("samples/damon: implement a DAMON module for memory tiering") Signed-off-by: Zenghui Yu Signed-off-by: SJ Park Reviewed-by: SJ Park Cc: # 6.16.x Signed-off-by: Andrew Morton --- samples/damon/mtier.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c index eb1143de8df1..3785b0c7ffb1 100644 --- a/samples/damon/mtier.c +++ b/samples/damon/mtier.c @@ -120,6 +120,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) addr.end = promote ? node1_end_addr : node0_end_addr; } + if (addr.start >= addr.end) + goto free_out; + range.start = addr.start; range.end = addr.end; -- cgit v1.2.3 From e187bc02f8fa4226d62814592cf064ee4557c470 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Thu, 25 Jun 2026 16:38:53 +0100 Subject: mm: do file ownership checks with the proper mount idmap Ever since idmapped mounts were introduced, inode ownership checks (for side-channel protection) in mincore() and madvise(MADV_PAGEOUT) were done against the nop_mnt_idmap, which completely ignores the file's mount's idmap. This results in odd edgecases like: 1) mount/bind-mount with an idmap userA:userB:1 2) userB runs an owner_or_capable() check on file that is owned by userA on-disk/in-memory, but owned by userB after idmap translation 3) owner_or_capable() mysteriously fails as the correct idmap wasn't supplied In the case of mincore/madvise MADV_PAGEOUT, this is usually benign, because file_permission(file, MAY_WRITE) will probably succeed, as it uses the proper idmap internally, but it does not need to be the case on e.g a 0444 file where even the owner itself doesn't have permissions to write to it. Since this is clearly not trivial to get right, introduce a file_owner_or_capable() that can carry the correct semantics, and switch the various users in mm to it. The issue was found by manual code inspection & an off-list discussion with Jan Kara. Link: https://lore.kernel.org/20260625153853.913949-1-pfalcato@suse.de Fixes: 9caccd41541a ("fs: introduce MOUNT_ATTR_IDMAP") Signed-off-by: Pedro Falcato Reviewed-by: Jan Kara Reviewed-by: Christian Brauner (Amutable) Acked-by: David Hildenbrand (Arm) Cc: Al Viro Cc: Jann Horn Cc: Liam R. Howlett Cc: Matthew Wilcox (Oracle) Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- include/linux/fs.h | 5 +++++ mm/filemap.c | 2 +- mm/madvise.c | 3 +-- mm/mincore.c | 3 +-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index d10897b3a1e3..50ce731a2b78 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2444,6 +2444,11 @@ static inline struct mnt_idmap *file_mnt_idmap(const struct file *file) return mnt_idmap(file->f_path.mnt); } +static inline bool file_owner_or_capable(const struct file *file) +{ + return inode_owner_or_capable(file_mnt_idmap(file), file_inode(file)); +} + /** * is_idmapped_mnt - check whether a mount is mapped * @mnt: the mount to check diff --git a/mm/filemap.c b/mm/filemap.c index 5af62e6abca5..58eb9d240643 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -4704,7 +4704,7 @@ static inline bool can_do_cachestat(struct file *f) { if (f->f_mode & FMODE_WRITE) return true; - if (inode_owner_or_capable(file_mnt_idmap(f), file_inode(f))) + if (file_owner_or_capable(f)) return true; return file_permission(f, MAY_WRITE) == 0; } diff --git a/mm/madvise.c b/mm/madvise.c index cd9bb077072c..77552b03d318 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -336,8 +336,7 @@ static inline bool can_do_file_pageout(struct vm_area_struct *vma) * otherwise we'd be including shared non-exclusive mappings, which * opens a side channel. */ - return inode_owner_or_capable(&nop_mnt_idmap, - file_inode(vma->vm_file)) || + return file_owner_or_capable(vma->vm_file) || file_permission(vma->vm_file, MAY_WRITE) == 0; } diff --git a/mm/mincore.c b/mm/mincore.c index 296f2e3922b5..c8757c5085bf 100644 --- a/mm/mincore.c +++ b/mm/mincore.c @@ -227,8 +227,7 @@ static inline bool can_do_mincore(struct vm_area_struct *vma) * for writing; otherwise we'd be including shared non-exclusive * mappings, which opens a side channel. */ - return inode_owner_or_capable(&nop_mnt_idmap, - file_inode(vma->vm_file)) || + return file_owner_or_capable(vma->vm_file) || file_permission(vma->vm_file, MAY_WRITE) == 0; } -- cgit v1.2.3 From 74a21a2db6aecff5b01cf6b3a52144dd805d51ff Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 29 Jun 2026 10:21:23 +0800 Subject: tools/virtio: add missing compat definitions for vhost_net_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch series "tools: Fix tools/virtio test build", v2. This series fixes build failures hit by: make -C tools/virtio test Patch 1 adds tools/virtio compatibility definitions needed by current virtio headers when building the tools/virtio tests. Patch 2 makes tools/include/linux/overflow.h include stdint.h for SIZE_MAX, which is used by its size helper functions. With the series applied, make -C tools/virtio test builds virtio_test, vringh_test and vhost_net_test successfully. Tested on x86_64 and arm64 with: make -C tools/virtio clean make -C tools/virtio test This patch (of 2): vhost_net_test builds virtio_ring.c in userspace. Recent virtio headers pull in helper headers that are not provided by the tools/virtio compatibility layer, including asm/percpu_types.h, linux/completion.h, linux/mod_devicetable.h and linux/virtio_features.h. Add the missing compat definitions and the DMA attribute used by the current virtio ring code. Link: https://lore.kernel.org/20260629022124.131894-1-chenyichong@uniontech.com Link: https://lore.kernel.org/20260629022124.131894-2-chenyichong@uniontech.com Signed-off-by: Yichong Chen Acked-by: Eugenio Pérez Cc: chenyichong Cc: Jason Wang Cc: Lorenzo Stoakes Cc: "Michael S. Tsirkin" Cc: Mike Rapoport Cc: Paolo Abeni Cc: Xuan Zhuo Signed-off-by: Andrew Morton --- tools/virtio/asm/percpu_types.h | 7 ++++ tools/virtio/linux/completion.h | 9 ++++ tools/virtio/linux/device.h | 1 + tools/virtio/linux/dma-mapping.h | 1 + tools/virtio/linux/mod_devicetable.h | 14 +++++++ tools/virtio/linux/virtio_features.h | 79 ++++++++++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+) create mode 100644 tools/virtio/asm/percpu_types.h create mode 100644 tools/virtio/linux/completion.h create mode 100644 tools/virtio/linux/mod_devicetable.h create mode 100644 tools/virtio/linux/virtio_features.h diff --git a/tools/virtio/asm/percpu_types.h b/tools/virtio/asm/percpu_types.h new file mode 100644 index 000000000000..4eb53d93c099 --- /dev/null +++ b/tools/virtio/asm/percpu_types.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_PERCPU_TYPES_H +#define _ASM_PERCPU_TYPES_H + +#define __percpu_qual + +#endif /* _ASM_PERCPU_TYPES_H */ diff --git a/tools/virtio/linux/completion.h b/tools/virtio/linux/completion.h new file mode 100644 index 000000000000..5e54b679721b --- /dev/null +++ b/tools/virtio/linux/completion.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_COMPLETION_H +#define _LINUX_COMPLETION_H + +struct completion { + unsigned int done; +}; + +#endif /* _LINUX_COMPLETION_H */ diff --git a/tools/virtio/linux/device.h b/tools/virtio/linux/device.h index 075c2140d975..abf100cb0023 100644 --- a/tools/virtio/linux/device.h +++ b/tools/virtio/linux/device.h @@ -1,4 +1,5 @@ #ifndef LINUX_DEVICE_H +#define LINUX_DEVICE_H struct device { void *parent; diff --git a/tools/virtio/linux/dma-mapping.h b/tools/virtio/linux/dma-mapping.h index 8d1a16cb20db..b9fc5e8338e3 100644 --- a/tools/virtio/linux/dma-mapping.h +++ b/tools/virtio/linux/dma-mapping.h @@ -61,5 +61,6 @@ enum dma_data_direction { #define DMA_MAPPING_ERROR (~(dma_addr_t)0) #define DMA_ATTR_CPU_CACHE_CLEAN (1UL << 11) +#define DMA_ATTR_DEBUGGING_IGNORE_CACHELINES 0 #endif diff --git a/tools/virtio/linux/mod_devicetable.h b/tools/virtio/linux/mod_devicetable.h new file mode 100644 index 000000000000..3ba594b8229d --- /dev/null +++ b/tools/virtio/linux/mod_devicetable.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_MOD_DEVICETABLE_H +#define _LINUX_MOD_DEVICETABLE_H + +#include + +struct virtio_device_id { + __u32 device; + __u32 vendor; +}; + +#define VIRTIO_DEV_ANY_ID 0xffffffff + +#endif /* _LINUX_MOD_DEVICETABLE_H */ diff --git a/tools/virtio/linux/virtio_features.h b/tools/virtio/linux/virtio_features.h new file mode 100644 index 000000000000..04cbb9622ec7 --- /dev/null +++ b/tools/virtio/linux/virtio_features.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_VIRTIO_FEATURES_H +#define _LINUX_VIRTIO_FEATURES_H + +#include +#include +#include + +#define VIRTIO_FEATURES_U64S 2 +#define VIRTIO_FEATURES_BITS (VIRTIO_FEATURES_U64S * 64) + +#define VIRTIO_BIT(b) (1ULL << ((b) & 0x3f)) +#define VIRTIO_U64(b) ((b) >> 6) + +#define VIRTIO_DECLARE_FEATURES(name) \ + union { \ + u64 name; \ + u64 name##_array[VIRTIO_FEATURES_U64S];\ + } + +static inline bool virtio_features_chk_bit(unsigned int bit) +{ + return bit < VIRTIO_FEATURES_BITS; +} + +static inline bool virtio_features_test_bit(const u64 *features, + unsigned int bit) +{ + return virtio_features_chk_bit(bit) && + !!(features[VIRTIO_U64(bit)] & VIRTIO_BIT(bit)); +} + +static inline void virtio_features_set_bit(u64 *features, unsigned int bit) +{ + if (virtio_features_chk_bit(bit)) + features[VIRTIO_U64(bit)] |= VIRTIO_BIT(bit); +} + +static inline void virtio_features_clear_bit(u64 *features, unsigned int bit) +{ + if (virtio_features_chk_bit(bit)) + features[VIRTIO_U64(bit)] &= ~VIRTIO_BIT(bit); +} + +static inline void virtio_features_zero(u64 *features) +{ + memset(features, 0, sizeof(features[0]) * VIRTIO_FEATURES_U64S); +} + +static inline void virtio_features_from_u64(u64 *features, u64 from) +{ + virtio_features_zero(features); + features[0] = from; +} + +static inline bool virtio_features_equal(const u64 *f1, const u64 *f2) +{ + int i; + + for (i = 0; i < VIRTIO_FEATURES_U64S; ++i) + if (f1[i] != f2[i]) + return false; + return true; +} + +static inline void virtio_features_copy(u64 *to, const u64 *from) +{ + memcpy(to, from, sizeof(to[0]) * VIRTIO_FEATURES_U64S); +} + +static inline void virtio_features_andnot(u64 *to, const u64 *f1, const u64 *f2) +{ + int i; + + for (i = 0; i < VIRTIO_FEATURES_U64S; i++) + to[i] = f1[i] & ~f2[i]; +} + +#endif /* _LINUX_VIRTIO_FEATURES_H */ -- cgit v1.2.3 From 039892c35f9d8f5ea00d7c2ed1c25224f28b11d7 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 29 Jun 2026 10:21:24 +0800 Subject: tools/include: include stdint.h for SIZE_MAX in overflow.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tools/include/linux/overflow.h uses SIZE_MAX in its size helper functions. Include stdint.h so tools users that include overflow.h without another SIZE_MAX provider can build. Link: https://lore.kernel.org/20260629022124.131894-3-chenyichong@uniontech.com Signed-off-by: Yichong Chen Acked-by: Eugenio Pérez Cc: Jason Wang Cc: Lorenzo Stoakes Cc: "Michael S. Tsirkin" Cc: Mike Rapoport Cc: Paolo Abeni Cc: Xuan Zhuo Signed-off-by: Andrew Morton --- tools/include/linux/overflow.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/include/linux/overflow.h b/tools/include/linux/overflow.h index 3427d7880326..98963688143f 100644 --- a/tools/include/linux/overflow.h +++ b/tools/include/linux/overflow.h @@ -1,4 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ +#include #ifndef __LINUX_OVERFLOW_H #define __LINUX_OVERFLOW_H -- cgit v1.2.3 From c283e9ada7fcb7dd4b10592623086b2e6d2f9925 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Sat, 27 Jun 2026 02:40:23 +0000 Subject: 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; } -- cgit v1.2.3 From c4a5f0071cc6d378a0e7151b60d85986aefac1f3 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 11:50:58 +0800 Subject: 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 @@ found: 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)); -- cgit v1.2.3 From ea528f18231ec0f33317be57f8866913b19aba6e Mon Sep 17 00:00:00 2001 From: Antony Antony Date: Sat, 27 Jun 2026 10:23:43 +0200 Subject: 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"); -- cgit v1.2.3 From 6666336d8fe5f3f0cd3f8da2865ca78110a6e170 Mon Sep 17 00:00:00 2001 From: Jia He Date: Tue, 30 Jun 2026 22:23:47 +0000 Subject: 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 -- cgit v1.2.3 From a52d6c7160f7e2f8c56adf29146385b8f2868d3d Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Thu, 2 Jul 2026 09:52:42 +0800 Subject: 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) { -- cgit v1.2.3 From 18313833e2c6de222a4f6c072da759d0d5888528 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 17 Jun 2026 14:21:05 +0300 Subject: 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)) -- cgit v1.2.3 From 49df66b7993c80b80c7eb9a84ba5b3410c8296a0 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 11:46:21 +0200 Subject: batman-adv: ensure minimal ethernet header on TX As documented in commit 8bd67ebb50c0 ("net: bridge: xmit: make sure we have at least eth header len bytes"), it is possible by for a local user with eBPF TC hook access to attach a tc filter which truncates the packet and redirects to an batadv interface. But the code assumes that at least ETH_HLEN bytes are available and thus might read outside of the available buffer. The batadv_interface_tx() must therefore always check itself if enough data is available for the ethernet header and don't rely on min_header_len. Cc: stable@vger.kernel.org Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol") Reported-by: Sashiko Signed-off-by: Sven Eckelmann --- net/batman-adv/mesh-interface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index 511f70e0706a..0b75234521b6 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -195,6 +195,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; + if (!pskb_may_pull(skb, ETH_HLEN)) + goto dropped; + /* reset control block to avoid left overs from previous users */ memset(skb->cb, 0, sizeof(struct batadv_skb_cb)); -- cgit v1.2.3 From a846aecb931b4d65d5eafa92a0623545af46d4f2 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 14:19:36 +0000 Subject: 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); -- cgit v1.2.3 From f01f5275feb77bac9fefbbf7cc584fe0b3850a92 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 23:18:27 +0200 Subject: 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; } -- cgit v1.2.3 From 2b37415618bfc6a83d4aceb00fd8d6491096f2ed Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 4 May 2026 19:44:09 -0700 Subject: watchdog: s32g_wdt: remove incorrect options in watchdog_info struct The s32g_wdt driver uses two incorrect constants in the options field of its watchdog_info struct. This bit mask should contain WDIOF_* constants, but the driver uses two WDIOC_* ioctl constants (in addition to correct WDIOF_* constants). This causes many incorrect bits to be set in the bit mask. The functionality indicated by these ioctl constants is supported by all drivers using the watchdog framework, so this patch simply removes them. Fixes: bd3f54ec559b ("watchdog: Add the Watchdog Timer for the NXP S32 platform") Cc: stable@vger.kernel.org # 6.18+ Signed-off-by: Ethan Nelson-Moore Acked-by: Daniel Lezcano Link: https://lore.kernel.org/r/20260505024409.60301-1-enelsonmoore@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/s32g_wdt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/watchdog/s32g_wdt.c b/drivers/watchdog/s32g_wdt.c index ad55063060af..6422a694fc65 100644 --- a/drivers/watchdog/s32g_wdt.c +++ b/drivers/watchdog/s32g_wdt.c @@ -56,8 +56,7 @@ MODULE_PARM_DESC(early_enable, static const struct watchdog_info s32g_wdt_info = { .identity = "s32g watchdog", - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | - WDIOC_GETTIMEOUT | WDIOC_GETTIMELEFT, + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; static struct s32g_wdt_device *wdd_to_s32g_wdt(struct watchdog_device *wdd) -- cgit v1.2.3 From 36e05e134ee44f9fbfcebcbcdadb5f765fccd9f0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:22:57 +0200 Subject: watchdog: ni903x_wdt: Check ACPI_COMPANION() against NULL Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the ni903x_wdt watchdog driver. Fixes: d37ec2fbab55 ("watchdog: ni903x_wdt: Convert to a platform driver") Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/2280455.irdbgypaU6@rafael.j.wysocki Signed-off-by: Guenter Roeck --- drivers/watchdog/ni903x_wdt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/ni903x_wdt.c b/drivers/watchdog/ni903x_wdt.c index 8b1b9baa914e..c72a9ee9cb8e 100644 --- a/drivers/watchdog/ni903x_wdt.c +++ b/drivers/watchdog/ni903x_wdt.c @@ -183,9 +183,14 @@ static int ni903x_acpi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct watchdog_device *wdd; struct ni903x_wdt *wdt; + acpi_handle handle; acpi_status status; int ret; + handle = ACPI_HANDLE(dev); + if (!handle) + return -ENODEV; + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) return -ENOMEM; @@ -193,7 +198,7 @@ static int ni903x_acpi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdt); wdt->dev = dev; - status = acpi_walk_resources(ACPI_HANDLE(dev), METHOD_NAME__CRS, + status = acpi_walk_resources(handle, METHOD_NAME__CRS, ni903x_resources, wdt); if (ACPI_FAILURE(status) || wdt->io_base == 0) { dev_err(dev, "failed to get resources\n"); -- cgit v1.2.3 From 0be186a120a797edb28effb9359296ce4cde9a25 Mon Sep 17 00:00:00 2001 From: Manuel Ebner Date: Sat, 27 Jun 2026 11:17:08 +0200 Subject: docs: watchdog: Fix brackets Add missing brackets ')'. Signed-off-by: Manuel Ebner Reviewed-by: Randy Dunlap Link: https://lore.kernel.org/r/20260627091707.29688-2-manuelebner@mailbox.org Signed-off-by: Guenter Roeck --- Documentation/watchdog/watchdog-parameters.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/watchdog/watchdog-parameters.rst b/Documentation/watchdog/watchdog-parameters.rst index ec5f67c060cb..502cb6adbeda 100644 --- a/Documentation/watchdog/watchdog-parameters.rst +++ b/Documentation/watchdog/watchdog-parameters.rst @@ -59,7 +59,7 @@ advantechwdt: alim1535_wdt: timeout: - Watchdog timeout in seconds. (0 < timeout < 18000, default=60 + Watchdog timeout in seconds. (0 < timeout < 18000, default=60) nowayout: Watchdog cannot be stopped once started (default=kernel config parameter) @@ -68,7 +68,7 @@ alim1535_wdt: alim7101_wdt: timeout: - Watchdog timeout in seconds. (1<=timeout<=3600, default=30 + Watchdog timeout in seconds. (1<=timeout<=3600, default=30) use_gpio: Use the gpio watchdog (required by old cobalt boards). default=0/off/no -- cgit v1.2.3 From ec2d428b2e32dd157de8f86a86dd85c5b2c8f45c Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 15 Apr 2026 16:54:52 -0400 Subject: dmaengine: idxd: fix double free of wq, engine, and group structs The release callbacks for wq, engine, and group devices (idxd_conf_wq_release, idxd_conf_engine_release, idxd_conf_group_release) each call kfree() on the enclosing struct. The setup error paths and cleanup functions also call kfree() explicitly after put_device(), producing a double free whenever put_device() drops the reference count to zero and fires the release. In the setup functions, device_initialize() is called before device_add(), so the reference count is exactly 1 at the error sites. put_device() unconditionally fires the release, which frees the struct; the subsequent explicit kfree() then operates on freed memory. For idxd_setup_wqs(), the wq release callback also owns opcap_bmap and wqcfg. The error unwind additionally freed those fields explicitly before calling put_device(), causing further double frees on both. Remove the redundant explicit kfree() calls from all setup error paths and cleanup functions for wq, engine, and group structs, delegating sole ownership of those allocations to the release callbacks. Fixes: 7c5dd23e57c1 ("dmaengine: idxd: fix wq conf_dev 'struct device' lifetime") Fixes: 75b911309060 ("dmaengine: idxd: fix engine conf_dev lifetime") Fixes: defe49f96012 ("dmaengine: idxd: fix group conf_dev lifetime") Signed-off-by: Yuho Choi Acked-by: Vinicius Costa Gomes Reviewed-by: Frank Li Link: https://patch.msgid.link/20260415205452.67155-1-dbgh9129@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/idxd/init.c | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c index f1cfc7790d95..4b827a329756 100644 --- a/drivers/dma/idxd/init.c +++ b/drivers/dma/idxd/init.c @@ -159,18 +159,12 @@ static void idxd_cleanup_interrupts(struct idxd_device *idxd) static void idxd_clean_wqs(struct idxd_device *idxd) { - struct idxd_wq *wq; struct device *conf_dev; int i; for (i = 0; i < idxd->max_wqs; i++) { - wq = idxd->wqs[i]; - if (idxd->hw.wq_cap.op_config) - bitmap_free(wq->opcap_bmap); - kfree(wq->wqcfg); - conf_dev = wq_confdev(wq); + conf_dev = wq_confdev(idxd->wqs[i]); put_device(conf_dev); - kfree(wq); } bitmap_free(idxd->wq_enable_map); kfree(idxd->wqs); @@ -212,7 +206,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd) rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id); if (rc < 0) { put_device(conf_dev); - kfree(wq); goto err_unwind; } @@ -227,7 +220,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd) wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev)); if (!wq->wqcfg) { put_device(conf_dev); - kfree(wq); rc = -ENOMEM; goto err_unwind; } @@ -235,9 +227,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd) if (idxd->hw.wq_cap.op_config) { wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL); if (!wq->opcap_bmap) { - kfree(wq->wqcfg); put_device(conf_dev); - kfree(wq); rc = -ENOMEM; goto err_unwind; } @@ -252,13 +242,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd) err_unwind: while (--i >= 0) { - wq = idxd->wqs[i]; - if (idxd->hw.wq_cap.op_config) - bitmap_free(wq->opcap_bmap); - kfree(wq->wqcfg); - conf_dev = wq_confdev(wq); + conf_dev = wq_confdev(idxd->wqs[i]); put_device(conf_dev); - kfree(wq); } bitmap_free(idxd->wq_enable_map); @@ -270,15 +255,12 @@ err_free_wqs: static void idxd_clean_engines(struct idxd_device *idxd) { - struct idxd_engine *engine; struct device *conf_dev; int i; for (i = 0; i < idxd->max_engines; i++) { - engine = idxd->engines[i]; - conf_dev = engine_confdev(engine); + conf_dev = engine_confdev(idxd->engines[i]); put_device(conf_dev); - kfree(engine); } kfree(idxd->engines); } @@ -313,7 +295,6 @@ static int idxd_setup_engines(struct idxd_device *idxd) rc = dev_set_name(conf_dev, "engine%d.%d", idxd->id, engine->id); if (rc < 0) { put_device(conf_dev); - kfree(engine); goto err; } @@ -324,10 +305,8 @@ static int idxd_setup_engines(struct idxd_device *idxd) err: while (--i >= 0) { - engine = idxd->engines[i]; - conf_dev = engine_confdev(engine); + conf_dev = engine_confdev(idxd->engines[i]); put_device(conf_dev); - kfree(engine); } kfree(idxd->engines); @@ -336,13 +315,10 @@ static int idxd_setup_engines(struct idxd_device *idxd) static void idxd_clean_groups(struct idxd_device *idxd) { - struct idxd_group *group; int i; for (i = 0; i < idxd->max_groups; i++) { - group = idxd->groups[i]; - put_device(group_confdev(group)); - kfree(group); + put_device(group_confdev(idxd->groups[i])); } kfree(idxd->groups); } @@ -377,7 +353,6 @@ static int idxd_setup_groups(struct idxd_device *idxd) rc = dev_set_name(conf_dev, "group%d.%d", idxd->id, group->id); if (rc < 0) { put_device(conf_dev); - kfree(group); goto err; } @@ -402,7 +377,6 @@ static int idxd_setup_groups(struct idxd_device *idxd) while (--i >= 0) { group = idxd->groups[i]; put_device(group_confdev(group)); - kfree(group); } kfree(idxd->groups); -- cgit v1.2.3 From ee1d7274102285d78a53161fc705a8d8cd40b066 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 25 May 2026 10:15:50 -0400 Subject: dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open() The failed_dev_add and failed_dev_name paths drop the file-device reference while wq->wq_lock is still held. If put_device(fdev) drops the last reference, idxd_file_dev_release() runs synchronously and tries to take wq->wq_lock again, deadlocking. Those paths also fall through into the later ctx cleanup labels even though idxd_file_dev_release() owns that cleanup and frees ctx. This can make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context. Move idxd_wq_get() before file-device setup can fail, since the release callback always calls idxd_wq_put(). Then unlock wq->wq_lock before put_device(fdev) and return directly from the file-device setup failure path, leaving ctx cleanup to the release callback. Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened") Signed-off-by: Yuho Choi Reviewed-by: Dave Jiang Acked-by: Vinicius Costa Gomes Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/idxd/cdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c index 0366c7cf3502..82b07cf942ef 100644 --- a/drivers/dma/idxd/cdev.c +++ b/drivers/dma/idxd/cdev.c @@ -288,6 +288,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) fdev->parent = cdev_dev(idxd_cdev); fdev->bus = &dsa_bus_type; fdev->type = &idxd_cdev_file_type; + idxd_wq_get(wq); rc = dev_set_name(fdev, "file%d", ctx->id); if (rc < 0) { @@ -301,13 +302,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) goto failed_dev_add; } - idxd_wq_get(wq); mutex_unlock(&wq->wq_lock); return 0; failed_dev_add: failed_dev_name: + mutex_unlock(&wq->wq_lock); put_device(fdev); + return rc; failed_ida: failed_set_pasid: if (device_user_pasid_enabled(idxd)) -- cgit v1.2.3 From 867621ba203027338b525af6729719c544135336 Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Mon, 15 Jun 2026 11:39:08 +0530 Subject: dmaengine: qcom: bam_dma: Fix command element mask field for BAM v1.6.0+ BAM version 1.6.0 and later changed the behavior of the mask field in command elements for read operations. In older BAM versions, or prior implementation assumptions, the mask field was effectively ignored for read commands. However, starting from BAM v1.6.0, the mask field for read commands is repurposed to carry the upper 4 bits of the destination address, enabling support for 36-bit addressing. For write commands, the mask field continues to function as a traditional write mask. The current driver sets mask = 0xffffffff for all command elements. While this works for write operations, it breaks read operations on BAM v1.6.0+ hardware. In such cases, the hardware interprets the upper address bits as 0xf, resulting in an invalid destination address (0xf_xxxxxxxx instead of 0x0_xxxxxxxx). This leads to failures such as NAND enumeration issues observed on platforms like IPQ5424. Fix this by assigning the mask field based on command type: - For read commands: set mask = 0 (upper address bits = 0) - For write commands: retain mask = 0xffffffff Also update the bam_cmd_element structure documentation to reflect the dual purpose of the mask field across BAM versions. This ensures correct behavior on BAM v1.6.0+ while maintaining backward compatibility with older hardware. Fixes: dfebb055f73a2 ("dmaengine: qcom: bam_dma: wrapper functions for command descriptor") Tested-by: Lakshmi Sowjanya D Signed-off-by: Md Sadre Alam Reviewed-by: Frank Li Reviewed-by: Dmitry Baryshkov Cc: stable@vger.kernel.org Signed-off-by: Varadarajan Narayanan Link: https://patch.msgid.link/20260615060908.1263171-1-varadarajan.narayanan@oss.qualcomm.com Signed-off-by: Vinod Koul --- include/linux/dma/qcom_bam_dma.h | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/include/linux/dma/qcom_bam_dma.h b/include/linux/dma/qcom_bam_dma.h index 68fc0e643b1b..d9d07a9ab313 100644 --- a/include/linux/dma/qcom_bam_dma.h +++ b/include/linux/dma/qcom_bam_dma.h @@ -13,9 +13,12 @@ * supported by BAM DMA Engine. * * @cmd_and_addr - upper 8 bits command and lower 24 bits register address. - * @data - for write command: content to be written into peripheral register. - * for read command: dest addr to write peripheral register value. - * @mask - register mask. + * @data - For write command: content to be written into peripheral register. + * For read command: lower 32 bits of destination address. + * @mask - For write command: register write mask. + * For read command on BAM v1.6.0+: upper 4 bits of destination address. + * For read command on BAM < v1.6.0: ignored by hardware. + * Setting to 0 ensures 32-bit addressing compatibility. * @reserved - for future usage. * */ @@ -42,6 +45,10 @@ enum bam_command_type { * @addr: target address * @cmd: BAM command * @data: actual data for write and dest addr for read in le32 + * + * For BAM v1.6.0+, the mask field behavior depends on command type: + * - Write commands: mask = write mask (typically 0xffffffff) + * - Read commands: mask = upper 4 bits of destination address (0 for 32-bit) */ static inline void bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, @@ -50,7 +57,11 @@ bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, bam_ce->cmd_and_addr = cpu_to_le32((addr & 0xffffff) | ((cmd & 0xff) << 24)); bam_ce->data = data; - bam_ce->mask = cpu_to_le32(0xffffffff); + if (cmd == BAM_READ_COMMAND) + bam_ce->mask = cpu_to_le32(0x0); /* 32-bit addressing */ + else + bam_ce->mask = cpu_to_le32(0xffffffff); /* Write mask */ + bam_ce->reserved = 0; } /* @@ -60,7 +71,7 @@ bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, * @bam_ce: BAM command element * @addr: target address * @cmd: BAM command - * @data: actual data for write and dest addr for read + * @data: actual data for write and destination address for read */ static inline void bam_prep_ce(struct bam_cmd_element *bam_ce, u32 addr, -- cgit v1.2.3 From b9b0889071569d43623c260074e159cd8f26adb1 Mon Sep 17 00:00:00 2001 From: Jacob Moroni Date: Wed, 17 Jun 2026 16:40:13 +0000 Subject: 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) { -- cgit v1.2.3 From f107c62bfc057b82758c233391ee0842f02a0582 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 1 Jul 2026 21:21:46 +0200 Subject: 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 -- cgit v1.2.3 From fd354554af1d2b33232ca6c8a3d79ed82413d715 Mon Sep 17 00:00:00 2001 From: Joshua Crofts Date: Wed, 1 Jul 2026 21:21:47 +0200 Subject: 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 -- cgit v1.2.3 From 2815a277c53e9a84784d6410cd55a9da5b33068d Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 12:17:51 +0800 Subject: 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); -- cgit v1.2.3 From 155fd5ce2382b0ffbec0d7ee7b3a6818a27a5aed Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 12:17:52 +0800 Subject: 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) { -- cgit v1.2.3 From e939334ea7dd219f100f963dbb1cb43df520c20a Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 25 Jun 2026 10:01:48 +0800 Subject: 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; -- cgit v1.2.3 From 9f0f2d2121f16d420199a82ac5bbc242269133b3 Mon Sep 17 00:00:00 2001 From: Danila Chernetsov Date: Sat, 27 Jun 2026 09:59:51 +0000 Subject: 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); } -- cgit v1.2.3 From 63bbf9ac5dde2ba85e7b39d0a0b7d540e6252ba4 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 25 Jun 2026 19:32:39 +0800 Subject: 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; -- cgit v1.2.3 From bb27fcc67c429d97f785c92c35a6c5adebb05d7f Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 30 Jun 2026 14:00:40 +0800 Subject: 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); -- cgit v1.2.3 From fdb3be00ba4dafa313e699d6b5b90d13f22f3f25 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 20:45:24 +0200 Subject: batman-adv: fix VLAN priority offset The batadv_skb_set_priority() receives an SKB with the inner ethernet header at position "offset". When it tries to extract the IPv4 and IPv6 header, it needs to skip the ethernet header to get access to the IP header. But for VLAN header, it performs the access with the struct vlan_ethhdr. This struct contains both both the ethernet header and the VLAN header. It is therefore incorrect to skip over the whole vlan_ethhdr size to get access to the vlan_ethhdr. Cc: stable@vger.kernel.org Fixes: c54f38c9aa22 ("batman-adv: set skb priority according to content") Signed-off-by: Sven Eckelmann --- net/batman-adv/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 4d3807a645b7..8844e40e6a80 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -368,7 +368,7 @@ void batadv_skb_set_priority(struct sk_buff *skb, int offset) switch (ethhdr->h_proto) { case htons(ETH_P_8021Q): - vhdr = skb_header_pointer(skb, offset + sizeof(*vhdr), + vhdr = skb_header_pointer(skb, offset, sizeof(*vhdr), &vhdr_tmp); if (!vhdr) return; -- cgit v1.2.3 From 58481c749c976e81bef9a540e2225ddb021daaa6 Mon Sep 17 00:00:00 2001 From: Yang Xiuwei Date: Thu, 2 Jul 2026 16:29:37 +0800 Subject: 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)) -- cgit v1.2.3 From dad9f96945d77ecd4708f730c06ef54dcd8cc057 Mon Sep 17 00:00:00 2001 From: Cheng Yongkang Date: Fri, 5 Jun 2026 08:32:10 -0700 Subject: wifi: ath9k: hif_usb: don't dereference hif_dev after re-arming firmware request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ath9k_hif_request_firmware() re-arms an asynchronous firmware load via request_firmware_nowait(), passing hif_dev as the completion context, and then still dereferences hif_dev: dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", hif_dev->fw_name); The re-armed callback ath9k_hif_usb_firmware_cb() runs on the "events" workqueue and, when the firmware is missing, walks the retry chain into ath9k_hif_usb_firmware_fail() -> complete_all(&hif_dev->fw_done). That releases the wait_for_completion(&hif_dev->fw_done) in a concurrent ath9k_hif_usb_disconnect(), which then kfree()s hif_dev. The trailing dev_info() in the frame that re-armed the request can therefore read freed memory (hif_dev->udev, the first field of struct hif_device_usb): BUG: KASAN: slab-use-after-free in ath9k_hif_request_firmware Read of size 8 ... by task kworker/... ath9k_hif_request_firmware ath9k_hif_usb_firmware_cb drivers/net/wireless/ath/ath9k/hif_usb.c:1247 request_firmware_work_func Allocated by ...: ath9k_hif_usb_probe drivers/net/wireless/ath/ath9k/hif_usb.c Freed by ...: ath9k_hif_usb_disconnect -> kfree drivers/net/wireless/ath/ath9k/hif_usb.c The fw_done barrier only makes disconnect wait for the firmware chain to *terminate*; it does not protect the outer ath9k_hif_request_firmware() frame that re-armed the request and keeps touching hif_dev afterwards. Drop the post-request dev_info(): it is the only use of hif_dev after the async request is armed, and it is purely informational (the dev_err() on the failure path runs only when request_firmware_nowait() did not arm a callback, so hif_dev is still alive there). This was first reported by syzbot as a single, non-reproduced crash that was later auto-obsoleted, and was independently rediscovered by the reFuzz fuzzer, which produced a C reproducer (USB-gadget connect/disconnect of an ath9k_htc device whose firmware download fails). The vulnerable code is unchanged and still present in v7.1-rc6, where the slab-use-after-free reproduces under KASAN once the (sub-microsecond) race window is widened. Fixes: e904cf6fe230 ("ath9k_htc: introduce support for different fw versions") Reported-by: syzbot+50122cbc2874b1eb25b0@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=50122cbc2874b1eb25b0 Signed-off-by: Cheng Yongkang Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260605153210.20471-1-1020691186@qq.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath9k/hif_usb.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 515267f48d80..47f904e7e652 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1215,15 +1215,10 @@ static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev, ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, &hif_dev->udev->dev, GFP_KERNEL, hif_dev, ath9k_hif_usb_firmware_cb); - if (ret) { + if (ret) dev_err(&hif_dev->udev->dev, "ath9k_htc: Async request for firmware %s failed\n", hif_dev->fw_name); - return ret; - } - - dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", - hif_dev->fw_name); return ret; } -- cgit v1.2.3 From ba7debb4dd6427386862220e8335a53a4bfc235d Mon Sep 17 00:00:00 2001 From: Daniel Hodges Date: Fri, 6 Feb 2026 13:52:07 -0500 Subject: wifi: ath6kl: fix use-after-free in aggr_reset_state() The aggr_reset_state() function uses timer_delete() (non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers like aggr_module_destroy(). If the timer callback (aggr_timeout) is executing when aggr_reset_state() is called, the callback will continue to access aggr_conn fields like rx_tid[] and stat[] which may be freed immediately after by kfree(aggr_info->aggr_conn) in aggr_module_destroy(). Additionally, the timer callback can re-arm itself via mod_timer() while aggr_reset_state() is running, creating a more complex race condition. Use timer_delete_sync() instead to ensure any running timer callback has completed before returning. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Cc: stable@vger.kernel.org Signed-off-by: Daniel Hodges Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260206185207.30098-1-git@danielhodges.dev Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 97fdac7237e2..0e268017af52 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn) return; if (aggr_conn->timer_scheduled) { - timer_delete(&aggr_conn->timer); + timer_delete_sync(&aggr_conn->timer); aggr_conn->timer_scheduled = false; } -- cgit v1.2.3 From e8d85672dd7e2523f774caafba8f858384e18df7 Mon Sep 17 00:00:00 2001 From: Gaole Zhang Date: Tue, 9 Jun 2026 17:06:09 +0800 Subject: wifi: ath11k: fix NULL pointer dereference in ath11k_hal_srng_access_begin In ATH11K_QMI_EVENT_FW_READY, ATH11K_FLAG_REGISTERED is set unconditionally even when ath11k_core_qmi_firmware_ready() fails. This leaves the driver in an inconsistent state where initialization is considered complete although the firmware ready handling did not finish successfully. During the subsequent SSR, the driver enters the restart path based on this incorrect state and dereferences uninitialized srng members, resulting in a NULL pointer dereference. Call trace: ath11k_hal_srng_access_begin+0xc/0x60 [ath11k] (P) ath11k_ce_cleanup_pipes+0x17c/0x180 [ath11k] ath11k_core_restart+0x40/0x168 [ath11k] Fix this by: - skipping firmware_ready if ATH11K_FLAG_REGISTERED is already set - setting ATH11K_FLAG_REGISTERED only when firmware_ready succeeds - setting ATH11K_FLAG_QMI_FAIL and aborting the FW_READY handling on error Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.2.0.c2-00204-QCAMSLSWPLZ-1 Fixes: 6fe62a8cec51c ("wifi: ath11k: Add cold boot calibration support on WCN6750") Signed-off-by: Gaole Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260609090609.4041009-1-gaole.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/qmi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 410a7ee076a0..7dc07339b957 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -3294,9 +3294,14 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); - ath11k_core_qmi_firmware_ready(ab); - set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); - + if (!test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { + ret = ath11k_core_qmi_firmware_ready(ab); + if (ret) { + set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags); + break; + } + set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags); + } break; case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE: break; -- cgit v1.2.3 From 738f24bbbc95dd50cb4229d1ed62a05f29db2bda Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 22:47:34 -0700 Subject: 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: -- cgit v1.2.3 From fe938ee497d58c644f6910cfe6ae155f6fb3e523 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 22:49:15 -0700 Subject: 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; -- cgit v1.2.3 From 536394ec81419b67d9f4f0028812c4372397be1b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 29 Jun 2026 18:44:41 -0700 Subject: 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: -- cgit v1.2.3 From 03f384bc0cb8d4a1301d4f5b0baef2d980258383 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 29 Jun 2026 21:51:21 -0700 Subject: net: usb: net1080: validate packet_len before pad-byte access in rx_fixup For an even packet_len, net1080_rx_fixup() reads the pad byte at skb->data[packet_len] before the skb->len != packet_len check further down, and packet_len is only bounded against NC_MAX_PACKET. A malicious NetChip 1080 device can send a short frame advertising a large even packet_len (e.g. 0x4000), so the pad-byte read lands past the end of the skb: BUG: KASAN: slab-out-of-bounds in net1080_rx_fixup Read of size 1 at addr ffff8880106c83c6 by task ksoftirqd/0/14 ... net1080_rx_fixup (drivers/net/usb/net1080.c:384) usbnet_bh (drivers/net/usb/usbnet.c:1589) process_one_work (kernel/workqueue.c:3322) bh_worker (kernel/workqueue.c:3708) tasklet_action (kernel/softirq.c:965) handle_softirqs (kernel/softirq.c:622) ... Reject the frame when packet_len >= skb->len before reading. Fixes: 904813cd8a0b ("[PATCH] USB: usbnet (4/9) module for net1080 cables") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260630045121.1565324-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- drivers/net/usb/net1080.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c index 5d4a1fd2b524..19f6e1222d93 100644 --- a/drivers/net/usb/net1080.c +++ b/drivers/net/usb/net1080.c @@ -381,7 +381,7 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_trim(skb, skb->len - sizeof *trailer); if ((packet_len & 0x01) == 0) { - if (skb->data [packet_len] != PAD_BYTE) { + if (packet_len >= skb->len || skb->data[packet_len] != PAD_BYTE) { dev->net->stats.rx_frame_errors++; netdev_dbg(dev->net, "bad pad\n"); return 0; -- cgit v1.2.3 From 62e7df6d042aeebd5efb581074e28865c04477be Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 30 Jun 2026 15:16:25 +0800 Subject: octeontx2-pf: fix SQB pointer leak on init failure otx2_init_hw_resources() initializes SQ aura and pool resources before several later setup steps. On failure, err_free_sq_ptrs only frees SQB pages, leaving the per-SQ sqb_ptrs arrays behind. Use otx2_free_sq_res() for the SQ unwind path and let it free sqb_ptrs even when sq->sqe has not been allocated yet. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1. An x86_64 allyesconfig build showed no new warnings. As we do not have an OcteonTX2 PF device and the corresponding AF mailbox setup to test with, no runtime testing was able to be performed. Fixes: caa2da34fd25 ("octeontx2-pf: Initialize and config queues") Cc: stable@vger.kernel.org Reviewed-by: Ratheesh Kannoth Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260630071625.349996-1-dawei.feng@seu.edu.cn Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index b63df5737ff2..88ac85354445 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1568,15 +1568,15 @@ static void otx2_free_sq_res(struct otx2_nic *pf) otx2_sq_free_sqbs(pf); for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) { sq = &qset->sq[qidx]; - /* Skip freeing Qos queues if they are not initialized */ - if (!sq->sqe) - continue; - qmem_free(pf->dev, sq->sqe); - qmem_free(pf->dev, sq->sqe_ring); - qmem_free(pf->dev, sq->cpt_resp); - qmem_free(pf->dev, sq->tso_hdrs); - qmem_free(pf->dev, sq->timestamps); - kfree(sq->sg); + /* sq->sqe is not initialized for unused QoS queues */ + if (sq->sqe) { + qmem_free(pf->dev, sq->sqe); + qmem_free(pf->dev, sq->sqe_ring); + qmem_free(pf->dev, sq->cpt_resp); + qmem_free(pf->dev, sq->tso_hdrs); + qmem_free(pf->dev, sq->timestamps); + kfree(sq->sg); + } kfree(sq->sqb_ptrs); } } @@ -1711,13 +1711,12 @@ int otx2_init_hw_resources(struct otx2_nic *pf) return err; err_free_nix_queues: - otx2_free_sq_res(pf); otx2_free_cq_res(pf); otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); err_free_txsch: otx2_txschq_stop(pf); err_free_sq_ptrs: - otx2_sq_free_sqbs(pf); + otx2_free_sq_res(pf); err_free_rq_ptrs: otx2_free_aura_ptr(pf, AURA_NIX_RQ); otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true); -- cgit v1.2.3 From d335dcc6f521571d57117b8deeebc940836e5450 Mon Sep 17 00:00:00 2001 From: Qihang Date: Wed, 1 Jul 2026 10:26:17 +0800 Subject: gue: validate REMCSUM private option length GUE private flags can indicate that remote checksum offload metadata is present. The private flags field itself is accounted for by guehdr_flags_len(), but guehdr_priv_flags_len() currently returns 0 even when GUE_PFLAG_REMCSUM is set. This lets a packet with only the private flags field pass validate_gue_flags(), after which gue_remcsum() and gue_gro_remcsum() read the missing REMCSUM start/offset fields from the following bytes. Account for GUE_PLEN_REMCSUM when GUE_PFLAG_REMCSUM is present so that malformed packets are rejected during option validation. Fixes: c1aa8347e73e ("gue: Protocol constants for remote checksum offload") Signed-off-by: Qihang Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/gue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/gue.h b/include/net/gue.h index dfca298bec9c..caefd6da8693 100644 --- a/include/net/gue.h +++ b/include/net/gue.h @@ -80,7 +80,7 @@ static inline size_t guehdr_flags_len(__be16 flags) static inline size_t guehdr_priv_flags_len(__be32 flags) { - return 0; + return (flags & GUE_PFLAG_REMCSUM) ? GUE_PLEN_REMCSUM : 0; } /* Validate standard and private flags. Returns non-zero (meaning invalid) -- cgit v1.2.3 From 77bfebf110773f5a0d6b5ff8110896adb2c9c335 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 5 Jun 2026 11:13:50 +0000 Subject: 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 @@ impl Thread { 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 @@ impl Thread { 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 @@ impl Thread { &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 @@ impl Thread { &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 @@ impl Thread { 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 @@ impl Thread { 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 @@ impl Thread { 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 @@ impl Thread { }; if let Err(err) = ret { - if err.reply != BR_TRANSACTION_COMPLETE { - info.reply = err.reply; - } - self.push_return_work(err.reply); - if let Some(source) = &err.source { - info.errno = source.to_errno(); + if err.reply != BR_TRANSACTION_COMPLETE { info.reply = err.reply; + if let Some(source) = &err.source { + info.errno = source.to_errno(); - { - 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 @@ impl Thread { 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 @@ impl Transaction { 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 @@ impl Transaction { 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 @@ impl Transaction { 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 @@ impl Transaction { 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 @@ impl Transaction { 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 @@ impl DeliverToRead for Transaction { 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 @@ impl DeliverToRead for Transaction { // 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(); -- cgit v1.2.3 From b34826e55aad3520ec813f1f367c11b24b29dc9f Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 3 Jun 2026 10:44:54 -0700 Subject: 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)) { -- cgit v1.2.3 From eb1645bf10190e71f6f0316e37ff70755d719b53 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Tue, 16 Jun 2026 02:47:43 +0530 Subject: 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 crate::defs::*; 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(); -- cgit v1.2.3 From 114a116aaa5f0295376cdf12da743c5bce3b20ce Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 Jun 2026 18:52:30 +0000 Subject: 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); -- cgit v1.2.3 From f223d27a546c1e1f48d38fd67760e78f068fe8c4 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 19 Jun 2026 18:52:31 +0000 Subject: 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); /* -- cgit v1.2.3 From 803c8a9502e9b97cd6ae937618ef4a8fd6274343 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 31 May 2026 22:29:24 +0900 Subject: 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 @@ impl Drop for Allocation { 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 @@ impl<'a> AllocationView<'a> { } 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 => { -- cgit v1.2.3 From 6849cabfd30fb5727cfd31e8241e15801e17ebf9 Mon Sep 17 00:00:00 2001 From: Keshav Verma Date: Thu, 25 Jun 2026 16:09:57 +0530 Subject: 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 @@ impl Process { 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)?) } -- cgit v1.2.3 From b28d3d0841a6083f82dfa9fe940afcd748f2a1e2 Mon Sep 17 00:00:00 2001 From: Federico Lombardo Date: Thu, 2 Jul 2026 22:43:47 +0200 Subject: 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), -- cgit v1.2.3 From bc4a9828897871ff3e5a1f8a1d346decbf4ee95e Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 3 Jul 2026 11:25:12 +0000 Subject: 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 @@ impl DeliverToRead for FreezeMessage { } 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 @@ impl Node { } } - 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 @@ impl Node { ); } 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 @@ impl Process { // 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 @@ impl Process { 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 @@ impl Process { // 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); -- cgit v1.2.3 From 77e43bcb7ec177e293a5c3f1b91a2c5aebfb6c68 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 1 Jul 2026 07:44:17 +0200 Subject: netfilter: nf_nat_sip: reload possible stale data pointer quoting sashiko: ------------------------------------------------------------------------ [..] noticed a potential memory bug and header corruption involving the SIP NAT helper. In net/netfilter/nf_nat_sip.c:nf_nat_sip(): if (skb_ensure_writable(skb, skb->len)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; } uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, 0, 0, NULL, 0)) { If a cloned or fragmented SKB is reallocated by skb_ensure_writable(), the old data buffer is freed. However, nf_nat_sip() fails to update *dptr to point to the new buffer. It also appears to use nf_nat_mangle_udp_packet() on what could be a TCP packet, which would overwrite the sequence number with a checksum update. ------------------------------------------------------------------------ nf_conntrack_sip linerizes skbs, hence no fragmented skb can be seen. But clones are possible, so rebuild dptr. Disable nf_nat_mangle_udp_packet() branch for TCP streams. It doesn't look like this can ever happen, else we should have received bug reports about this, so just check the conntrack is UDP and drop otherwise. The calling conntrack_sip set ->forced_dport for SIP_HDR_VIA_UDP messages, so I don't think this is ever expected to be true for a TCP stream. Fixes: 7266507d8999 ("netfilter: nf_ct_sip: support Cisco 7941/7945 IP phones") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal --- net/netfilter/nf_nat_sip.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 67c04d8143ab..aea02f6aff09 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -289,13 +289,24 @@ next: /* Mangle destination port for Cisco phones, then fix up checksums */ if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { + int doff = *dptr - (const char *)skb->data; struct udphdr *uh; + if (doff <= 0) { + DEBUG_NET_WARN_ON_ONCE(1); + return NF_DROP; + } + + /* ct_sip_info->forced_dport only expected with UDP */ + if (nf_ct_protonum(ct) != IPPROTO_UDP) + return NF_DROP; + if (skb_ensure_writable(skb, skb->len)) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; } + *dptr = skb->data + doff; uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; -- cgit v1.2.3 From 64cdf7d30ac18e43df6c48004435febb965809a8 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Sun, 28 Jun 2026 16:05:54 +0800 Subject: netfilter: xt_u32: reject invalid shift counts u32_match_it() executes rule-supplied shift operands on a 32-bit value. A malformed u32 rule can provide a shift count of 32 or more, triggering an undefined shift out-of-bounds during packet evaluation. Validate XT_U32_LEFTSH and XT_U32_RIGHTSH operands in u32_mt_checkentry() and reject malformed rules before they reach the packet path. Fixes: 1b50b8a371e9 ("[NETFILTER]: Add u32 match") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Signed-off-by: Florian Westphal --- net/netfilter/xt_u32.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index 117d4615d668..ec1a21e3b6e2 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -100,7 +100,7 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par) { const struct xt_u32 *data = par->matchinfo; const struct xt_u32_test *ct; - unsigned int i; + unsigned int i, j; if (data->ntests > ARRAY_SIZE(data->tests)) return -EINVAL; @@ -111,6 +111,16 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par) if (ct->nnums > ARRAY_SIZE(ct->location) || ct->nvalues > ARRAY_SIZE(ct->value)) return -EINVAL; + + for (j = 1; j < ct->nnums; ++j) { + switch (ct->location[j].nextop) { + case XT_U32_LEFTSH: + case XT_U32_RIGHTSH: + if (ct->location[j].number >= 32) + return -EINVAL; + break; + } + } } return 0; -- cgit v1.2.3 From 444853cd438201007da5359821adcc2995655ab1 Mon Sep 17 00:00:00 2001 From: Feng Wu Date: Thu, 25 Jun 2026 08:44:25 +0000 Subject: netfilter: xt_rateest: fix u64 truncation in xt_rateest_mt() On links faster than ~34 Gbps, where byte rate may exceed 2^32-1 (~ 4.3 GBps), the comparison result becomes incorrect because the truncated value no longer reflects the actual estimator rate. Fix by changing the local variables to u64. Fixes: 1c0d32fde5bd ("net_sched: gen_estimator: complete rewrite of rate estimators") Signed-off-by: Feng Wu Signed-off-by: Florian Westphal --- net/netfilter/xt_rateest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index b1d736c15fcb..7c05b6342578 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -16,7 +16,7 @@ xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_rateest_match_info *info = par->matchinfo; struct gnet_stats_rate_est64 sample = {0}; - u_int32_t bps1, bps2, pps1, pps2; + u64 bps1, bps2, pps1, pps2; bool ret = true; gen_estimator_read(&info->est1->rate_est, &sample); -- cgit v1.2.3 From 278296b69fae5dd951599692cd481bae4995215c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 1 Jul 2026 12:46:57 +0200 Subject: netfilter: nfnetlink_cthelper: cap to maximum number of expectation per master on updates Really cap it to NF_CT_EXPECT_MAX_CNT (255) on updates. The commit ("netfilter: nfnetlink_cthelper: cap to maximum number of expectation per master") only covers creation of helpers, not updates. Fixes: 397c8300972f ("netfilter: nf_conntrack_helper: cap maximum number of expectation at helper registration") Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- net/netfilter/nfnetlink_cthelper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 2cbcca9110db..f062ac210343 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -316,6 +316,8 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy, new_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + if (!new_policy->max_expected) + new_policy->max_expected = NF_CT_EXPECT_MAX_CNT; if (new_policy->max_expected > NF_CT_EXPECT_MAX_CNT) return -EINVAL; -- cgit v1.2.3 From 43ccc20b5a733226417832cf16ef45322e594990 Mon Sep 17 00:00:00 2001 From: Zhixing Chen Date: Wed, 1 Jul 2026 18:09:30 +0800 Subject: netfilter: ip6tables: mark malformed IPv6 extension headers for hotdrop The ah, hbh and rt matches check that the fixed extension header is present, then use the header length field to derive the advertised extension header length for matching. For the ah match, add the missing advertised-length check. For hbh and rt, update the existing advertised-length checks. In all three cases, set hotdrop to true before returning false when the advertised extension header length exceeds the available skb data. Returning false treats the packet as a rule mismatch. Set hotdrop to true and drop malformed packets so they cannot bypass rules intended to drop packets with these IPv6 extension headers. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Zhixing Chen Signed-off-by: Florian Westphal --- net/ipv6/netfilter/ip6t_ah.c | 5 +++++ net/ipv6/netfilter/ip6t_hbh.c | 1 + net/ipv6/netfilter/ip6t_rt.c | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 70da2f2ce064..1258783ed876 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -56,6 +56,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par) } hdrlen = ipv6_authlen(ah); + if (skb->len - ptr < hdrlen) { + /* Packet smaller than its length field */ + par->hotdrop = true; + return false; + } pr_debug("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen); pr_debug("RES %04X ", ah->reserved); diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index 450dd53846a2..6d1a5d2026a6 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -75,6 +75,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) hdrlen = ipv6_optlen(oh); if (skb->len - ptr < hdrlen) { /* Packet smaller than it's length field */ + par->hotdrop = true; return false; } diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 5561bd9cea81..278b52752f36 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -56,7 +56,8 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) hdrlen = ipv6_optlen(rh); if (skb->len - ptr < hdrlen) { - /* Pcket smaller than its length field */ + /* Packet smaller than its length field */ + par->hotdrop = true; return false; } -- cgit v1.2.3 From d63611cbe8af99dd61b118ee6e5b5e3e518250b2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 2 Jul 2026 14:33:09 +0200 Subject: netfilter: nft_set_rbtree: get command skips end element with open interval The get command on intervals provide partial matches such as subranges for usability reasons. However, an open interval has no closing end element. If the closing element matches within the range of the open internal, ie. its closest match is the start element of the open range, then, return 0 but offer no matching element to userspace through netlink as a special case. Userspace provides at least a matching start element in this case and the closing end element matching the open interal is ignored. Another possibility is to report the matching start element of the open interval for this end interval. However, this results in duplicated matching being listed in userspace because userspace does not expect a start element as response to a end element. Fixes: 2aa34191f06f ("netfilter: nft_set_rbtree: use binary search array in get command") Reported-by: Melbin K Mathew Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- net/netfilter/nf_tables_api.c | 3 +++ net/netfilter/nft_set_rbtree.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 4884f7f7aaee..a9eaf9455c77 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -6563,6 +6563,9 @@ static int nft_get_set_elem(struct nft_ctx *ctx, const struct nft_set *set, if (err < 0) return err; + if (!elem.priv) + return 0; + err = -ENOMEM; skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); if (skb == NULL) diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 018bbb6df4ce..6222e9bb57bc 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -184,10 +184,14 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set, if (!interval || nft_set_elem_expired(interval->from)) return ERR_PTR(-ENOENT); - if (flags & NFT_SET_ELEM_INTERVAL_END) + if (flags & NFT_SET_ELEM_INTERVAL_END) { + if (!interval->to) + return NULL; + rbe = container_of(interval->to, struct nft_rbtree_elem, ext); - else + } else { rbe = container_of(interval->from, struct nft_rbtree_elem, ext); + } return &rbe->priv; } -- cgit v1.2.3 From 6b335af0d0d1ff44ac579d106953bf19299e5233 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 2 Jul 2026 15:34:28 +0800 Subject: ipvs: fix PMTU for GUE/GRE tunnel ICMP errors When an ICMP Fragmentation Needed error is received for a tunneled IPVS connection, ip_vs_in_icmp() recomputes the MTU that the original packet can use by subtracting the tunnel overhead from the reported next-hop MTU. The current code always subtracts sizeof(struct iphdr), which is only the IPIP overhead. For GUE and GRE tunnels, ipvs_udp_decap() and ipvs_gre_decap() already compute the additional tunnel header length, but that value is scoped to the decapsulation block and is lost before the ICMP_FRAG_NEEDED handling. As a result, the ICMP error sent back to the client advertises an MTU that is too large, so PMTUD can fail to converge for GUE/GRE-tunneled real servers. With a reported next-hop MTU of 1400, a GUE tunnel currently returns 1380 to the client. The correct value is 1368: 1400 - sizeof(struct iphdr) - sizeof(struct udphdr) - sizeof(struct guehdr) Hoist the tunnel header length into the main ip_vs_in_icmp() scope and subtract sizeof(struct iphdr) + ulen in the Fragmentation Needed path. The IPIP path keeps ulen as 0, so its existing 1400 - 20 = 1380 result is unchanged. Fixes: 508f744c0de3 ("ipvs: strip udp tunnel headers from icmp errors") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index d40b404c1bf6..906f2c361676 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1767,6 +1767,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, bool tunnel, new_cp = false; union nf_inet_addr *raddr; char *outer_proto = "IPIP"; + int ulen = 0; *related = 1; @@ -1831,7 +1832,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Error for our tunnel must arrive at LOCAL_IN */ (skb_rtable(skb)->rt_flags & RTCF_LOCAL)) { __u8 iproto; - int ulen; /* Non-first fragment has no UDP/GRE header */ if (unlikely(cih->frag_off & htons(IP_OFFSET))) @@ -1936,8 +1936,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); } - if (mtu > 68 + sizeof(struct iphdr)) - mtu -= sizeof(struct iphdr); + if (mtu > 68 + sizeof(struct iphdr) + ulen) + mtu -= sizeof(struct iphdr) + ulen; info = htonl(mtu); } /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of -- cgit v1.2.3 From 2975324d164c552b028632f107b567302863b7f6 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Thu, 2 Jul 2026 19:28:36 +0800 Subject: ipvs: reset full ip_vs_seq structs in ip_vs_conn_new Commit 9a05475cebdd ("ipvs: avoid kmem_cache_zalloc in ip_vs_conn_new") changed ip_vs_conn_new() to allocate an ip_vs_conn object with kmem_cache_alloc(). The function then initializes many fields explicitly, but only resets in_seq.delta and out_seq.delta in the two struct ip_vs_seq members. That leaves init_seq and previous_delta uninitialized. This is normally harmless while the corresponding IP_VS_CONN_F_IN_SEQ or IP_VS_CONN_F_OUT_SEQ flag is clear. For connections learned from a sync message, however, ip_vs_proc_conn() preserves those flags from IP_VS_CONN_F_BACKUP_MASK and passes opt=NULL when the message omits IPVS_OPT_SEQ_DATA. In that case the new connection can be hashed with SEQ flags set but with the rest of in_seq/out_seq still containing stale slab data. When a packet for such a connection is later handled by an IPVS application helper, vs_fix_seq() and vs_fix_ack_seq() use previous_delta and init_seq to rewrite TCP sequence numbers. A malformed sync message can therefore make forwarded packets carry stale slab bytes in their TCP seq/ack numbers, and can also corrupt the forwarded TCP flow. Reset both struct ip_vs_seq members completely before publishing the connection. This matches the existing "reset struct ip_vs_seq" comment and keeps the sequence-adjustment gates inactive unless valid sequence data is installed later. Fixes: 9a05475cebdd ("ipvs: avoid kmem_cache_zalloc in ip_vs_conn_new") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude-Code:GLM-5.2 Signed-off-by: Yizhou Zhao Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index cb36641f8d1c..6ed2622363f0 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1420,8 +1420,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af, cp->app = NULL; cp->app_data = NULL; /* reset struct ip_vs_seq */ - cp->in_seq.delta = 0; - cp->out_seq.delta = 0; + memset(&cp->in_seq, 0, sizeof(cp->in_seq)); + memset(&cp->out_seq, 0, sizeof(cp->out_seq)); if (unlikely(flags & IP_VS_CONN_F_NO_CPORT)) { int af_id = ip_vs_af_index(cp->af); -- cgit v1.2.3 From 1b47026fb4b35bac850ad6e8a4ad7fc018e09ebc Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Fri, 3 Jul 2026 13:04:46 +0800 Subject: netfilter: xt_connmark: reject invalid shift parameters Revision 2 of the CONNMARK target accepts user-controlled shift parameters and applies them to 32-bit mark values in connmark_tg_shift(). A shift_bits value of 32 or more triggers an undefined-shift bug when the rule is evaluated. Invalid shift_dir values are also accepted and silently fall back to the left-shift path. Reject invalid revision-2 shift parameters in connmark_tg_check() so malformed rules fail at installation time, before they can reach the packet path. Fixes: 472a73e00757 ("netfilter: xt_conntrack: Support bit-shifting for CONNMARK & MARK targets.") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Wyatt Feng Reviewed-by: Ren Wei Reviewed-by: Phil Sutter Signed-off-by: Florian Westphal --- net/netfilter/xt_connmark.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 4277084de2e7..2cf27f7d59b9 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -112,6 +112,16 @@ static int connmark_tg_check(const struct xt_tgchk_param *par) return ret; } +static int connmark_tg_check_v2(const struct xt_tgchk_param *par) +{ + const struct xt_connmark_tginfo2 *info = par->targinfo; + + if (info->shift_dir > D_SHIFT_RIGHT || info->shift_bits >= 32) + return -EINVAL; + + return connmark_tg_check(par); +} + static void connmark_tg_destroy(const struct xt_tgdtor_param *par) { nf_ct_netns_put(par->net, par->family); @@ -162,7 +172,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV4, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy, @@ -183,7 +193,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = { .name = "CONNMARK", .revision = 2, .family = NFPROTO_IPV6, - .checkentry = connmark_tg_check, + .checkentry = connmark_tg_check_v2, .target = connmark_tg_v2, .targetsize = sizeof(struct xt_connmark_tginfo2), .destroy = connmark_tg_destroy, -- cgit v1.2.3 From a0738abd042f7406edd2175a819cf2e66388ed97 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sat, 27 Jun 2026 00:02:10 +0200 Subject: 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; -- cgit v1.2.3 From d3666875c75eb1bc8090343fa0d6fc8fb7924356 Mon Sep 17 00:00:00 2001 From: Mingyou Chen Date: Wed, 1 Jul 2026 20:01:39 +0800 Subject: 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); } -- cgit v1.2.3 From caa0ecbeff4f7fbf70f22bd8ca598918bffb1b78 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 10:13:38 +0800 Subject: 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) { -- cgit v1.2.3 From 9d4af746af8ce27eefc2338b2feaa1e01f28b6c3 Mon Sep 17 00:00:00 2001 From: Xue Lei Date: Wed, 1 Jul 2026 20:10:42 +0800 Subject: 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); -- cgit v1.2.3 From dbf590b662695b16fbf5917ef129697be4410ea9 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:37:59 +0800 Subject: 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, -- cgit v1.2.3 From 17a8ce84964f243c8f89dc7353ac7e8d3137bc74 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:39:43 +0800 Subject: 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); -- cgit v1.2.3 From d322e40f4edf92bf0ca329e5aa4ae1c0316feb38 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:40:52 +0800 Subject: 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; -- cgit v1.2.3 From f9a13e05a327080c3a1c8165adf9e678fb68fef2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:42:33 +0800 Subject: 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; } -- cgit v1.2.3 From d03a19bd6c7f86b99ca8fb61a6ec2345cee1d9d6 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 3 Jul 2026 15:43:50 +0800 Subject: 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; } -- cgit v1.2.3 From 2b533e775aec580cf60074417f4ca00ac9cf3580 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 3 Jul 2026 13:35:47 +0200 Subject: 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); + +/****************************************************************************/ -- cgit v1.2.3 From 6301f6a34ed86fe6f3b7b3211ea069f3677fc559 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Tue, 30 Jun 2026 11:09:22 -0400 Subject: net/sched: sch_teql: move rcu_read_lock()/spin_lock() from _bh variants This is a followup based on sashiko comments [1] on commit e5b811fe7931 ("net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF") Use plain rcu_read_lock()/spin_lock() in teql_master_xmit() instead of the _bh variants, since ndo_start_xmit is already invoked with BH disabled by the core stack and the _bh primitives can warn in_hardirq() when xmit is reached through netpoll or a softirq xmit path with hard IRQs disabled. Moves rcu_read_lock() after restart: label + adds rcu_read_unlock() before goto restart (fixes the unbounded RCU hold across retries) [1] https://sashiko.dev/#/patchset/20260628111229.669751-1-jhs%40mojatatu.com Signed-off-by: Jamal Hadi Salim Fixes: e5b811fe7931 ("net/sched: sch_teql: Introduce slaves_lock to avoid race condition and UAF") Link: https://patch.msgid.link/20260630150922.238714-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/sch_teql.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 24ba31f8c828..5c42a29a981c 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -311,14 +311,14 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; - rcu_read_lock_bh(); - - start = rcu_dereference_bh(master->slaves); - restart: nores = 0; busy = 0; + rcu_read_lock(); + + start = rcu_dereference(master->slaves); + q = start; if (!q) goto drop; @@ -345,17 +345,17 @@ restart: netdev_start_xmit(skb, slave, slave_txq, false) == NETDEV_TX_OK) { __netif_tx_unlock(slave_txq); - spin_lock_bh(&master->slaves_lock); + spin_lock(&master->slaves_lock); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock_bh(&master->slaves_lock); + spin_unlock(&master->slaves_lock); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; - rcu_read_unlock_bh(); + rcu_read_unlock(); return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); @@ -364,37 +364,38 @@ restart: busy = 1; break; case 1: - spin_lock_bh(&master->slaves_lock); + spin_lock(&master->slaves_lock); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock_bh(&master->slaves_lock); - rcu_read_unlock_bh(); + spin_unlock(&master->slaves_lock); + rcu_read_unlock(); return NETDEV_TX_OK; default: nores = 1; break; } __skb_pull(skb, skb_network_offset(skb)); - } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start); + } while ((q = rcu_dereference(NEXT_SLAVE(q))) != start); if (nores && skb_res == NULL) { skb_res = skb; + rcu_read_unlock(); goto restart; } if (busy) { netif_stop_queue(dev); - rcu_read_unlock_bh(); + rcu_read_unlock(); return NETDEV_TX_BUSY; } master->tx_errors++; drop: master->tx_dropped++; - rcu_read_unlock_bh(); + rcu_read_unlock(); dev_kfree_skb(skb); return NETDEV_TX_OK; } -- cgit v1.2.3 From 539dfcf69105d8d3d4d677b71de6e5ede2e6dfa0 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 1 Jul 2026 18:42:22 +0200 Subject: mac802154: remove interfaces with RCU list deletion Queue wake, stop, and disable paths walk local->interfaces under RCU. The bulk hardware teardown path removes entries with list_del(), so an asynchronous transmit completion can follow a poisoned list node in ieee802154_wake_queue(). Use list_del_rcu() as in the single-interface removal path. The following unregister_netdevice() waits for in-flight RCU readers before freeing the netdevice, so no separate grace-period wait is needed. Fixes: 592dfbfc72f5 ("mac820154: move interface unregistration into iface") Reported-by: syzbot+36256deb69a588e9290e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=36256deb69a588e9290e Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Reviewed-by: Kuniyuki Iwashima Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/20260701164222.9094-1-alhouseenyousef@gmail.com Signed-off-by: Paolo Abeni --- net/mac802154/iface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 000be60d9580..b823720630e7 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -703,7 +703,7 @@ void ieee802154_remove_interfaces(struct ieee802154_local *local) mutex_lock(&local->iflist_mtx); list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { - list_del(&sdata->list); + list_del_rcu(&sdata->list); unregister_netdevice(sdata->dev); } -- cgit v1.2.3 From 0f0e4ae6975c773f7854fc48932a267f6c79088f Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:15 +0300 Subject: net/mlx5: LAG, Fix off-by-one in single-FDB error rollback On failure at index i, the reverse cleanup loop in mlx5_lag_create_single_fdb() starts from i, so the failed index itself is rolled back. That can operate on uninitialized state or double-tear-down a rule the add_one path already self-rolled-back. Start the rollback from i - 1 so only successfully-installed entries are undone. Fixes: ddbb5ddc43ad ("net/mlx5: LAG, Refactor lag logic") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-2-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c index 113866494d16..6b4ad3c53f2f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c @@ -78,7 +78,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter) } return 0; err: - mlx5_lag_for_each_reverse(j, i, 0, ldev, filter) { + mlx5_lag_for_each_reverse(j, i - 1, 0, ldev, filter) { struct mlx5_eswitch *slave_esw; if (j == master_idx) -- cgit v1.2.3 From d4b85f9a668b9c44216bb78daf4ec1a915cc92d1 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:16 +0300 Subject: net/mlx5: LAG, MPESW, Fix missing complete() on devcom error mlx5_mpesw_work() returned without calling complete() when mlx5_lag_get_devcom_comp() returned NULL. A caller that queued the work and waited on mpesww->comp would block indefinitely. Funnel the early-return path through a new "complete" label so the waiter is always woken. Fixes: b430c1b4f63b ("net/mlx5: Replace global mlx5_intf_lock with HCA devcom component lock") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-3-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c index 50bfb450c71e..abf72026c751 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c @@ -194,8 +194,10 @@ static void mlx5_mpesw_work(struct work_struct *work) struct mlx5_lag *ldev = mpesww->lag; devcom = mlx5_lag_get_devcom_comp(ldev); - if (!devcom) - return; + if (!devcom) { + mpesww->result = -ENODEV; + goto complete; + } mlx5_devcom_comp_lock(devcom); mlx5_mpesw_sd_devcoms_lock(ldev); @@ -213,6 +215,7 @@ unlock: mutex_unlock(&ldev->lock); mlx5_mpesw_sd_devcoms_unlock(ldev); mlx5_devcom_comp_unlock(devcom); +complete: complete(&mpesww->comp); } -- cgit v1.2.3 From 7bed4af0ced82948d660205efecd551ef8bc3912 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 30 Jun 2026 14:29:17 +0300 Subject: net/mlx5e: TC, skip peer flow cleanup when LAG seq is unavailable mlx5_lag_get_dev_seq() will return error when the peer isn't in the LAG or when no device is marked as master. Result bad memory access and kernel crash[1]. Hence, skip the peer when lookup fails. Note: In case there are peer flows, they are cleaned before LAG cleared the master mark. [1] RIP: 0010:mlx5e_tc_del_fdb_peers_flow+0x3d/0x350 [mlx5_core] Call Trace: mlx5e_tc_clean_fdb_peer_flows+0xc1/0x130 [mlx5_core] mlx5_esw_offloads_unpair+0x3a/0x400 [mlx5_core] mlx5_esw_offloads_devcom_event+0xee/0x360 [mlx5_core] mlx5_devcom_send_event+0x7a/0x140 [mlx5_core] mlx5_esw_offloads_devcom_cleanup+0x2f/0x90 [mlx5_core] mlx5e_tc_esw_cleanup+0x28/0xf0 [mlx5_core] mlx5e_rep_tc_cleanup+0x19/0x30 [mlx5_core] mlx5e_cleanup_uplink_rep_tx+0x36/0x40 [mlx5_core] mlx5e_cleanup_rep_tx+0x55/0x60 [mlx5_core] mlx5e_detach_netdev+0x96/0xf0 [mlx5_core] mlx5e_netdev_change_profile+0x5b/0x120 [mlx5_core] mlx5e_netdev_attach_nic_profile+0x1b/0x30 [mlx5_core] mlx5e_vport_rep_unload+0xdd/0x110 [mlx5_core] __esw_offloads_unload_rep+0x81/0xb0 [mlx5_core] mlx5_eswitch_unregister_vport_reps+0x1d7/0x220 [mlx5_core] mlx5e_rep_remove+0x22/0x30 [mlx5_core] device_release_driver_internal+0x194/0x1f0 bus_remove_device+0xe8/0x1b0 device_del+0x159/0x3c0 mlx5_rescan_drivers_locked+0xbc/0x2d0 [mlx5_core] mlx5_unregister_device+0x54/0x80 [mlx5_core] mlx5_uninit_one+0x73/0x130 [mlx5_core] remove_one+0x78/0xe0 [mlx5_core] pci_device_remove+0x39/0xa0 Fixes: 971b28accc09 ("net/mlx5: LAG, replace mlx5_get_dev_index with LAG sequence number") Signed-off-by: Shay Drory Reviewed-by: Mark Bloch Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630112917.698313-4-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 910492eb51f2..1bc7b9019124 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -5547,6 +5547,9 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw) mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) { i = mlx5_lag_get_dev_seq(peer_esw->dev); + if (i < 0) + continue; + list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i]) mlx5e_tc_del_fdb_peers_flow(flow); } -- cgit v1.2.3 From 6bb8898f702385d363dc2c513a1efa62807f8068 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Tue, 23 Jun 2026 10:35:06 +0800 Subject: 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) -- cgit v1.2.3 From 25f6b929c7e379cbea7cb8caa67b49b2d1efae17 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:49 +0300 Subject: net/mlx5e: Fix HV VHCA stats zero-sized buffer allocation mlx5e_hv_vhca_stats_create() is called from mlx5e_nic_enable(), before mlx5e_open(). At that point priv->stats_nch is still zero, because it is only ever incremented in mlx5e_channel_stats_alloc(), which is reached only from mlx5e_open_channel(). mlx5e_hv_vhca_stats_buf_size() therefore returns 0, and kvzalloc(0, GFP_KERNEL) returns ZERO_SIZE_PTR ((void *)16) rather than NULL. The "if (!buf)" guard does not catch this, and mlx5e_hv_vhca_stats_create() completes "successfully" with priv->stats_agent.buf set to ZERO_SIZE_PTR. Once channels are opened (priv->stats_nch > 0) and the hypervisor enables stats reporting, mlx5e_hv_vhca_stats_work() recomputes buf_len using the new non-zero stats_nch and calls memset(buf, 0, buf_len) on ZERO_SIZE_PTR, faulting at address 0x10. Allocate the buffer based on priv->max_nch, which is set in mlx5e_priv_init() and is the upper bound on stats_nch: - Add a separate helper mlx5e_hv_vhca_stats_buf_max_size() that returns sizeof(per_ring_stats) * max(max_nch, stats_nch), and use it for the kvzalloc() in mlx5e_hv_vhca_stats_create(). - Keep mlx5e_hv_vhca_stats_buf_size() (which returns based on stats_nch) for the worker's active payload size, so the wire format (block->rings = stats_nch) and the amount of data filled by mlx5e_hv_vhca_fill_stats() are unchanged. The max(max_nch, stats_nch) guard handles the rare case where mlx5e_attach_netdev() recomputes max_nch downward across a detach/resume cycle while priv->stats_nch persists (mlx5e_detach_netdev does not call mlx5e_priv_cleanup, so stats_nch is only reset when the netdev is destroyed). Without the guard, the worker could compute buf_len from stats_nch and overrun the smaller buffer allocated based on the reduced max_nch. Allocating a non-zero buffer also makes the kvzalloc() failure path in mlx5e_hv_vhca_stats_create() reachable for the first time: it returns early without (re)creating the agent. Clear priv->stats_agent.{agent,buf} in mlx5e_hv_vhca_stats_destroy() after freeing them, so that if a later create() bails out on this path, a subsequent teardown does not double-free the stale agent/buffer left from a previous enable/disable cycle. This mirrors the existing mlx5e pattern of preallocating arrays of size max_nch (e.g. priv->channel_stats) and lazily populating entries up to stats_nch on demand. Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-2-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index 195863b2c013..72f3ca4dd076 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -54,6 +54,12 @@ static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) priv->stats_nch); } +static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv) +{ + return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * + max(priv->max_nch, priv->stats_nch)); +} + static void mlx5e_hv_vhca_stats_work(struct work_struct *work) { struct mlx5e_hv_vhca_stats_agent *sagent; @@ -122,7 +128,7 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent) void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) { - int buf_len = mlx5e_hv_vhca_stats_buf_size(priv); + int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv); struct mlx5_hv_vhca_agent *agent; priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL); @@ -155,5 +161,7 @@ void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) return; mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent); + priv->stats_agent.agent = NULL; kvfree(priv->stats_agent.buf); + priv->stats_agent.buf = NULL; } -- cgit v1.2.3 From 89b25b5f46f488ea3b29b3444864c76944c9075b Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:50 +0300 Subject: net/mlx5e: Fix HV VHCA stats agent registration race mlx5e_hv_vhca_stats_create() registers the stats agent through mlx5_hv_vhca_agent_create(). The helper publishes the agent in hv_vhca->agents[type] under agents_lock and immediately schedules an asynchronous control invalidation on the HV VHCA workqueue before returning to mlx5e. The asynchronous invalidation invokes the control agent's invalidate callback, which reads the hypervisor control block and forwards the command to mlx5e_hv_vhca_stats_control(). That callback may either: - call cancel_delayed_work_sync(&priv->stats_agent.work), or - call queue_delayed_work(priv->wq, &sagent->work, sagent->delay). However, the delayed_work and priv->stats_agent.agent are only initialized after mlx5_hv_vhca_agent_create() returns to mlx5e: agent = mlx5_hv_vhca_agent_create(...); /* publish + invalidate */ ... priv->stats_agent.agent = agent; /* too late */ INIT_DELAYED_WORK(&priv->stats_agent.work, ...); /* too late */ If the asynchronous control path runs before the two assignments above, it can: - Operate on an uninitialized delayed_work whose timer.function is NULL. queue_delayed_work() calls add_timer() unconditionally, so when the timer expires the timer softirq invokes a NULL function pointer. - Re-initialize the timer later through INIT_DELAYED_WORK() while the timer is already enqueued in the timer wheel, corrupting the hlist (entry.pprev cleared while the previous bucket node still points at this entry). - When the worker eventually runs, mlx5e_hv_vhca_stats_work() reads sagent->agent (NULL) and dereferences it inside mlx5_hv_vhca_agent_write(). Fix this by: - Initializing priv->stats_agent.work before invoking mlx5_hv_vhca_agent_create(), so the work is always in a valid state when the control callback observes it. - Adding a struct mlx5_hv_vhca_agent **ctx_update out-parameter to mlx5_hv_vhca_agent_create(). The helper writes the agent pointer to *ctx_update before publishing into hv_vhca->agents[] and triggering the agents_update flow, so any callback subsequently invoked from that flow already sees a valid priv->stats_agent.agent. This avoids having the control callback participate in agent initialization. While at it, access priv->stats_agent.agent with READ_ONCE()/WRITE_ONCE() for the cross-CPU access with the worker, and clear priv->stats_agent.buf on the agent_create() failure path. Fixes: cef35af34d6d ("net/mlx5e: Add mlx5e HV VHCA stats agent") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-3-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- .../ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 21 ++++++++++++--------- .../net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c | 8 ++++++-- .../net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h | 6 ++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index 72f3ca4dd076..cdaf77650164 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -73,7 +73,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work) sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work); priv = container_of(sagent, struct mlx5e_priv, stats_agent); buf_len = mlx5e_hv_vhca_stats_buf_size(priv); - agent = sagent->agent; + agent = READ_ONCE(sagent->agent); buf = sagent->buf; memset(buf, 0, buf_len); @@ -135,11 +135,14 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) if (!priv->stats_agent.buf) return; + INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); + agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca, MLX5_HV_VHCA_AGENT_STATS, mlx5e_hv_vhca_stats_control, NULL, mlx5e_hv_vhca_stats_cleanup, - priv); + priv, + &priv->stats_agent.agent); if (IS_ERR_OR_NULL(agent)) { if (IS_ERR(agent)) @@ -148,20 +151,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv) agent); kvfree(priv->stats_agent.buf); - return; + priv->stats_agent.buf = NULL; } - - priv->stats_agent.agent = agent; - INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work); } void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv) { - if (IS_ERR_OR_NULL(priv->stats_agent.agent)) + struct mlx5_hv_vhca_agent *agent; + + agent = READ_ONCE(priv->stats_agent.agent); + if (IS_ERR_OR_NULL(agent)) return; - mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent); - priv->stats_agent.agent = NULL; + mlx5_hv_vhca_agent_destroy(agent); + WRITE_ONCE(priv->stats_agent.agent, NULL); kvfree(priv->stats_agent.buf); priv->stats_agent.buf = NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c index d6dc7bce855e..305752dab7bd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c @@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca) return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL, NULL, mlx5_hv_vhca_control_agent_invalidate, - NULL, NULL); + NULL, NULL, NULL); } static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent) @@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleaup)(struct mlx5_hv_vhca_agent *agent), - void *priv) + void *priv, + struct mlx5_hv_vhca_agent **ctx_update) { struct mlx5_hv_vhca_agent *agent; @@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, agent->invalidate = invalidate; agent->cleanup = cleaup; + if (ctx_update) + WRITE_ONCE(*ctx_update, agent); + mutex_lock(&hv_vhca->agents_lock); hv_vhca->agents[type] = agent; mutex_unlock(&hv_vhca->agents_lock); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h index f240ffe5116c..8b3974cf0ee4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h @@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context); + void *context, + struct mlx5_hv_vhca_agent **ctx_update); void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent); int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent, @@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca, void (*invalidate)(struct mlx5_hv_vhca_agent*, u64 block_mask), void (*cleanup)(struct mlx5_hv_vhca_agent *agent), - void *context) + void *context, + struct mlx5_hv_vhca_agent **ctx_update) { return NULL; } -- cgit v1.2.3 From 5a799714e8ca0bce9ea40694f49914cf1adbbaa9 Mon Sep 17 00:00:00 2001 From: Feng Liu Date: Tue, 30 Jun 2026 14:51:51 +0300 Subject: net/mlx5e: Fix publication race for priv->channel_stats[] mlx5e_channel_stats_alloc() publishes a new entry to priv->channel_stats[] and then increments priv->stats_nch as a publication token, but neither store carries any memory barrier: priv->channel_stats[ix] = kvzalloc_node(...); if (!priv->channel_stats[ix]) return -ENOMEM; priv->stats_nch++; Concurrent readers compute the loop bound from priv->stats_nch and then dereference priv->channel_stats[i] using plain accesses, e.g. for (i = 0; i < priv->stats_nch; i++) { struct mlx5e_channel_stats *cs = priv->channel_stats[i]; ... cs->rq.packets ... } On weakly-ordered architectures (ARM, PowerPC, RISC-V) the writes to channel_stats[ix] and stats_nch may become visible to other CPUs out of program order. A reader can observe stats_nch == N while still seeing channel_stats[N-1] == NULL, leading to a NULL pointer dereference in the channel_stats loop. This has been observed in production on BlueField-3 DPUs (arm64), where ovs-vswitchd queries netdev statistics over netlink during NIC bringup, racing mlx5e_open_channel() -> mlx5e_channel_stats_alloc() on another CPU: Unable to handle kernel NULL pointer dereference at virtual address 0x840 Hardware name: BlueField-3 DPU pc : mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core] Call trace: mlx5e_fold_sw_stats64+0x30/0x180 [mlx5_core] dev_get_stats+0x50/0xc0 ovs_vport_get_stats+0x38/0xac [openvswitch] ovs_vport_cmd_fill_info+0x194/0x290 [openvswitch] ovs_vport_cmd_get+0xbc/0x10c [openvswitch] genl_family_rcv_msg_doit+0xd0/0x160 genl_rcv_msg+0xec/0x1f0 netlink_rcv_skb+0x64/0x130 genl_rcv+0x40/0x60 netlink_unicast+0x2fc/0x370 netlink_sendmsg+0x1dc/0x454 ... __arm64_sys_sendmsg+0x2c/0x40 Add mlx5e_stats_nch_write() and mlx5e_stats_nch_read() helpers in en.h that wrap the smp_store_release()/smp_load_acquire() pair on stats_nch. The release/acquire pair establishes the contract: stats_nch == N => channel_stats[0..N-1] are visible and non-NULL. Publish the stats_nch increment via mlx5e_stats_nch_write() in the writer (mlx5e_channel_stats_alloc()), and read stats_nch via mlx5e_stats_nch_read() in all readers: mlx5e RX/TX queue stats, mlx5e_get_base_stats(), ethtool channels stats, IPoIB stats, the sw_stats fold and the HV VHCA stats agent. Fixes: fa691d0c9c08 ("net/mlx5e: Allocate per-channel stats dynamically at first usage") Signed-off-by: Feng Liu Reviewed-by: Eran Ben Elisha Reviewed-by: Cosmin Ratiu Reviewed-by: Nimrod Oren Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260630115151.729219-4-tariqt@nvidia.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 12 ++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c | 10 ++++++---- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 14 ++++++++------ drivers/net/ethernet/mellanox/mlx5/core/en_stats.c | 9 +++++---- drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c | 3 ++- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 2270e2e550dd..d507289096c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -987,6 +987,18 @@ struct mlx5e_priv { struct ethtool_fec_hist_range *fec_ranges; }; +static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv) +{ + /* Pairs with smp_store_release in mlx5e_stats_nch_write(). */ + return smp_load_acquire(&priv->stats_nch); +} + +static inline void mlx5e_stats_nch_write(struct mlx5e_priv *priv, u16 n) +{ + /* Pairs with smp_load_acquire in mlx5e_stats_nch_read(). */ + smp_store_release(&priv->stats_nch, n); +} + struct mlx5e_dev { struct net_device *netdev; struct devlink_port dl_port; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c index cdaf77650164..631f802105d5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c @@ -33,9 +33,10 @@ mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch, static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, int buf_len) { + u16 nch = mlx5e_stats_nch_read(priv); int ch, i = 0; - for (ch = 0; ch < priv->stats_nch; ch++) { + for (ch = 0; ch < nch; ch++) { void *buf = data + i; if (WARN_ON_ONCE(buf + @@ -50,8 +51,9 @@ static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data, static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv) { - return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) * - priv->stats_nch); + u16 nch = mlx5e_stats_nch_read(priv); + + return sizeof(struct mlx5e_hv_vhca_per_ring_stats) * nch; } static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv) @@ -106,7 +108,7 @@ static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent, sagent = &priv->stats_agent; block->version = MLX5_HV_VHCA_STATS_VERSION; - block->rings = priv->stats_nch; + block->rings = mlx5e_stats_nch_read(priv); if (!block->command) { cancel_delayed_work_sync(&priv->stats_agent.work); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 775f0c6e55c9..aa8610cedaa8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2773,7 +2773,7 @@ static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu) GFP_KERNEL, cpu_to_node(cpu)); if (!priv->channel_stats[ix]) return -ENOMEM; - priv->stats_nch++; + mlx5e_stats_nch_write(priv, priv->stats_nch + 1); return 0; } @@ -4040,9 +4040,10 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s) { + u16 nch = mlx5e_stats_nch_read(priv); int i; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq; struct mlx5e_rq_stats *rq_stats = &channel_stats->rq; @@ -5488,7 +5489,7 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i, struct mlx5e_rq_stats *xskrq_stats; struct mlx5e_rq_stats *rq_stats; - if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch) + if (mlx5e_is_uplink_rep(priv) || !mlx5e_stats_nch_read(priv)) return; channel_stats = priv->channel_stats[i]; @@ -5512,7 +5513,7 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i, struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_sq_stats *sq_stats; - if (!priv->stats_nch) + if (!mlx5e_stats_nch_read(priv)) return; /* no special case needed for ptp htb etc since txq2sq_stats is kept up @@ -5538,6 +5539,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, struct netdev_queue_stats_tx *tx) { struct mlx5e_priv *priv = netdev_priv(dev); + u16 nch = mlx5e_stats_nch_read(priv); struct mlx5e_ptp *ptp_channel; int i, tc; @@ -5549,7 +5551,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, rx->hw_gro_wire_packets = 0; rx->hw_gro_wire_bytes = 0; - for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) { + for (i = priv->channels.params.num_channels; i < nch; i++) { struct netdev_queue_stats_rx rx_i = {0}; mlx5e_get_queue_stats_rx(dev, i, &rx_i); @@ -5585,7 +5587,7 @@ static void mlx5e_get_base_stats(struct net_device *dev, tx->stop = 0; tx->wake = 0; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; /* handle two cases: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 7f33261ba655..de38b60806c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -515,6 +515,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c) static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) { struct mlx5e_sw_stats *s = &priv->stats.sw; + u16 nch = mlx5e_stats_nch_read(priv); int i; memset(s, 0, sizeof(*s)); @@ -522,7 +523,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw) for (i = 0; i < priv->channels.num; i++) /* for active channels only */ mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]); - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i]; @@ -2614,7 +2615,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ptp) { return; } static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels) { - int max_nch = priv->stats_nch; + int max_nch = mlx5e_stats_nch_read(priv); return (NUM_RQ_STATS * max_nch) + (NUM_CH_STATS * max_nch) + @@ -2627,8 +2628,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels) static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels) { + int max_nch = mlx5e_stats_nch_read(priv); bool is_xsk = priv->xsk.ever_used; - int max_nch = priv->stats_nch; int i, j, tc; for (i = 0; i < max_nch; i++) @@ -2660,8 +2661,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels) static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(channels) { + int max_nch = mlx5e_stats_nch_read(priv); bool is_xsk = priv->xsk.ever_used; - int max_nch = priv->stats_nch; int i, j, tc; for (i = 0; i < max_nch; i++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 0a6003fe60e9..674bed721e63 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -135,10 +135,11 @@ void mlx5i_cleanup(struct mlx5e_priv *priv) static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv) { + u16 nch = mlx5e_stats_nch_read(priv); struct rtnl_link_stats64 s = {}; int i, j; - for (i = 0; i < priv->stats_nch; i++) { + for (i = 0; i < nch; i++) { struct mlx5e_channel_stats *channel_stats; struct mlx5e_rq_stats *rq_stats; -- cgit v1.2.3 From aede83625ff5d9539508582036df30c809d51058 Mon Sep 17 00:00:00 2001 From: Andreas Kempe Date: Thu, 2 Jul 2026 10:41:23 +0000 Subject: 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; } -- cgit v1.2.3 From affe3f077d7a4eeb25937f5323ff059a54b4712c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Mon, 29 Jun 2026 21:51:55 +0200 Subject: 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 { -- cgit v1.2.3 From a00ffd15674bfaf8b906503c1600e3d8709af56c Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Mon, 18 May 2026 14:43:11 +0500 Subject: 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; -- cgit v1.2.3 From a9f41809bf1bd8e5c1bc4b6a1052adac58eb7ab6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 May 2026 23:56:06 +0200 Subject: 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) -- cgit v1.2.3 From aa411adc6ce40ad1a55ebc965f255a4cfc0005f8 Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Sat, 4 Jul 2026 17:22:45 +0530 Subject: 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 = { -- cgit v1.2.3 From 660667cd406648bbaffbd5c0d897c2263a852f11 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Tue, 30 Jun 2026 15:48:56 -0400 Subject: llc: fix SAP refcount leak in llc_ui_autobind() llc_ui_autobind() opens a SAP after choosing a dynamic LSAP. llc_sap_open() returns a reference owned by the caller, and llc_sap_add_socket() takes a second reference for the socket's membership in the SAP hash tables. llc_ui_bind() drops the caller's reference after adding the socket, but llc_ui_autobind() keeps it. When the socket is closed, llc_sap_remove_socket() releases only the socket reference, leaving the SAP on llc_sap_list with sk_count == 0. This is user-visible because repeated autobind and close cycles can consume all dynamic SAP values and make later autobinds fail with -EUSERS. Drop the caller's reference after a successful autobind, matching llc_ui_bind()'s ownership model. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Shuangpeng Bai Link: https://patch.msgid.link/20260630194856.1036497-1-shuangpeng.kernel@gmail.com Signed-off-by: Paolo Abeni --- net/llc/af_llc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 8ed1be1ecccc..b0447c33dbf0 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -312,6 +312,7 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr) /* assign new connection to its SAP */ llc_sap_add_socket(sap, sk); sock_reset_flag(sk, SOCK_ZAPPED); + llc_sap_put(sap); rc = 0; out: dev_put(dev); -- cgit v1.2.3 From d7a8d500d7e42837bd8dce40cb52c97c6e8706a9 Mon Sep 17 00:00:00 2001 From: Jens Emil Schulz Østergaard Date: Tue, 30 Jun 2026 14:20:13 +0200 Subject: net: microchip: vcap: fix races on the shared Super VCAP block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VCAP instances on a chip are not independent, yet they are locked independently. On sparx5 and lan969x the IS0 and IS2 instances are backed by the same Super VCAP hardware block and share its cache and command registers: every access drives the shared VCAP_SUPER_CTRL register and moves data through the shared cache registers. Accessing one instance therefore races with accessing another. The per-instance admin->lock cannot prevent this, as each instance takes a different lock. The locking issue is mostly disguised by the fact that the core usage of the vcap api runs under rtnl. However, the full rule dump in debugfs decodes rules straight from hardware (a READ command followed by a cache read) and runs outside rtnl, so it races a concurrent tc-flower rule write to another Super VCAP instance. Besides corrupting the dump, the read repopulates the shared cache between the writers cache fill and its write command, so the writer commits the wrong data and corrupts the hardware entry. Introduce vcap_lock() and vcap_unlock() helpers and route every rule lock site in the VCAP API and its debugfs code through them. Replace the per-instance admin->lock with a single mutex in struct vcap_control that serializes access to all instances. The helpers reach it through a new admin->vctrl back-pointer, and the clients initialise and destroy the control lock instead of a per-instance one. No path holds more than one instance lock, so collapsing them onto a single mutex cannot self-deadlock. Fixes: 71c9de995260 ("net: microchip: sparx5: Add VCAP locking to protect rules") Signed-off-by: Jens Emil Schulz Østergaard Link: https://patch.msgid.link/20260630-microchip_fix_vcap_locking-v1-1-f60a4596734d@microchip.com Signed-off-by: Paolo Abeni --- .../ethernet/microchip/lan966x/lan966x_vcap_impl.c | 5 +- .../ethernet/microchip/sparx5/sparx5_vcap_impl.c | 5 +- drivers/net/ethernet/microchip/vcap/vcap_api.c | 72 ++++++++++++---------- drivers/net/ethernet/microchip/vcap/vcap_api.h | 3 +- .../net/ethernet/microchip/vcap/vcap_api_debugfs.c | 8 +-- .../microchip/vcap/vcap_api_debugfs_kunit.c | 3 +- .../net/ethernet/microchip/vcap/vcap_api_kunit.c | 3 +- .../net/ethernet/microchip/vcap/vcap_api_private.h | 3 + 8 files changed, 60 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 72e3b189bac5..eb28df80b281 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -601,7 +601,6 @@ static void lan966x_vcap_admin_free(struct vcap_admin *admin) kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); - mutex_destroy(&admin->lock); kfree(admin); } @@ -615,7 +614,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, if (!admin) return ERR_PTR(-ENOMEM); - mutex_init(&admin->lock); + admin->vctrl = ctrl; INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); @@ -721,6 +720,7 @@ int lan966x_vcap_init(struct lan966x *lan966x) ctrl->ops = &lan966x_vcap_ops; INIT_LIST_HEAD(&ctrl->list); + mutex_init(&ctrl->lock); for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { cfg = &lan966x_vcap_inst_cfg[i]; @@ -780,5 +780,6 @@ void lan966x_vcap_deinit(struct lan966x *lan966x) lan966x_vcap_admin_free(admin); } + mutex_destroy(&ctrl->lock); kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c index 95b93e46a41d..cf332de6bf73 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -1930,7 +1930,6 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin) { if (!admin) return; - mutex_destroy(&admin->lock); kfree(admin->cache.keystream); kfree(admin->cache.maskstream); kfree(admin->cache.actionstream); @@ -1950,7 +1949,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl, INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = ctrl; admin->vtype = cfg->vtype; admin->vinst = cfg->vinst; admin->ingress = cfg->ingress; @@ -2059,6 +2058,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5) ctrl->ops = &sparx5_vcap_ops; INIT_LIST_HEAD(&ctrl->list); + mutex_init(&ctrl->lock); for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) { cfg = &consts->vcaps_cfg[idx]; admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg); @@ -2097,5 +2097,6 @@ void sparx5_vcap_deinit(struct sparx5 *sparx5) list_del(&admin->list); sparx5_vcap_admin_free(admin); } + mutex_destroy(&ctrl->lock); kfree(ctrl); } diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 0fdb5e363bad..ff86cde11a32 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -934,6 +934,16 @@ static bool vcap_rule_exists(struct vcap_control *vctrl, u32 id) return false; } +void vcap_lock(struct vcap_admin *admin) +{ + mutex_lock(&admin->vctrl->lock); +} + +void vcap_unlock(struct vcap_admin *admin) +{ + mutex_unlock(&admin->vctrl->lock); +} + /* Find a rule with a provided rule id return a locked vcap */ static struct vcap_rule_internal * vcap_get_locked_rule(struct vcap_control *vctrl, u32 id) @@ -943,11 +953,11 @@ vcap_get_locked_rule(struct vcap_control *vctrl, u32 id) /* Look for the rule id in all vcaps */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) if (ri->data.id == id) return ri; - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return NULL; } @@ -961,14 +971,14 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) /* Look for the rule id in all vcaps */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.cookie == cookie) { id = ri->data.id; break; } } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (id) return id; } @@ -985,11 +995,11 @@ int vcap_admin_rule_count(struct vcap_admin *admin, int cid) int count = 0; list_for_each_entry(elem, &admin->rules, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); if (elem->data.vcap_chain_id >= min_cid && elem->data.vcap_chain_id < max_cid) ++count; - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return count; } @@ -2266,7 +2276,7 @@ int vcap_add_rule(struct vcap_rule *rule) if (ret) return ret; /* Insert the new rule in the list of vcap rules */ - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); vcap_rule_set_state(ri); ret = vcap_insert_rule(ri, &move); @@ -2302,7 +2312,7 @@ int vcap_add_rule(struct vcap_rule *rule) goto out; } out: - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return ret; } EXPORT_SYMBOL_GPL(vcap_add_rule); @@ -2330,7 +2340,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, if (vctrl->vcaps[admin->vtype].rows == 0) return ERR_PTR(-EINVAL); - mutex_lock(&admin->lock); + vcap_lock(admin); /* Check if a rule with this id already exists */ if (vcap_rule_exists(vctrl, id)) { err = -EINVAL; @@ -2369,13 +2379,13 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, goto out_free; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return (struct vcap_rule *)ri; out_free: kfree(ri); out_unlock: - mutex_unlock(&admin->lock); + vcap_unlock(admin); return ERR_PTR(err); } @@ -2446,7 +2456,7 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id) return ERR_PTR(-ENOENT); rule = vcap_decode_rule(elem); - mutex_unlock(&elem->admin->lock); + vcap_unlock(elem->admin); return rule; } EXPORT_SYMBOL_GPL(vcap_get_rule); @@ -2483,7 +2493,7 @@ int vcap_mod_rule(struct vcap_rule *rule) err = vcap_write_counter(ri, &ctr); out: - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } EXPORT_SYMBOL_GPL(vcap_mod_rule); @@ -2570,7 +2580,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) admin->last_used_addr = elem->addr; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return err; } EXPORT_SYMBOL_GPL(vcap_del_rule); @@ -2585,7 +2595,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) if (ret) return ret; - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry_safe(ri, next_ri, &admin->rules, list) { vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size); list_del(&ri->list); @@ -2598,7 +2608,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) list_del(&eport->list); kfree(eport); } - mutex_unlock(&admin->lock); + vcap_unlock(admin); return 0; } @@ -3016,7 +3026,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl, continue; /* Found the admin, now find the offloadable rules */ - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { /* Is the rule in the lookup defined by the chain */ if (!(ri->data.vcap_chain_id >= chain && @@ -3034,7 +3044,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl, if (err) break; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (err) break; } @@ -3074,7 +3084,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl, continue; /* Found the admin, now find the rules on the chain */ - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.vcap_chain_id != chain) continue; @@ -3089,7 +3099,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl, if (err) break; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (err) break; } @@ -3133,9 +3143,9 @@ static int vcap_enable(struct vcap_control *vctrl, struct net_device *ndev, eport->cookie = cookie; eport->src_cid = src_cid; eport->dst_cid = dst_cid; - mutex_lock(&admin->lock); + vcap_lock(admin); list_add_tail(&eport->list, &admin->enabled); - mutex_unlock(&admin->lock); + vcap_unlock(admin); if (vcap_path_exist(vctrl, ndev, src_cid)) { /* Enable chained lookups */ @@ -3185,9 +3195,9 @@ static int vcap_disable(struct vcap_control *vctrl, struct net_device *ndev, dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid); } - mutex_lock(&found->lock); + vcap_lock(found); list_del(&eport->list); - mutex_unlock(&found->lock); + vcap_unlock(found); kfree(eport); return 0; } @@ -3270,9 +3280,9 @@ int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr) return -EINVAL; } - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); err = vcap_write_counter(ri, ctr); - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } @@ -3291,9 +3301,9 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr) return -EINVAL; } - mutex_lock(&ri->admin->lock); + vcap_lock(ri->admin); err = vcap_read_counter(ri, ctr); - mutex_unlock(&ri->admin->lock); + vcap_unlock(ri->admin); return err; } @@ -3395,7 +3405,7 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, /* Iterate all rules in each VCAP instance */ list_for_each_entry(admin, &vctrl->list, list) { - mutex_lock(&admin->lock); + vcap_lock(admin); list_for_each_entry(ri, &admin->rules, list) { if (ri->data.cookie != cookie) continue; @@ -3412,12 +3422,12 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl, if (err) goto unlock; } - mutex_unlock(&admin->lock); + vcap_unlock(admin); } return err; unlock: - mutex_unlock(&admin->lock); + vcap_unlock(admin); return err; } EXPORT_SYMBOL_GPL(vcap_get_rule_count_by_cookie); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h index 6069ad95c27e..05b4b02e59ef 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -164,7 +164,7 @@ struct vcap_admin { struct list_head list; /* for insertion in vcap_control */ struct list_head rules; /* list of rules */ struct list_head enabled; /* list of enabled ports */ - struct mutex lock; /* control access to rules */ + struct vcap_control *vctrl; /* the control instance owning this vcap */ enum vcap_type vtype; /* type of vcap */ int vinst; /* instance number within the same type */ int first_cid; /* first chain id in this vcap */ @@ -275,6 +275,7 @@ struct vcap_control { const struct vcap_info *vcaps; /* client supplied vcap models */ const struct vcap_statistics *stats; /* client supplied vcap stats */ struct list_head list; /* list of vcap instances */ + struct mutex lock; /* serialize access to all vcap instances */ }; #endif /* __VCAP_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c index 59bfbda29bb3..e0c65c7ab23e 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -410,9 +410,9 @@ static int vcap_debugfs_show(struct seq_file *m, void *unused) }; int ret; - mutex_lock(&info->admin->lock); + vcap_lock(info->admin); ret = vcap_show_admin(info->vctrl, info->admin, &out); - mutex_unlock(&info->admin->lock); + vcap_unlock(info->admin); return ret; } DEFINE_SHOW_ATTRIBUTE(vcap_debugfs); @@ -427,9 +427,9 @@ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) }; int ret; - mutex_lock(&info->admin->lock); + vcap_lock(info->admin); ret = vcap_show_admin_raw(info->vctrl, info->admin, &out); - mutex_unlock(&info->admin->lock); + vcap_unlock(info->admin); return ret; } DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c index 9c9d38042125..ac2a3b8c4f32 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c @@ -243,10 +243,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) { /* Initialize the shared objects */ INIT_LIST_HEAD(&test_vctrl.list); + mutex_init(&test_vctrl.lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = &test_vctrl; list_add_tail(&admin->list, &test_vctrl.list); memset(test_updateaddr, 0, sizeof(test_updateaddr)); test_updateaddridx = 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index ce26ccbdccdf..83de384d3e3b 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -233,10 +233,11 @@ static void vcap_test_api_init(struct vcap_admin *admin) { /* Initialize the shared objects */ INIT_LIST_HEAD(&test_vctrl.list); + mutex_init(&test_vctrl.lock); INIT_LIST_HEAD(&admin->list); INIT_LIST_HEAD(&admin->rules); INIT_LIST_HEAD(&admin->enabled); - mutex_init(&admin->lock); + admin->vctrl = &test_vctrl; list_add_tail(&admin->list, &test_vctrl.list); memset(test_updateaddr, 0, sizeof(test_updateaddr)); test_updateaddridx = 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h index 844bdf6b5f45..b4057fbe3d18 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -50,6 +50,9 @@ struct vcap_stream_iter { /* Check that the control has a valid set of callbacks */ int vcap_api_check(struct vcap_control *ctrl); +/* Serialize access to the vcap instances of a control */ +void vcap_lock(struct vcap_admin *admin); +void vcap_unlock(struct vcap_admin *admin); /* Erase the VCAP cache area used or encoding and decoding */ void vcap_erase_cache(struct vcap_rule_internal *ri); -- cgit v1.2.3 From cb89f0c1aed02eb233c4271f76f830b37e222ff6 Mon Sep 17 00:00:00 2001 From: Malaya Kumar Rout Date: Sat, 4 Jul 2026 16:27:36 +0530 Subject: 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; } -- cgit v1.2.3 From 920f893f735e92ba3a1cd9256899a186b161928d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 3 Jul 2026 12:02:38 +0200 Subject: 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; /* -- cgit v1.2.3 From 742a87fa54ad7123bff41bd1aa149fef6929a7af Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 5 Jul 2026 16:07:13 +0800 Subject: 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. -- cgit v1.2.3 From aba30af07d4fe499b50209801eba9da8a815522f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 16:46:01 +0800 Subject: 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); -- cgit v1.2.3 From 3cda0dfe8c651dcbb9e38977905d3d3b1750c4ab Mon Sep 17 00:00:00 2001 From: Aleksandrova Alyona Date: Wed, 24 Jun 2026 17:48:46 +0300 Subject: 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) -- cgit v1.2.3 From 09780b4b4bdcc7848249dfc8e44b4d55aa388024 Mon Sep 17 00:00:00 2001 From: Michael Reeves Date: Mon, 29 Jun 2026 20:01:01 +1000 Subject: arm64: dts: apple: t8122: Fix I2C resources Correct the MMIO ranges and IRQs for T8122 I2C controllers. They were mistakenly incorrect in the original commit. This fix is required for i2c dependent hardware to function correctly, i.e. audio codecs. Tested on J613 (MacBook Air M3). Fixes: c65ab4905e58 ("arm64: dts: apple: Initial t8122 (M3) device trees") Signed-off-by: Michael Reeves Link: https://patch.msgid.link/20260629-t8122-i2c-fix-v1-1-cec044001550@gmail.com Signed-off-by: Sven Peter --- arch/arm64/boot/dts/apple/t8122.dtsi | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/arm64/boot/dts/apple/t8122.dtsi b/arch/arm64/boot/dts/apple/t8122.dtsi index c6196225e96e..1ee61c5b3409 100644 --- a/arch/arm64/boot/dts/apple/t8122.dtsi +++ b/arch/arm64/boot/dts/apple/t8122.dtsi @@ -186,12 +186,12 @@ /* Required to get >32-bit DMA via DARTs */ dma-ranges = <0 0 0 0 0xffffffff 0xffffc000>; - i2c0: i2c@235010000 { + i2c0: i2c@2a1010000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x35010000 0x0 0x4000>; + reg = <0x2 0xa1010000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c0_pins>; pinctrl-names = "default"; #address-cells = <0x1>; @@ -200,12 +200,12 @@ status = "disabled"; }; - i2c1: i2c@235014000 { + i2c1: i2c@2a1014000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x35014000 0x0 0x4000>; + reg = <0x2 0xa1014000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c1_pins>; pinctrl-names = "default"; #address-cells = <0x1>; @@ -214,12 +214,12 @@ status = "disabled"; }; - i2c2: i2c@235018000 { + i2c2: i2c@2a1018000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x35018000 0x0 0x4000>; + reg = <0x2 0xa1018000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c2_pins>; pinctrl-names = "default"; #address-cells = <0x1>; @@ -228,12 +228,12 @@ status = "disabled"; }; - i2c3: i2c@23501c000 { + i2c3: i2c@2a101c000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x3501c000 0x0 0x4000>; + reg = <0x2 0xa101c000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c3_pins>; pinctrl-names = "default"; #address-cells = <0x1>; @@ -242,12 +242,12 @@ status = "disabled"; }; - i2c4: i2c@235020000 { + i2c4: i2c@2a1020000 { compatible = "apple,t8122-i2c", "apple,t8103-i2c"; - reg = <0x2 0x35020000 0x0 0x4000>; + reg = <0x2 0xa1020000 0x0 0x4000>; clocks = <&clkref>; interrupt-parent = <&aic>; - interrupts = ; + interrupts = ; pinctrl-0 = <&i2c4_pins>; pinctrl-names = "default"; #address-cells = <0x1>; -- cgit v1.2.3 From 1002500f54b5f60faa6a03636c7865fd4db53ad6 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 28 Jun 2026 20:25:52 -0700 Subject: lib/crypto: md5: Remove support for md5_mod_init_arch() No definitions of md5_mod_init_arch() remain, so remove the code that handles it. Link: https://patch.msgid.link/20260629032552.26100-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- lib/crypto/md5.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/crypto/md5.c b/lib/crypto/md5.c index 6bf130cfbbf9..3d2b017a0525 100644 --- a/lib/crypto/md5.c +++ b/lib/crypto/md5.c @@ -298,19 +298,5 @@ void hmac_md5_usingrawkey(const u8 *raw_key, size_t raw_key_len, } EXPORT_SYMBOL_GPL(hmac_md5_usingrawkey); -#ifdef md5_mod_init_arch -static int __init md5_mod_init(void) -{ - md5_mod_init_arch(); - return 0; -} -subsys_initcall(md5_mod_init); - -static void __exit md5_mod_exit(void) -{ -} -module_exit(md5_mod_exit); -#endif - MODULE_DESCRIPTION("MD5 and HMAC-MD5 library functions"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 5b4d8e11ba68cd9697de49861a818c75f21e463d Mon Sep 17 00:00:00 2001 From: Mehdi Hassan Date: Sat, 4 Jul 2026 02:35:06 +0000 Subject: 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; } -- cgit v1.2.3 From 92ea163c773cb4d0d5eaf103ed80c49d6758b96f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sat, 13 Jun 2026 18:16:33 -0400 Subject: 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); -- cgit v1.2.3 From 8669a550c752d86baebc5fdc83b8ff35c4372c0e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 4 Jul 2026 09:46:09 +0200 Subject: batman-adv: clean untagged VLAN on netdev registration failure When an mesh interface is registered, it creates an untagged struct batadv_meshif_vlan on top of it via the NETDEV_REGISTER notifier. But in this process, another receiver of this notification can veto the registration. The netdev registration will be aborted because of this veto. The register_netdevice() call will try to clean up the net_device using unregister_netdevice_queue() - which only uses the .priv_destructor to free private resources. In this situation, .dellink will not be called. The cleanup of the untagged batadv_meshif_vlan must thefore be done in the destructor to avoid a leak of this object. Cc: stable@vger.kernel.org Fixes: 5d2c05b21337 ("batman-adv: add per VLAN interface attribute framework") Signed-off-by: Sven Eckelmann --- net/batman-adv/main.c | 8 ++++++++ net/batman-adv/mesh-interface.c | 13 ++----------- net/batman-adv/mesh-interface.h | 2 ++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 8844e40e6a80..67bed3ee77e7 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -259,6 +259,7 @@ err_orig: void batadv_mesh_free(struct net_device *mesh_iface) { struct batadv_priv *bat_priv = netdev_priv(mesh_iface); + struct batadv_meshif_vlan *vlan; WRITE_ONCE(bat_priv->mesh_state, BATADV_MESH_DEACTIVATING); @@ -273,6 +274,13 @@ void batadv_mesh_free(struct net_device *mesh_iface) batadv_mcast_free(bat_priv); + /* destroy the "untagged" VLAN */ + vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); + if (vlan) { + batadv_meshif_destroy_vlan(bat_priv, vlan); + batadv_meshif_vlan_put(vlan); + } + /* Free the TT and the originator tables only after having terminated * all the other depending components which may use these structures for * their purposes. diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index 0b75234521b6..fbfd99268de4 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -595,8 +595,8 @@ int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) * @bat_priv: the bat priv with all the mesh interface information * @vlan: the object to remove */ -static void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, - struct batadv_meshif_vlan *vlan) +void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_meshif_vlan *vlan) { /* explicitly remove the associated TT local entry because it is marked * with the NOPURGE flag @@ -1091,22 +1091,13 @@ static int batadv_meshif_newlink(struct net_device *dev, static void batadv_meshif_destroy_netlink(struct net_device *mesh_iface, struct list_head *head) { - struct batadv_priv *bat_priv = netdev_priv(mesh_iface); struct batadv_hard_iface *hard_iface; - struct batadv_meshif_vlan *vlan; while (!list_empty(&mesh_iface->adj_list.lower)) { hard_iface = netdev_adjacent_get_private(mesh_iface->adj_list.lower.next); batadv_hardif_disable_interface(hard_iface); } - /* destroy the "untagged" VLAN */ - vlan = batadv_meshif_vlan_get(bat_priv, BATADV_NO_FLAGS); - if (vlan) { - batadv_meshif_destroy_vlan(bat_priv, vlan); - batadv_meshif_vlan_put(vlan); - } - unregister_netdevice_queue(mesh_iface, head); } diff --git a/net/batman-adv/mesh-interface.h b/net/batman-adv/mesh-interface.h index 53756c5a45e0..5e1e83e04ffb 100644 --- a/net/batman-adv/mesh-interface.h +++ b/net/batman-adv/mesh-interface.h @@ -21,6 +21,8 @@ void batadv_interface_rx(struct net_device *mesh_iface, bool batadv_meshif_is_valid(const struct net_device *net_dev); extern struct rtnl_link_ops batadv_link_ops; int batadv_meshif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid); +void batadv_meshif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_meshif_vlan *vlan); void batadv_meshif_vlan_release(struct kref *ref); struct batadv_meshif_vlan *batadv_meshif_vlan_get(struct batadv_priv *bat_priv, unsigned short vid); -- cgit v1.2.3 From 27c7d40008231ae4140d35501b60087a9de2d2c3 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 21:06:23 +0200 Subject: batman-adv: tt: avoid request storms during pending request batadv_send_tt_request() allocates a tt_req_node when none exists for the destination originator node. This should prevent that a multiple TT requests are send at the same time to an originator. But if allocation of the send buffer failed, this request must be cleaned up again. But indicator for such a failure is "ret == false". But the actual implementation is checking for "ret == true". The check must be inverted to not loose the information about the TT request directly after it was attempted to be sent out. This should avoid potential request storms. Cc: stable@vger.kernel.org Fixes: 335fbe0f5d25 ("batman-adv: tvlv - convert tt query packet to use tvlv unicast packets") Signed-off-by: Sven Eckelmann --- net/batman-adv/translation-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 4bfad36a4b70..aae72015645a 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2971,7 +2971,7 @@ static bool batadv_send_tt_request(struct batadv_priv *bat_priv, out: batadv_hardif_put(primary_if); - if (ret && tt_req_node) { + if (!ret && tt_req_node) { spin_lock_bh(&bat_priv->tt.req_list_lock); if (!hlist_unhashed(&tt_req_node->list)) { hlist_del_init(&tt_req_node->list); -- cgit v1.2.3 From 7a581d9aaba8c82bd6177fa36b2588eea77f6e2b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 22:27:13 +0200 Subject: batman-adv: tt: prevent TVLV OOB check overflow A TT unicast TVLV contains the number of VLANs stored in it. This number is an u16 and gets multiplied by the size of the struct batadv_tvlv_tt_vlan_data (8 bytes). The size can therefore overflow the u16 used to store the tt_vlan_len. All additional safety checks to prevent out-of-bounds access of the TVLV buffer are invalid due to this overflow. Using size_t prevents this overflow and ensures that the safety checks compare against the actual buffer requirements. Cc: stable@vger.kernel.org Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") Signed-off-by: Sven Eckelmann --- net/batman-adv/translation-table.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index aae72015645a..dae5e1d8c038 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -4033,7 +4033,8 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv, u16 tvlv_value_len) { struct batadv_tvlv_tt_data *tt_data; - u16 tt_vlan_len, tt_num_entries; + u16 tt_num_entries; + size_t tt_vlan_len; char tt_flag; bool ret; -- cgit v1.2.3 From 6b628425aed49a1c7a4ffc997583840fc582d32b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 20:28:31 +0200 Subject: batman-adv: frag: free unfragmentable packet The caller of batadv_frag_send_packet() assume that the skb provided to the function are always consumed. But the pre-check for an empty payload or the zero fragment size returned an error without any further actions. A failed pre-check must use the same error handling code as the rest of the function. Cc: stable@vger.kernel.org Fixes: ee75ed88879a ("batman-adv: Fragment and send skbs larger than mtu") Signed-off-by: Sven Eckelmann --- net/batman-adv/fragmentation.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 8a006a0473a8..13d4689d332d 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -518,8 +518,10 @@ int batadv_frag_send_packet(struct sk_buff *skb, mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE); max_fragment_size = mtu - header_size; - if (skb->len == 0 || max_fragment_size == 0) - return -EINVAL; + if (skb->len == 0 || max_fragment_size == 0) { + ret = -EINVAL; + goto free_skb; + } num_fragments = (skb->len - 1) / max_fragment_size + 1; max_fragment_size = (skb->len - 1) / num_fragments + 1; -- cgit v1.2.3 From 353d2c1d5492e53ae34f490a84494124dc3d3531 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 21:04:03 +0200 Subject: batman-adv: frag: fix primary_if leak on failed linearization If the skb has a frag_list, it must be linearized before it can be split using skb_split(). But when this step failed, it must not only free the skb but also take care of the reference to the already found primary_if. Cc: stable@vger.kernel.org Reported-by: Sashiko Fixes: a063f2fba3fa ("batman-adv: Don't skb_split skbuffs with frag_list") Signed-off-by: Sven Eckelmann --- net/batman-adv/fragmentation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 13d4689d332d..2e20a2cb64cb 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -547,7 +547,7 @@ int batadv_frag_send_packet(struct sk_buff *skb, */ if (skb_has_frag_list(skb) && __skb_linearize(skb)) { ret = -ENOMEM; - goto free_skb; + goto put_primary_if; } /* Create one header to be copied to all fragments */ -- cgit v1.2.3 From 38eaed28e250895d56f4b7989bd65479a511c5c3 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 3 Jul 2026 20:47:45 +0200 Subject: batman-adv: mcast: avoid OOB read of num_dests header Before the access to struct batadv_tvlv_mcast_tracker's num_dests, it is attempted to check whether enough space is actually in the network header. But instead of using offsetofend() to check for the whole size (2) which must be accessible, offsetof() of is called. The latter is always returning 0. The comparison with the network header length will always return that enough data is available - even when only 1 or 0 bytes are accessible. Instead of using offsetofend(), use the more common check for the whole header. Cc: stable@vger.kernel.org Fixes: 07afe1ba288c ("batman-adv: mcast: implement multicast packet reception and forwarding") Signed-off-by: Sven Eckelmann --- net/batman-adv/multicast_forw.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/multicast_forw.c b/net/batman-adv/multicast_forw.c index b8668a80b94a..1404a3b7adfb 100644 --- a/net/batman-adv/multicast_forw.c +++ b/net/batman-adv/multicast_forw.c @@ -927,11 +927,11 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, { struct batadv_tvlv_mcast_tracker *mcast_tracker; struct batadv_neigh_node *neigh_node; - unsigned long offset, num_dests_off; struct sk_buff *nexthop_skb; unsigned char *skb_net_hdr; bool local_recv = false; unsigned int tvlv_len; + unsigned long offset; bool xmitted = false; u8 *dest, *next_dest; u16 num_dests; @@ -940,9 +940,8 @@ static int batadv_mcast_forw_packet(struct batadv_priv *bat_priv, /* (at least) TVLV part needs to be linearized */ SKB_LINEAR_ASSERT(skb); - /* check if num_dests is within skb length */ - num_dests_off = offsetof(struct batadv_tvlv_mcast_tracker, num_dests); - if (num_dests_off > skb_network_header_len(skb)) + /* check if batadv_tvlv_mcast_tracker header is within skb length */ + if (sizeof(*mcast_tracker) > skb_network_header_len(skb)) return -EINVAL; skb_net_hdr = skb_network_header(skb); -- cgit v1.2.3 From 98052bdaf6ac1639a63ffc10244eeeab1f62ed2b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 2 Jul 2026 19:32:40 +0200 Subject: batman-adv: dat: fix tie-break for candidate selection The original version of the candidate selection for DAT attempted to compare both candidate and max_orig_node to identify which has the smaller MAC address. This comparison is required as tie-break when a hash collision happened. But the used function returned 0 when the function was not equal and a non-zero value when it was equal. As result, the actually selected node was dependent on the order of entries in the orig_hash and not actually on the mac addresses. The last originator in the hash collision would always win. To have a proper ordering, it must diff the actual MAC address bytes and reject the candidate when the diff is not smaller than 0. Cc: stable@vger.kernel.org Fixes: 785ea1144182 ("batman-adv: Distributed ARP Table - create DHT helper functions") Signed-off-by: Sven Eckelmann --- net/batman-adv/distributed-arp-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index c40c9e02391b..a6fe4820f65b 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -546,7 +546,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, * the one with the lowest address */ if (tmp_max == max && max_orig_node && - batadv_compare_eth(candidate->orig, max_orig_node->orig)) + memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0) goto out; ret = true; -- cgit v1.2.3 From 2538bd3cd1ff5af655908469544ac7b7ae259386 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 11:01:17 +0800 Subject: 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; -- cgit v1.2.3 From 430ea57d6daf765e88f90046afbfd1e071cb7200 Mon Sep 17 00:00:00 2001 From: Chen YanJun Date: Wed, 1 Jul 2026 11:31:52 +0800 Subject: 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; -- cgit v1.2.3 From 136992de9bb91871084ae52d172610541c76e4d2 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Thu, 2 Jul 2026 01:05:16 +0000 Subject: 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; } -- cgit v1.2.3 From f38f8cce2f7e79775b3db7e8a5eacda04ac908e4 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Fri, 3 Jul 2026 05:19:32 +0000 Subject: 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); -- cgit v1.2.3 From a707e4127c0f893c7a7703500ab56297a5bd2d51 Mon Sep 17 00:00:00 2001 From: Rafael Beims Date: Wed, 10 Jun 2026 12:00:18 -0300 Subject: 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); -- cgit v1.2.3 From 44494b0d1d16e76ae805817579eacc801b10ed37 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jun 2026 15:00:54 +0200 Subject: 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 @@ err: 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 @@ err: } } - 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); -- cgit v1.2.3 From d78a407bad6f500884a8606aea1a5a9207be4030 Mon Sep 17 00:00:00 2001 From: Rafael Beims Date: Fri, 12 Jun 2026 09:25:46 -0300 Subject: 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; -- cgit v1.2.3 From 536fb3d739d75a03cb318c0c6fe799425cfea501 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 15:31:04 +0800 Subject: 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); -- cgit v1.2.3 From ea588e4c7484ea883d3dab308f1f2c2f0f51f7d6 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Mon, 6 Jul 2026 02:19:31 +0800 Subject: 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 @@ sticky: 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++; -- cgit v1.2.3 From c845febafd92b2056abc0af541c1ad85785f1353 Mon Sep 17 00:00:00 2001 From: Malaya Kumar Rout Date: Sun, 5 Jul 2026 22:40:38 +0530 Subject: 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; } -- cgit v1.2.3 From db4a79713ed8e252d5e4edf6eaaa80948b6855a2 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sat, 4 Jul 2026 10:40:54 +0200 Subject: 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; -- cgit v1.2.3 From a0a558ca7e75b49e71f8c545c30e8c005e6e4e2f Mon Sep 17 00:00:00 2001 From: Shigeru Yoshida Date: Wed, 1 Jul 2026 01:46:20 +0900 Subject: qede: fix off-by-one in BD ring consumption on build_skb failure qede_rx_build_skb() and qede_tpa_rx_build_skb() do not check for a NULL return from qede_build_skb(). When it returns NULL under memory pressure, the functions still consume a BD from the ring before returning NULL. The callers then recycle additional BDs, resulting in one extra BD being consumed (off-by-one). This desynchronizes the BD ring, which can corrupt DMA page reference counts and lead to SLUB freelist corruption. Commit 4e910dbe3650 ("qede: confirm skb is allocated before using") added a NULL check inside qede_build_skb() to prevent a NULL pointer dereference, but did not address the missing NULL checks in the callers, making this off-by-one reachable. Fix this by adding NULL checks for the return value of qede_build_skb() in both qede_rx_build_skb() and qede_tpa_rx_build_skb(), returning NULL immediately before any BD ring manipulation. Fixes: 8a8633978b84 ("qede: Add build_skb() support.") Signed-off-by: Shigeru Yoshida Reviewed-by: Jamie Bainbridge Link: https://patch.msgid.link/20260630164623.3152625-1-syoshida@redhat.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/qlogic/qede/qede_fp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index 33e18bb69774..c11e0d8f98aa 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev, struct sk_buff *skb; skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; + bd->page_offset += rxq->rx_buf_seg_size; if (bd->page_offset == PAGE_SIZE) { @@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev, } skb = qede_build_skb(rxq, bd, len, pad); + if (unlikely(!skb)) + return NULL; if (unlikely(qede_realloc_rx_buffer(rxq, bd))) { /* Incr page ref count to reuse on allocation failure so -- cgit v1.2.3 From dd6a23bac306b7aa322e0aaccb60c6e32a198fb3 Mon Sep 17 00:00:00 2001 From: Nirmoy Das Date: Tue, 30 Jun 2026 09:51:57 -0700 Subject: selftests: net: make busywait timeout clock portable loopy_wait() expects millisecond timestamps. However, Ubuntu Resolute can use uutils date, where `date -u +%s%3N` returns seconds plus full nanoseconds instead of a 3-digit millisecond field. This makes busywait expire too early and can make vlan_bridge_binding.sh read a stale operstate. Fixes: 25ae948b4478 ("selftests/net: add lib.sh") Cc: stable@vger.kernel.org # 6.8+ Link: https://github.com/uutils/coreutils/issues/11658 Signed-off-by: Nirmoy Das Link: https://patch.msgid.link/20260630165157.3814871-1-nirmoyd@nvidia.com Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/lib.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh index b3827b43782b..d46d2cec89e4 100644 --- a/tools/testing/selftests/net/lib.sh +++ b/tools/testing/selftests/net/lib.sh @@ -70,12 +70,33 @@ ksft_exit_status_merge() $ksft_xfail $ksft_pass $ksft_skip $ksft_fail } +timestamp_ms() +{ + local now + local seconds + local nanoseconds + + now=$(date -u +%s:%N) || return + seconds=${now%:*} + nanoseconds=${now#*:} + + if [[ $nanoseconds =~ ^[0-9]+$ ]]; then + nanoseconds=${nanoseconds:0:9} + else + nanoseconds=0 + fi + + echo $((seconds * 1000 + 10#$nanoseconds / 1000000)) +} + loopy_wait() { local sleep_cmd=$1; shift local timeout_ms=$1; shift + local start_time + local current_time - local start_time="$(date -u +%s%3N)" + start_time=$(timestamp_ms) || return while true do local out @@ -84,7 +105,7 @@ loopy_wait() return 0 fi - local current_time="$(date -u +%s%3N)" + current_time=$(timestamp_ms) || return if ((current_time - start_time > timeout_ms)); then echo -n "$out" return 1 -- cgit v1.2.3 From f0f1887a9e30712a1df03e152dce6fb91344b1f3 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 30 Jun 2026 10:41:09 -0700 Subject: net: qualcomm: rmnet: validate MAP frame length before ingress parsing When ingress deaggregation is disabled, rmnet_map_ingress_handler() passes the skb straight to __rmnet_map_ingress_handler(), skipping the length validation that rmnet_map_deaggregate() performs on the aggregated path. The parser then dereferences the MAP header and csum header/trailer based on the on-wire pkt_len without checking skb->len, so a short frame is read out of bounds: BUG: KASAN: slab-out-of-bounds in rmnet_map_checksum_downlink_packet Read of size 1 at addr ffff88801118ed00 by task exploit/147 Call Trace: ... rmnet_map_checksum_downlink_packet (drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c:413) __rmnet_map_ingress_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:96) rmnet_rx_handler (drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c:129) __netif_receive_skb_core.constprop.0 (net/core/dev.c:6089) netif_receive_skb (net/core/dev.c:6460) tun_get_user (drivers/net/tun.c:1955) tun_chr_write_iter (drivers/net/tun.c:2001) vfs_write (fs/read_write.c:688) ksys_write (fs/read_write.c:740) do_syscall_64 (arch/x86/entry/syscall_64.c:94) ... Factor that validation out of rmnet_map_deaggregate() into rmnet_map_validate_packet_len() and run it on the no-aggregation path too. The MAP header is bounds-checked first, since this path can receive a frame shorter than the header. Fixes: ceed73a2cf4a ("drivers: net: ethernet: qualcomm: rmnet: Initial implementation") Reported-by: Weiming Shi Suggested-by: Subash Abhinov Kasiviswanathan Signed-off-by: Xiang Mei Reviewed-by: Subash Abhinov Kasiviswanathan Link: https://patch.msgid.link/20260630174110.2003121-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- .../net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 5 +- drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h | 1 + .../net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 72 ++++++++++++---------- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index 9f3479500f85..d055a2628d8c 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb, consume_skb(skb); } else { - __rmnet_map_ingress_handler(skb, port); + if (rmnet_map_validate_packet_len(skb, port)) + __rmnet_map_ingress_handler(skb, port); + else + kfree_skb(skb); } } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h index b70284095568..60ca8b780c88 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port); void rmnet_map_tx_aggregate_exit(struct rmnet_port *port); void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size, u32 count, u32 time); +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port); #endif /* _RMNET_MAP_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index 8b4640c5d61e..305ae15ae8f3 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -333,54 +333,62 @@ done: return map_header; } -/* Deaggregates a single packet - * A whole new buffer is allocated for each portion of an aggregated frame. - * Caller should keep calling deaggregate() on the source skb until 0 is - * returned, indicating that there are no more packets to deaggregate. Caller - * is responsible for freeing the original skb. - */ -struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, - struct rmnet_port *port) +u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port) { struct rmnet_map_v5_csum_header *next_hdr = NULL; struct rmnet_map_header *maph; void *data = skb->data; - struct sk_buff *skbn; - u8 nexthdr_type; u32 packet_len; - if (skb->len == 0) - return NULL; + if (skb->len < sizeof(*maph)) + return 0; maph = (struct rmnet_map_header *)skb->data; + + /* Some hardware can send us empty frames. Catch them */ + if (!maph->pkt_len) + return 0; + packet_len = ntohs(maph->pkt_len) + sizeof(*maph); if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { packet_len += sizeof(struct rmnet_map_dl_csum_trailer); - } else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) { - if (!(maph->flags & MAP_CMD_FLAG)) { - packet_len += sizeof(*next_hdr); - if (maph->flags & MAP_NEXT_HEADER_FLAG) - next_hdr = data + sizeof(*maph); - else - /* Mapv5 data pkt without csum hdr is invalid */ - return NULL; - } + } else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) && + !(maph->flags & MAP_CMD_FLAG)) { + /* Mapv5 data pkt without csum hdr is invalid */ + if (!(maph->flags & MAP_NEXT_HEADER_FLAG)) + return 0; + + packet_len += sizeof(*next_hdr); + next_hdr = data + sizeof(*maph); } - if (((int)skb->len - (int)packet_len) < 0) - return NULL; + if (skb->len < packet_len) + return 0; - /* Some hardware can send us empty frames. Catch them */ - if (!maph->pkt_len) - return NULL; + if (next_hdr && + u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) != + RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD) + return 0; - if (next_hdr) { - nexthdr_type = u8_get_bits(next_hdr->header_info, - MAPV5_HDRINFO_HDR_TYPE_FMASK); - if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD) - return NULL; - } + return packet_len; +} + +/* Deaggregates a single packet + * A whole new buffer is allocated for each portion of an aggregated frame. + * Caller should keep calling deaggregate() on the source skb until 0 is + * returned, indicating that there are no more packets to deaggregate. Caller + * is responsible for freeing the original skb. + */ +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, + struct rmnet_port *port) +{ + struct sk_buff *skbn; + u32 packet_len; + + packet_len = rmnet_map_validate_packet_len(skb, port); + if (!packet_len) + return NULL; skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC); if (!skbn) -- cgit v1.2.3 From 8b519cbcabe836a441369fbec1a8a6518a709251 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 1 Jul 2026 12:19:12 -0400 Subject: net/sched: act_pedit: fix TOCTOU heap OOB write in tc offload There is a TOCTOU race condition in flower lockless approach between sizing a flow_rule buffer and filling it. zdi-disclosures@trendmicro.com reports: The cls_flower classifier operates with TCF_PROTO_OPS_DOIT_UNLOCKED (fl_change runs without RTNL), while RTM_NEWACTION holds RTNL, so the independent locking domains make the race reachable in practice. KASAN confirms: BUG: KASAN: slab-out-of-bounds in tcf_pedit_offload_act_setup+0x81b/0x930 Write of size 4 at addr ffff888001f27520 by task poc-toctou/312 The buggy address is located 0 bytes to the right of allocated 288-byte region [ffff888001f27400, ffff888001f27520) (cache kmalloc-512) Note: The result is a heap OOB write attacker-controlled content into the adjacent slab object (requires CAP_NET_ADMIN). The fix introduces reading tcfp_nkeys under act->tcfa_lock in all places using a new tcf_pedit_nkeys_locked() which replaces the old tcf_pedit_nkeys(). Additionally we close the remaining TOCTOU window between the sizing read and the fill reads by more careful accounting. Rather than silently truncating the key count, which leads to incorrect action semantics offloaded to hardware and secondary OOB writes if the remaining capacity is zero or consumed by prior actions, we enforce remaining capacity checks and return -ENOSPC if the required space exceeds the remaining capacity. Fixes: 71d0ed7079df ("net/act_pedit: Support using offset relative to the conventional network headers") Reported-by: zdi-disclosures@trendmicro.com Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260701161912.125355-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- include/net/tc_act/tc_pedit.h | 18 ++++++++---------- net/sched/act_api.c | 13 +++++++++---- net/sched/act_pedit.c | 13 +++++++++++-- net/sched/cls_api.c | 22 +++++++++++++++++----- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h index cb7b82f2cbc7..97754ea0a827 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -37,17 +37,15 @@ static inline bool is_tcf_pedit(const struct tc_action *a) return false; } -static inline int tcf_pedit_nkeys(const struct tc_action *a) +/* Must be called with act->tcfa_lock held to ensure consistency of parallel + * reads of the same action's pedit keys (e.g. flow_offload count vs fill). + * Note, this is only used for pedit offload. + */ +static inline int tcf_pedit_nkeys_locked(const struct tc_action *a) { - struct tcf_pedit_parms *parms; - int nkeys; - - rcu_read_lock(); - parms = to_pedit_parms(a); - nkeys = parms->tcfp_nkeys; - rcu_read_unlock(); - - return nkeys; + lockdep_assert_held(&a->tcfa_lock); + return rcu_dereference_protected(to_pedit(a)->parms, + lockdep_is_held(&a->tcfa_lock))->tcfp_nkeys; } static inline u32 tcf_pedit_htype(const struct tc_action *a, int index) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index b68be143a067..f141634df214 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -148,10 +148,15 @@ static void offload_action_hw_count_dec(struct tc_action *act, static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act) { - if (is_tcf_pedit(act)) - return tcf_pedit_nkeys(act); - else - return 1; + unsigned int count; + + if (is_tcf_pedit(act)) { + spin_lock_bh(&act->tcfa_lock); + count = tcf_pedit_nkeys_locked(act); + spin_unlock_bh(&act->tcfa_lock); + return count; + } + return 1; } static bool tc_act_skip_hw(u32 flags) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 0d652dea4a69..d4d47a9921f4 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -567,9 +567,18 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, { if (bind) { struct flow_action_entry *entry = entry_data; + int nkeys = tcf_pedit_nkeys_locked(act); int k; - for (k = 0; k < tcf_pedit_nkeys(act); k++) { + /* If the required keys exceed the remaining capacity return + * -ENOSPC to abort the offload and fallback to software. + */ + if (nkeys > *index_inc) { + NL_SET_ERR_MSG_MOD(extack, "Not enough space to offload all pedit keys"); + return -ENOSPC; + } + + for (k = 0; k < nkeys; k++) { switch (tcf_pedit_cmd(act, k)) { case TCA_PEDIT_KEY_EX_CMD_SET: entry->id = FLOW_ACTION_MANGLE; @@ -606,7 +615,7 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data, return -EOPNOTSUPP; } - for (k = 1; k < tcf_pedit_nkeys(act); k++) { + for (k = 1; k < tcf_pedit_nkeys_locked(act); k++) { if (cmd != tcf_pedit_cmd(act, k)) { NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload"); return -EOPNOTSUPP; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 3e67600a4a1a..ffeea6db8337 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3886,12 +3886,21 @@ int tc_setup_action(struct flow_action *flow_action, entry = &flow_action->entries[j]; spin_lock_bh(&act->tcfa_lock); + + /* Abort the offload if we have exhausted the allocated capacity */ + if (j >= flow_action->num_entries) { + NL_SET_ERR_MSG_MOD(extack, "Flow action buffer overflow"); + err = -ENOSPC; + goto err_out_locked; + } + err = tcf_act_get_user_cookie(entry, act); if (err) goto err_out_locked; - index = 0; - err = tc_setup_offload_act(act, entry, &index, extack); + index = flow_action->num_entries - j; + err = tc_setup_offload_act(act, entry, &index, + extack); if (err) goto err_out_locked; @@ -3945,10 +3954,13 @@ unsigned int tcf_exts_num_actions(struct tcf_exts *exts) int i; tcf_exts_for_each_action(i, act, exts) { - if (is_tcf_pedit(act)) - num_acts += tcf_pedit_nkeys(act); - else + if (is_tcf_pedit(act)) { + spin_lock_bh(&act->tcfa_lock); + num_acts += tcf_pedit_nkeys_locked(act); + spin_unlock_bh(&act->tcfa_lock); + } else { num_acts++; + } } return num_acts; } -- cgit v1.2.3 From 9d160b35cc34a2ba8229d07651468a7848325135 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 30 Jun 2026 11:32:27 -0700 Subject: net/smc: fix UAF in smc_cdc_rx_handler() by pinning the socket smc_cdc_rx_handler() looks up the connection by token under the link group's conns_lock, drops the lock, and then dereferences conn and the smc_sock derived from it, ending in sock_hold(&smc->sk) inside smc_cdc_msg_recv(). No reference is held across the lock release. The only reference pinning the socket while the connection is discoverable in the link group is taken in smc_lgr_register_conn() (sock_hold) and dropped in __smc_lgr_unregister_conn() (sock_put), both under conns_lock. Once the handler drops conns_lock, a concurrent close() -> smc_release() -> smc_conn_free() -> smc_lgr_unregister_conn() can drop that reference and free the smc_sock, so the handler's later sock_hold() runs on freed memory: WARNING: lib/refcount.c:25 at refcount_warn_saturate Workqueue: rxe_wq do_work refcount_warn_saturate (lib/refcount.c:25) smc_cdc_msg_recv (net/smc/smc_cdc.c:430) smc_cdc_rx_handler (net/smc/smc_cdc.c:502) smc_wr_rx_tasklet_fn (net/smc/smc_wr.c:445) tasklet_action_common (kernel/softirq.c:938) handle_softirqs (kernel/softirq.c:622) Kernel panic - not syncing: panic_on_warn set Only SMC-R is affected. The SMC-D receive tasklet is stopped by tasklet_kill(&conn->rx_tsklet) in smc_conn_free() before the connection is unregistered, so it cannot run concurrently with the free. Take the socket reference while still holding conns_lock, so the registration reference can no longer be the last one, and drop it once the handler is done. Fixes: d7b0e37c1ac1 ("net/smc: restructure CDC message reception") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260630183227.2044998-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/smc/smc_cdc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 619b3bab3824..32d6d03df321 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -470,9 +470,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) { struct smc_link *link = (struct smc_link *)wc->qp->qp_context; struct smc_cdc_msg *cdc = buf; + struct smc_sock *smc = NULL; struct smc_connection *conn; struct smc_link_group *lgr; - struct smc_sock *smc; if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved)) return; /* short message */ @@ -483,21 +483,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) lgr = smc_get_lgr(link); read_lock_bh(&lgr->conns_lock); conn = smc_lgr_find_conn(ntohl(cdc->token), lgr); - read_unlock_bh(&lgr->conns_lock); - if (!conn || conn->out_of_sync) + if (!conn || conn->out_of_sync) { + read_unlock_bh(&lgr->conns_lock); return; + } smc = container_of(conn, struct smc_sock, conn); + sock_hold(&smc->sk); + read_unlock_bh(&lgr->conns_lock); if (cdc->prod_flags.failover_validation) { smc_cdc_msg_validate(smc, cdc, link); - return; + goto out; } if (smc_cdc_before(ntohs(cdc->seqno), conn->local_rx_ctrl.seqno)) /* received seqno is old */ - return; + goto out; smc_cdc_msg_recv(smc, cdc); +out: + sock_put(&smc->sk); } static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = { -- cgit v1.2.3 From 9e05e91a9a847ed57926414bd7c2c5e54d6c56c6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 1 Jul 2026 12:23:29 +0000 Subject: amt: fix size calculation in amt_get_size() amt_get_size() incorrectly used sizeof(struct iphdr) for the sizes of IFLA_AMT_DISCOVERY_IP, IFLA_AMT_REMOTE_IP, and IFLA_AMT_LOCAL_IP. These attributes contain IPv4 addresses (__be32), not full IP headers. Replace sizeof(struct iphdr) with sizeof(__be32) to avoid over-allocating netlink message space. Fixes: b9022b53adad ("amt: add control plane of amt interface") Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260701122329.3562825-1-edumazet@google.com Signed-off-by: Paolo Abeni --- drivers/net/amt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 724a8163a514..951dd10e192b 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -3301,9 +3301,9 @@ static size_t amt_get_size(const struct net_device *dev) nla_total_size(sizeof(__u16)) + /* IFLA_AMT_GATEWAY_PORT */ nla_total_size(sizeof(__u32)) + /* IFLA_AMT_LINK */ nla_total_size(sizeof(__u32)) + /* IFLA_MAX_TUNNELS */ - nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_DISCOVERY_IP */ - nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_REMOTE_IP */ - nla_total_size(sizeof(struct iphdr)); /* IFLA_AMT_LOCAL_IP */ + nla_total_size(sizeof(__be32)) + /* IFLA_AMT_DISCOVERY_IP */ + nla_total_size(sizeof(__be32)) + /* IFLA_AMT_REMOTE_IP */ + nla_total_size(sizeof(__be32)); /* IFLA_AMT_LOCAL_IP */ } static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev) -- cgit v1.2.3 From f72df3a4c33b64de3418ec74d1ad4f028e09d161 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:36:59 +0900 Subject: 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 @@ no_data_attr_special_case: !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 -- cgit v1.2.3 From 77dc384207d5fa63ba97c3bf3285fe1215a1cbf6 Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Thu, 2 Jul 2026 14:28:16 +0900 Subject: 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, -- cgit v1.2.3 From b8d6c528e9d57d263fee1a648409f84a68b2561d Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 10:31:44 +0900 Subject: 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; /* -- cgit v1.2.3 From 5b6eedd7cc2936f9238e852b553a1b326105bde8 Mon Sep 17 00:00:00 2001 From: Valeriy Yashnikov Date: Sat, 4 Jul 2026 19:38:57 +1000 Subject: 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; } -- cgit v1.2.3 From 06769b8f23b4b645b270c438649fff79768fb6fe Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Sun, 5 Jul 2026 19:14:09 +0800 Subject: 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; } -- cgit v1.2.3 From d97a36bae86a9a4021562ded2987f904e6bcb1d7 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 6 Jul 2026 12:00:00 +0900 Subject: 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 @@ found_it: 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 @@ found_it: 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 @@ found_it2: 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 @@ found_it2: if (name) { kfree(kaddr); iput(ia_vi); - return name->mref; + return ntfs_check_mref(name->mref); } ntfs_debug("Entry not found."); err = -ENOENT; -- cgit v1.2.3 From 0ebe8f625ab0520217a425d7cd366e4670484941 Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Mon, 6 Jul 2026 12:00:15 +0800 Subject: 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); -- cgit v1.2.3 From edf0730be33696a1bd142792830d392129e495cc Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 20 Jun 2026 00:25:42 +0800 Subject: 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); -- cgit v1.2.3 From 0d388f62031dbabcba0f44bb91b59f10e88cac17 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Sat, 20 Jun 2026 12:22:39 +0530 Subject: 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 -- cgit v1.2.3 From c6659f66d4ee4841aafae5659d2ef5e4c5c63cb6 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 Jun 2026 21:48:56 +0200 Subject: 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; -- cgit v1.2.3 From 10a2b430f8f06ae14b9590b6f6faa6b588ef0654 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 20 Jun 2026 21:45:18 -0500 Subject: 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); -- cgit v1.2.3 From 1d067abcd37062426c59ec73dbc4e87a63f33fea Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 21 Jun 2026 02:35:31 -0700 Subject: 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; } -- cgit v1.2.3 From 286e52a799fa158bdbd77da1426c4d93f9a6e7ad Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 21 Jun 2026 02:35:32 -0700 Subject: 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; } -- cgit v1.2.3 From aa6dcd5c8dd9ba1d7d0f60093bcda41c0d6d438d Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Mon, 22 Jun 2026 15:53:38 +0800 Subject: 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); } -- cgit v1.2.3 From 63c2391deefb31e1b801b7f32bd502ca4808639b Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 24 Jun 2026 16:53:43 +0800 Subject: 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); } } -- cgit v1.2.3 From 23b493d9dc5f00bba59347bd8ec7b044e26392a0 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:25:37 +0200 Subject: 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 --- drivers/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; -- cgit v1.2.3 From 2c51457d930f723e5f2903af90f5847f7df53f42 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Sat, 27 Jun 2026 00:58:30 +0800 Subject: 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); } -- cgit v1.2.3 From aa2eb62525188269cdd402a583b9a8ed94657ff0 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sat, 27 Jun 2026 16:30:28 +0800 Subject: 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); } -- cgit v1.2.3 From ebd6d37fa94bee929e0b4c9ca19fdf9b1dcf6cea Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 27 Jun 2026 17:05:10 -0700 Subject: 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); } -- cgit v1.2.3 From 843fe9bc583b7686ca68312ac9319c9240a73c03 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 1 Jul 2026 13:34:14 +0800 Subject: 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); } -- cgit v1.2.3 From 74e27cd1d98b546fdb276008a83708d062339661 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Wed, 1 Jul 2026 17:33:27 +0800 Subject: 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, -- cgit v1.2.3 From 2b0eab425e1f658d8fe1df7590e3b9af5959505e Mon Sep 17 00:00:00 2001 From: Peddolla Harshavardhan Reddy Date: Fri, 3 Jul 2026 13:55:23 +0530 Subject: 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); -- cgit v1.2.3 From 0a2581cbae9e442835f68d22044157db61cdf54d Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 3 Jul 2026 13:49:32 +0000 Subject: 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; -- cgit v1.2.3 From 13ff543e0b2c713aedeaadadde686686e949dc78 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 09:11:40 +0800 Subject: 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)); -- cgit v1.2.3 From d06a3e60c8fead962f08cf951eb1de7bd22dab76 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 09:12:30 +0800 Subject: 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); -- cgit v1.2.3 From 74ed3669f26803b1761c1f55403062bea44c3466 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 16:35:19 +0800 Subject: 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 -- cgit v1.2.3 From 8ecdeb8b8a33b22c597299043c0dcfce50beb9ea Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 16:48:24 +0800 Subject: 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); -- cgit v1.2.3 From 07a95ec2b54774201fdf4ef7ffb0ca2ab19ed29c Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Wed, 10 Jun 2026 19:22:09 +0800 Subject: 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; } -- cgit v1.2.3 From 57d503ce32eccfa7650065ca4c560f7e29a2e676 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 00:19:45 +0800 Subject: 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; } -- cgit v1.2.3 From 4e5a4641e7b4763656336b7891d01359aaf363cd Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 00:19:46 +0800 Subject: 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) -- cgit v1.2.3 From 293baeae9b2434a3e432629d7720b5603db2d77e Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 01:35:07 +0800 Subject: 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; } /** -- cgit v1.2.3 From 7f4b01812323443b55e4c65381c9dc851ff009e3 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:18:55 +0800 Subject: 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); -- cgit v1.2.3 From 172f06023669f0a96d32511669ff45c600731380 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:18:56 +0800 Subject: 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 -- cgit v1.2.3 From 41aa973eb05922848dded26875c55ef982ac1c49 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:36:57 +0800 Subject: 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; } -- cgit v1.2.3 From 36230936468f0ba4930e94aef496fc229d4bb951 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:37:04 +0800 Subject: 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 }, -- cgit v1.2.3 From 69ef6a7ec277f16d216be8da2b3cbe872786c999 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:37:11 +0800 Subject: 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 = -- cgit v1.2.3 From 57c05ce14fea03df01288fe1250f49197e161710 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 21:37:18 +0800 Subject: 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; -- cgit v1.2.3 From 035ed430ce6a2c35b01e211844a9f0a7643e57a4 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 12 Jun 2026 23:24:41 +0800 Subject: 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 -- cgit v1.2.3 From 4a360c6e18dfa9d70006c7247a6a8cc8dfe0d60f Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Sat, 13 Jun 2026 02:50:45 +0800 Subject: 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; -- cgit v1.2.3 From fbe0bb2b75eb3c61e8464486506253d1b471240b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 1 Jul 2026 22:58:35 +0900 Subject: 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; } } -- cgit v1.2.3 From 4b706360ffb7e459cb3d3edae30b06a584f6eddd Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 15:49:53 +0900 Subject: 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 @@ out: * @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 @@ out: * @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 @@ out: } #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 @@ out_err: 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 @@ out_err: 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); -- cgit v1.2.3 From a0b765030f6ba37aaac59618d4f39dac18d42757 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 1 Jul 2026 23:59:29 +0900 Subject: 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 @@ reconnected_fp: 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; -- cgit v1.2.3 From 216c5aba4ebde1a6e85d7831c0cf39a9a2ad7a38 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Wed, 1 Jul 2026 23:21:57 +0800 Subject: 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 @@ err_out: 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) -- cgit v1.2.3 From 9e8ad620ddfde5a5f4ef58372e3805e9388cb0f4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 19:37:53 +0900 Subject: 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); -- cgit v1.2.3 From faf8578c77f3d846aca9cd882c293e03eafcc6df Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 19:50:26 +0900 Subject: 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; -- cgit v1.2.3 From 1c5daa2ea924be89d78b5525510bcf3a3eb9735c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:07:25 +0900 Subject: 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 @@ out_err: 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 @@ out_err: } 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. -- cgit v1.2.3 From 1f12738b0ed7b89252271d71159c67e6349ef532 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:27:43 +0900 Subject: 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 @@ out_err: 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 @@ out_err: } 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); -- cgit v1.2.3 From 3e67423336f08a28e9efaac66e842210f9421484 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 20:40:41 +0900 Subject: 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 @@ out_err: 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; -- cgit v1.2.3 From f49bca41c12f9f79d39dbf0779d0c672d74b09fe Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 2 Jul 2026 21:16:50 +0900 Subject: 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) { -- cgit v1.2.3 From dce002f685e04e0c94e088cb66cb4021311cc6b5 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 5 Jul 2026 16:04:09 -0500 Subject: 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; -- cgit v1.2.3 From 521f39ca93cc43ce1b3eae8d44201f8f55dd9151 Mon Sep 17 00:00:00 2001 From: Sergey Shtylyov Date: Mon, 1 Jun 2026 17:49:01 +0300 Subject: 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 @@ err_setup_host: 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; } -- cgit v1.2.3 From 04f15d8f393037ed72876399882265604a370b2b Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Fri, 3 Jul 2026 14:02:10 +0100 Subject: 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), {} }; -- cgit v1.2.3 From b30973e8c3920ddfa9255a959b19057515b4e5e8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 30 Jun 2026 16:28:16 +0200 Subject: 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 */ }; -- cgit v1.2.3 From 4c9854ad3cdc7ce408453207f153bb910cc6f3c1 Mon Sep 17 00:00:00 2001 From: Tianze Shao Date: Wed, 1 Jul 2026 16:56:06 +0200 Subject: 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; -- cgit v1.2.3 From dbbb5bc5176e36b13aa22e2174ab4779c5ae1dca Mon Sep 17 00:00:00 2001 From: Syed Saba Kareem Date: Fri, 3 Jul 2026 18:02:47 +0530 Subject: 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"); -- cgit v1.2.3 From c35fac6481a1dbb2d114dd337b54a40799437546 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 2 Jun 2026 21:13:44 +0100 Subject: 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; } -- cgit v1.2.3 From 1b0d946d6f08bd39211385bc703a440911b41e46 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 13 Jun 2026 21:43:37 +0300 Subject: Bluetooth: hci_uart: clear HCI_UART_SENDING when write_work is canceled HCI_UART_SENDING bit in tx_state means write_work is pending and blocks queueing it again. Currently this bit is not cleared when canceling the work in hci_uart_close(), which blocks future writes when device is reopened later if write_work was pending. Fix by clearing HCI_UART_SENDING when canceling the work. Also make clearing of tx_skb safe by using disable_work_sync + enable_work instead of just cancel_work_sync. hci_uart_flush() purges the proto tx queue so we can cancel the pending write_work there, instead of doing it just in hci_uart_close(). Re-enable and possibly requeue the work after queue flush. Fixes: c1bb9336ae6b ("Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths") Link: https://lore.kernel.org/linux-bluetooth/07e0a28650773abec711ee492fdb1bf5d21a6c98.camel@iki.fi/ Cc: stable@vger.kernel.org Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/hci_ldisc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 47f4902b40b4..2ad42c3bbaac 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -239,6 +239,8 @@ static int hci_uart_flush(struct hci_dev *hdev) BT_DBG("hdev %p tty %p", hdev, tty); + disable_work_sync(&hu->write_work); + if (hu->tx_skb) { kfree_skb(hu->tx_skb); hu->tx_skb = NULL; } @@ -254,6 +256,14 @@ static int hci_uart_flush(struct hci_dev *hdev) percpu_up_read(&hu->proto_lock); + /* Resume TX. Also reschedule in case work was queued concurrently; + * this may schedule write_work although there's nothing to do. + */ + enable_work(&hu->write_work); + clear_bit(HCI_UART_SENDING, &hu->tx_state); + if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) + hci_uart_tx_wakeup(hu); + return 0; } @@ -271,12 +281,8 @@ static int hci_uart_open(struct hci_dev *hdev) /* Close device */ static int hci_uart_close(struct hci_dev *hdev) { - struct hci_uart *hu = hci_get_drvdata(hdev); - BT_DBG("hdev %p", hdev); - cancel_work_sync(&hu->write_work); - hci_uart_flush(hdev); hdev->flush = NULL; return 0; -- cgit v1.2.3 From a3b5f242997a3be7404112fd48784881560aea57 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 12 Jun 2026 11:27:56 +0800 Subject: 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); /* -- cgit v1.2.3 From ee5fb641c4ccac8406c668d3e947eb20ce44f233 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:23:19 +0800 Subject: 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; -- cgit v1.2.3 From e91da9053006da622b865e672a4a37439a3bb88b Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Wed, 1 Jul 2026 14:45:03 +0530 Subject: 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 | -- cgit v1.2.3 From 2b56757a9a7456825eb668fde92299e01c5e2721 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 1 Jul 2026 10:55:55 +0300 Subject: 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; -- cgit v1.2.3 From 005771c18c5b2c98cb4e7517661aea460990fd3f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Jun 2026 17:22:04 +0300 Subject: 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; } -- cgit v1.2.3 From 82ec992c404c3dc774c5e9f3d4aa858e97187675 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 1 Jul 2026 14:45:13 +0300 Subject: 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 --- drivers/gpu/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); } -- cgit v1.2.3 From eed3de2acf6aa5154d49098b026710b646db67ee Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 1 Jul 2026 10:30:30 +0300 Subject: 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, -- cgit v1.2.3 From d38eaf611839b85ade3dd3db309dbc8aaaaf0095 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 12 Jun 2026 10:21:09 -0400 Subject: Bluetooth: 6lowpan: Fix using chan->conn as indication to no remote netdev b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") don't reset the chan->conn to NULL anymore making the bt# netdev not be remove once the last l2cap_chan_del is removed. Instead of restoring the original behavior this remove the logic of keeping the interface after the last channel is removed because it never worked as intended and the l2cap_chan_del always detach its l2cap_conn which results in always removing the channel anyway. Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/6lowpan.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index cb1e329d66fd..962e0e885105 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -797,20 +797,10 @@ static void chan_close_cb(struct l2cap_chan *chan) struct lowpan_btle_dev *dev = NULL; struct lowpan_peer *peer; int err = -ENOENT; - bool last = false, remove = true; + bool last = false; BT_DBG("chan %p conn %p", chan, chan->conn); - if (chan->conn && chan->conn->hcon) { - if (!is_bt_6lowpan(chan->conn->hcon)) - return; - - /* If conn is set, then the netdev is also there and we should - * not remove it. - */ - remove = false; - } - spin_lock(&devices_lock); list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { @@ -837,10 +827,8 @@ static void chan_close_cb(struct l2cap_chan *chan) ifdown(dev->netdev); - if (remove) { - INIT_WORK(&entry->delete_netdev, delete_netdev); - schedule_work(&entry->delete_netdev); - } + INIT_WORK(&entry->delete_netdev, delete_netdev); + schedule_work(&entry->delete_netdev); } else { spin_unlock(&devices_lock); } -- cgit v1.2.3 From fa85d985f614bc3feb343000f14a1072e99b0df1 Mon Sep 17 00:00:00 2001 From: Samuel Page Date: Mon, 15 Jun 2026 16:09:22 +0100 Subject: Bluetooth: MGMT: Fix UAF of hci_conn_params in add_device_complete add_device_complete() runs from the hci_cmd_sync_work kworker, which holds only hci_req_sync_lock and *not* hci_dev_lock. It calls hci_conn_params_lookup() and then dereferences the returned object (params->flags) without taking hci_dev_lock: params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); ... device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0); hci_conn_params_lookup() walks hdev->le_conn_params and is documented to require hdev->lock. A concurrent MGMT_OP_REMOVE_DEVICE (remove_device()), which does run under hci_dev_lock, can call hci_conn_params_free() to list_del() and kfree() the very object the lookup returned, so the subsequent params->flags read touches freed memory [0]. Hold hci_dev_lock() across the hci_conn_params_lookup() and the read of params->flags (and the matching event emission) so the lookup result cannot be freed by a concurrent remove_device() before it is used, honouring the locking contract of hci_conn_params_lookup(). [0]: (trailing page/memory-state dump trimmed) BUG: KASAN: slab-use-after-free in add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 Read of size 1 at addr ffff000017ab26c1 by task kworker/u9:8/388 CPU: 1 UID: 0 PID: 388 Comm: kworker/u9:8 Not tainted 7.0.11 #20 PREEMPT Hardware name: linux,dummy-virt (DT) Workqueue: hci0 hci_cmd_sync_work Call trace: show_stack+0x2c/0x3c arch/arm64/kernel/stacktrace.c:499 (C) __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xb4/0xd4 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0x118/0x5d8 mm/kasan/report.c:482 kasan_report+0xb0/0xf4 mm/kasan/report.c:595 __asan_report_load1_noabort+0x20/0x2c mm/kasan/report_generic.c:378 add_device_complete+0x358/0x3d8 net/bluetooth/mgmt.c:7671 hci_cmd_sync_work+0x14c/0x240 net/bluetooth/hci_sync.c:334 process_one_work+0x628/0xd38 kernel/workqueue.c:3289 process_scheduled_works kernel/workqueue.c:3372 [inline] worker_thread+0x7a8/0xac0 kernel/workqueue.c:3453 kthread+0x39c/0x444 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:860 Allocated by task 3401: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_alloc_info+0x40/0x54 mm/kasan/generic.c:570 poison_kmalloc_redzone mm/kasan/common.c:398 [inline] __kasan_kmalloc+0xd4/0xd8 mm/kasan/common.c:415 kasan_kmalloc include/linux/kasan.h:263 [inline] __kmalloc_cache_noprof+0x1b0/0x458 mm/slub.c:5385 kmalloc_noprof include/linux/slab.h:950 [inline] kzalloc_noprof include/linux/slab.h:1188 [inline] hci_conn_params_add+0x10c/0x4b0 net/bluetooth/hci_core.c:2279 hci_conn_params_set net/bluetooth/mgmt.c:5162 [inline] add_device+0x5b4/0xa54 net/bluetooth/mgmt.c:7755 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Freed by task 3740: kasan_save_stack+0x3c/0x64 mm/kasan/common.c:57 kasan_save_track+0x20/0x3c mm/kasan/common.c:78 kasan_save_free_info+0x4c/0x74 mm/kasan/generic.c:584 poison_slab_object mm/kasan/common.c:253 [inline] __kasan_slab_free+0x88/0xb8 mm/kasan/common.c:285 kasan_slab_free include/linux/kasan.h:235 [inline] slab_free_hook mm/slub.c:2685 [inline] slab_free mm/slub.c:6170 [inline] kfree+0x14c/0x458 mm/slub.c:6488 hci_conn_params_free+0x288/0x484 net/bluetooth/hci_core.c:2312 remove_device+0x4b0/0x968 net/bluetooth/mgmt.c:7919 hci_mgmt_cmd net/bluetooth/hci_sock.c:1721 [inline] hci_sock_sendmsg+0x10b4/0x1dd0 net/bluetooth/hci_sock.c:1841 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg+0xe0/0x128 net/socket.c:742 sock_write_iter+0x250/0x390 net/socket.c:1195 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x66c/0xab0 fs/read_write.c:688 ksys_write+0x1fc/0x24c fs/read_write.c:740 __do_sys_write fs/read_write.c:751 [inline] __se_sys_write fs/read_write.c:748 [inline] __arm64_sys_write+0x70/0xa4 fs/read_write.c:748 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] invoke_syscall+0x84/0x2a8 arch/arm64/kernel/syscall.c:49 el0_svc_common.constprop.0+0xe4/0x294 arch/arm64/kernel/syscall.c:132 do_el0_svc+0x44/0x5c arch/arm64/kernel/syscall.c:151 el0_svc+0x38/0xac arch/arm64/kernel/entry-common.c:724 el0t_64_sync_handler+0xa0/0xe4 arch/arm64/kernel/entry-common.c:743 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596 Fixes: 1e2e3044c1bc ("Bluetooth: MGMT: Fix MGMT_OP_ADD_DEVICE invalid device flags") Cc: stable@vger.kernel.org Assisted-by: Bynario AI Signed-off-by: Samuel Page Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d23ca1dd0893..dc55763f9e58 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7658,6 +7658,8 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err) if (!err) { struct hci_conn_params *params; + hci_dev_lock(hdev); + params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, le_addr_type(cp->addr.type)); @@ -7666,6 +7668,7 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err) device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, hdev->conn_flags, params ? params->flags : 0); + hci_dev_unlock(hdev); } mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE, -- cgit v1.2.3 From d5541eb148da72d5e0a1bca8ecd171f9fc8b366f Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 21 Jun 2026 21:23:05 +0500 Subject: Bluetooth: ISO: avoid NULL deref of conn in iso_conn_big_sync() iso_conn_big_sync() drops the socket lock to call hci_get_route() and then re-acquires it, but dereferences iso_pi(sk)->conn->hcon afterwards without re-checking that conn is still valid. While the lock is dropped, the connection can be torn down under the same socket lock: iso_disconn_cfm() -> iso_conn_del() -> iso_chan_del() sets iso_pi(sk)->conn to NULL (and the broadcast teardown path can also clear conn->hcon on its own). When iso_conn_big_sync() re-acquires the lock and reads conn->hcon, conn may be NULL, causing a NULL pointer dereference (hcon is the first member of struct iso_conn). This path is reached from iso_sock_recvmsg() for a PA-sync broadcast sink socket (BT_SK_DEFER_SETUP | BT_SK_PA_SYNC), so the dropped-lock window can race with connection teardown driven by controller events. Re-validate iso_pi(sk)->conn and its hcon after re-acquiring the socket lock and bail out if the connection went away, as already done in the sibling iso_sock_rebind_bc(). Fixes: 7a17308c17880d ("Bluetooth: iso: Fix circular lock in iso_conn_big_sync") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 793a481d7066..cd7c7c9ea4fc 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1590,6 +1590,7 @@ static void iso_conn_big_sync(struct sock *sk) { int err; struct hci_dev *hdev; + struct iso_conn *conn; bdaddr_t src, dst; u8 src_type; @@ -1612,8 +1613,17 @@ static void iso_conn_big_sync(struct sock *sk) hci_dev_lock(hdev); lock_sock(sk); + /* The socket lock was dropped for hci_get_route(), so the connection + * may have been torn down meanwhile: iso_chan_del() clears conn and + * the broadcast teardown path can clear conn->hcon on its own. Check + * both before dereferencing conn->hcon. + */ + conn = iso_pi(sk)->conn; + if (!conn || !conn->hcon) + goto unlock; + if (!test_and_set_bit(BT_SK_BIG_SYNC, &iso_pi(sk)->flags)) { - err = hci_conn_big_create_sync(hdev, iso_pi(sk)->conn->hcon, + err = hci_conn_big_create_sync(hdev, conn->hcon, &iso_pi(sk)->qos, iso_pi(sk)->sync_handle, iso_pi(sk)->bc_num_bis, @@ -1622,6 +1632,7 @@ static void iso_conn_big_sync(struct sock *sk) bt_dev_err(hdev, "hci_big_create_sync: %d", err); } +unlock: release_sock(sk); hci_dev_unlock(hdev); hci_dev_put(hdev); -- cgit v1.2.3 From 2641a9e0a1dd4af2e21995470a21d55dd35e5203 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 23:36:13 +0800 Subject: Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for pending_rx_work. process_pending_rx() takes the same mutex, so teardown can deadlock against the worker it is flushing. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the l2cap_conn_ready() -> queue_work(..., &conn->pending_rx_work) submit path, the l2cap_conn_del() -> cancel_work_sync(&conn->pending_rx_work) teardown path, and the process_pending_rx() -> mutex_lock(&conn->lock) worker edge. Lockdep reported: WARNING: possible circular locking dependency detected process_pending_rx+0x21/0x2a [vuln_msv] l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv] *** DEADLOCK *** Cancel pending_rx_work before taking conn->lock, matching the existing lock-before-drain ordering used for the two delayed works in the same teardown path. The pending_rx queue is still purged after the work has been cancelled and conn->lock has been acquired. Fixes: 7ab56c3a6ecc ("Bluetooth: Fix deadlock in l2cap_conn_del()") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 62133eef9d2f..036d887dec34 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1775,19 +1775,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) disable_delayed_work_sync(&conn->info_timer); disable_delayed_work_sync(&conn->id_addr_timer); + cancel_work_sync(&conn->pending_rx_work); + mutex_lock(&conn->lock); kfree_skb(conn->rx_skb); skb_queue_purge(&conn->pending_rx); - - /* We can not call flush_work(&conn->pending_rx_work) here since we - * might block if we are running on a worker from the same workqueue - * pending_rx_work is waiting on. - */ - if (work_pending(&conn->pending_rx_work)) - cancel_work_sync(&conn->pending_rx_work); - ida_destroy(&conn->tx_ida); l2cap_unregister_all_users(conn); -- cgit v1.2.3 From 687617555cedfb74c9e3cb85d759b908dcb17856 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 21 Jun 2026 00:56:35 +0500 Subject: Bluetooth: L2CAP: validate option length before reading conf opt value l2cap_get_conf_opt() derives the option length from the attacker-controlled opt->len field and immediately dereferences opt->val (as u8, get_unaligned_le16() or get_unaligned_le32(), or a raw pointer for the default case) before any caller has confirmed that opt->len bytes are present in the buffer. The callers (l2cap_parse_conf_req(), l2cap_parse_conf_rsp() and l2cap_conf_rfc_get()) only detect a malformed option afterwards, once the running length has gone negative, by which point the out-of-bounds read has already executed. An existing post-hoc length check keeps the garbage value from being consumed, so this is not a data leak in the current control flow. It is still a validate-after-use ordering bug: up to 4 bytes are read past the end of the buffer before it is known to contain them, and it is fragile to future changes in the callers. Fix it at the source. Pass the end of the buffer into l2cap_get_conf_opt() and refuse to touch opt->val unless the full option (header + value) fits. Each caller computes an end pointer once before the loop and checks the return value directly instead of inferring the error from a negative length. Fixes: 7c9cbd0b5e38 ("Bluetooth: Verify that l2cap_get_conf_opt provides large enough buffer") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 036d887dec34..a1d249f42be7 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3045,13 +3045,24 @@ fail: return NULL; } -static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, - unsigned long *val) +static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type, + int *olen, unsigned long *val) { struct l2cap_conf_opt *opt = *ptr; int len; + /* opt->len is attacker-controlled. Validate that the full option + * (header + value) actually fits in the buffer before touching + * opt->val, otherwise the switch below reads past the end of the + * caller's buffer. + */ + if (end - *ptr < L2CAP_CONF_OPT_SIZE) + return -EINVAL; + len = L2CAP_CONF_OPT_SIZE + opt->len; + if (end - *ptr < len) + return -EINVAL; + *ptr += len; *type = opt->type; @@ -3423,6 +3434,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data void *ptr = rsp->data; void *endptr = data + data_size; void *req = chan->conf_req; + void *req_end = req + chan->conf_len; int len = chan->conf_len; int type, hint, olen; unsigned long val; @@ -3436,9 +3448,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data BT_DBG("chan %p", chan); while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&req, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; hint = type & L2CAP_CONF_HINT; type &= L2CAP_CONF_MASK; @@ -3666,6 +3680,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, struct l2cap_conf_req *req = data; void *ptr = req->data; void *endptr = data + size; + void *rsp_end = rsp + len; int type, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; @@ -3674,9 +3689,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; switch (type) { case L2CAP_CONF_MTU: @@ -3927,6 +3944,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) { int type, olen; unsigned long val; + void *rsp_end = rsp + len; /* Use sane default values in case a misbehaving remote device * did not send an RFC or extended window size option. */ @@ -3945,9 +3963,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) return; while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - if (len < 0) + int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val); + + if (ret < 0) break; + len -= ret; switch (type) { case L2CAP_CONF_RFC: -- cgit v1.2.3 From badff6c3bed8923a1257a853f137d447976eec30 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 16:36:52 +0800 Subject: Bluetooth: btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v3() During the v3 firmware download the controller sends a v3_data_req with a 32 bit offset and a 16 bit len. nxp_recv_fw_req_v3() checks only the lower bound of the offset and then sends firmware from that offset. nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); Nothing checks that fw_dnld_v3_offset + len stays within nxpdev->fw->size, so a controller that asks for an offset or length past the firmware image makes the driver read past the end of nxpdev->fw->data and send that memory back over UART. nxp_recv_fw_req_v1() already bounds the same write. Add the equivalent check to the v3 path, reject the request when it falls outside the firmware image, and zero len on the error path so the fw_v3_prev_sent bookkeeping at free_skb stays consistent. Fixes: 689ca16e5232 ("Bluetooth: NXP: Add protocol support for NXP Bluetooth chipsets") Suggested-by: Neeraj Sanjay Kale Reviewed-by: Neeraj Sanjay Kale Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btnxpuart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index e7036a48ce48..6a1cffe08d5f 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -1267,6 +1267,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; + if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size || + len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) { + bt_dev_err(hdev, "FW download out of bounds, ignoring request"); + len = 0; + goto free_skb; + } serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); -- cgit v1.2.3 From 12917f591cea1af36087dba5b9ec888652f0b42a Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Mon, 15 Jun 2026 11:33:05 -0400 Subject: Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection was pending, but hdev->sent_cmd can be NULL while req_status is still HCI_REQ_PEND, leading to a NULL pointer dereference and a general protection fault from the hci_rx_work() receive path. Instead of inspecting hdev->sent_cmd, track the in-flight create connection command with a new per-connection HCI_CONN_CREATE flag and route all cancellation through hci_cancel_connect_sync(), which dispatches to a dedicated per-type cancel function. The create command is in exactly one of two states: still queued, or in flight. The cancel function holds cmd_sync_work_lock across the whole decision: the worker takes this lock to dequeue every entry, so while it is held a queued command cannot start running and an in-flight command cannot complete and let the next command become pending. This keeps the flag test and hci_cmd_sync_cancel() atomic with respect to the worker, so a queued command is simply dequeued, and an in-flight command owned by this connection is cancelled without the risk of cancelling an unrelated command that became pending in the meantime. CIS uses the same flag mechanism via HCI_CONN_CREATE_CIS but cannot be dequeued per-connection. hci_acl_create_conn_sync() and hci_le_create_conn_sync() clear HCI_CONN_CREATE after the create command completes, but the command status handler can free conn via hci_conn_del() (for example when the controller rejects the connection) while the worker is still blocked on the connection complete event. Hold a reference on conn across the create command so the flag can be cleared without a use-after-free. Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections") Cc: stable@vger.kernel.org Suggested-by: XIAO WU Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 21 +------ net/bluetooth/hci_sync.c | 133 ++++++++++++++++++++++++++++++++++----- 3 files changed, 123 insertions(+), 32 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7e15da47fe3a..4ca09298e11a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -985,6 +985,7 @@ enum { HCI_CONN_AUTH_FAILURE, HCI_CONN_PER_ADV, HCI_CONN_BIG_CREATED, + HCI_CONN_CREATE, HCI_CONN_CREATE_CIS, HCI_CONN_CREATE_BIG_SYNC, HCI_CONN_BIG_SYNC, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c335372e4062..1966cd153d97 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -3178,26 +3178,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) conn->abort_reason = reason; - /* If the connection is pending check the command opcode since that - * might be blocking on hci_cmd_sync_work while waiting its respective - * event so we need to hci_cmd_sync_cancel to cancel it. - * - * hci_connect_le serializes the connection attempts so only one - * connection can be in BT_CONNECT at time. + /* Cancel the connect attempt. A return of 0 means the create command + * was still queued and got dequeued, so there is nothing to disconnect. */ - if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) { - switch (hci_skb_event(hdev->sent_cmd)) { - case HCI_EV_CONN_COMPLETE: - case HCI_EV_LE_CONN_COMPLETE: - case HCI_EV_LE_ENHANCED_CONN_COMPLETE: - case HCI_EVT_LE_CIS_ESTABLISHED: - hci_cmd_sync_cancel(hdev, ECANCELED); - break; - } - /* Cancel connect attempt if still queued/pending */ - } else if (!hci_cancel_connect_sync(hdev, conn)) { + if (!hci_cancel_connect_sync(hdev, conn)) return 0; - } /* Run immediately if on cmd_sync_work since this may be called * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 3be8c3581c6c..c896d4edd013 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6633,6 +6633,11 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) bt_dev_dbg(hdev, "conn %p", conn); + /* Hold a reference so conn stays valid for the HCI_CONN_CREATE + * clear_bit() at done. + */ + hci_conn_get(conn); + clear_bit(HCI_CONN_SCANNING, &conn->flags); conn->state = BT_CONNECT; @@ -6645,6 +6650,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) hdev->le_scan_type == LE_SCAN_ACTIVE && !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { hci_conn_del(conn); + hci_conn_put(conn); return -EBUSY; } @@ -6690,6 +6696,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) &own_addr_type); if (err) goto done; + + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ + set_bit(HCI_CONN_CREATE, &conn->flags); + /* Send command LE Extended Create Connection if supported */ if (use_ext_conn(hdev)) { err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type); @@ -6725,11 +6737,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) conn->conn_timeout, NULL); done: + clear_bit(HCI_CONN_CREATE, &conn->flags); + if (err == -ETIMEDOUT) hci_le_connect_cancel_sync(hdev, conn, 0x00); /* Re-enable advertising after the connection attempt is finished. */ hci_resume_advertising_sync(hdev); + hci_conn_put(conn); return err; } @@ -7004,10 +7019,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) else cp.role_switch = 0x00; - return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, - sizeof(cp), &cp, - HCI_EV_CONN_COMPLETE, - conn->conn_timeout, NULL); + /* Hold a reference so conn stays valid for the HCI_CONN_CREATE + * clear_bit() below. + */ + hci_conn_get(conn); + + /* Mark create connection in flight so hci_cancel_connect_sync() can + * cancel it while blocking on the connection complete event. + */ + set_bit(HCI_CONN_CREATE, &conn->flags); + + err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN, + sizeof(cp), &cp, + HCI_EV_CONN_COMPLETE, + conn->conn_timeout, NULL); + + clear_bit(HCI_CONN_CREATE, &conn->flags); + hci_conn_put(conn); + + return err; } int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) @@ -7059,22 +7089,97 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) return (err == -EEXIST) ? 0 : err; } -int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) +static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) { - if (conn->state != BT_OPEN) - return -EINVAL; + struct hci_cmd_sync_work_entry *entry; + int err = -EBUSY; + + /* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel + * atomic against the worker, which takes this lock to dequeue every + * entry: while it is held no other command can become pending, so + * hci_cmd_sync_cancel() cannot cancel an unrelated command. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + /* In flight: this connection owns the pending request, cancel it. */ + if (test_bit(HCI_CONN_CREATE, &conn->flags)) { + hci_cmd_sync_cancel(hdev, ECANCELED); + goto unlock; + } + + /* Still queued: a successful dequeue means it never started, so there + * is nothing to disconnect. + */ + entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn, + NULL); + if (entry) { + _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); + err = 0; + } + +unlock: + mutex_unlock(&hdev->cmd_sync_work_lock); + return err; +} + +static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + struct hci_cmd_sync_work_entry *entry; + int err = -EBUSY; + + /* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel + * atomic against the cmd_sync worker. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + if (test_bit(HCI_CONN_CREATE, &conn->flags)) { + hci_cmd_sync_cancel(hdev, ECANCELED); + goto unlock; + } + + entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn, + create_le_conn_complete); + if (entry) { + _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED); + err = 0; + } + +unlock: + mutex_unlock(&hdev->cmd_sync_work_lock); + return err; +} + +static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev, + struct hci_conn *conn) +{ + /* LE Create CIS is shared by the whole CIG and cannot be dequeued + * per-connection, so only an in-flight command can be cancelled. + * cmd_sync_work_lock keeps the test and the cancel atomic against the + * cmd_sync worker. + */ + mutex_lock(&hdev->cmd_sync_work_lock); + + if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) + hci_cmd_sync_cancel(hdev, ECANCELED); + + mutex_unlock(&hdev->cmd_sync_work_lock); + return -EBUSY; +} + +int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn) +{ switch (conn->type) { case ACL_LINK: - return !hci_cmd_sync_dequeue_once(hdev, - hci_acl_create_conn_sync, - conn, NULL); + return hci_acl_cancel_create_conn_sync(hdev, conn); case LE_LINK: - return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync, - conn, create_le_conn_complete); + return hci_le_cancel_create_conn_sync(hdev, conn); + case CIS_LINK: + return hci_cis_cancel_create_conn_sync(hdev, conn); + default: + return -ENOENT; } - - return -ENOENT; } int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn, -- cgit v1.2.3 From 352a59dc1f4a41314b6f827c17e16af7ca88271a Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:12:29 +0800 Subject: Bluetooth: 6lowpan: avoid untracked enable work lowpan_enable_set() allocates a temporary work item and schedules do_enable_set() on system_wq, then returns to debugfs. The debugfs active operation has ended at that point, but the worker still executes module text and manipulates enable_6lowpan and listen_chan. bt_6lowpan_exit() removes the debugfs files and immediately closes and puts listen_chan. It has no pointer to the queued work item, so it cannot cancel or flush it before tearing down the state that the worker uses. The buggy scenario involves two paths, with each column showing the order within that path: debugfs enable write module exit 1. lowpan_enable_set() allocates 1. bt_6lowpan_exit() removes set_enable work the debugfs file 2. schedule_work() queues 2. bt_6lowpan_exit() closes do_enable_set() and puts listen_chan 3. the write operation returns 3. module teardown can continue 4. do_enable_set() later runs against stale state Run the enable state transition synchronously in lowpan_enable_set() instead. The simple debugfs setter can sleep, and this file already handles the 6LoWPAN control write synchronously under the same set_lock. Once the setter returns, debugfs removal covers the whole operation and exit can no longer race with an untracked work item. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in do_enable_set+0x113/0x2e0 Workqueue: events do_enable_set [bluetooth_6lowpan] The buggy address belongs to the object at ffff888109cb8000 Fixes: 90305829635d ("Bluetooth: 6lowpan: Converting rwlocks to use RCU") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/6lowpan.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 962e0e885105..c4b0a4048be2 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1081,23 +1081,15 @@ done: } while (nchans); } -struct set_enable { - struct work_struct work; - bool flag; -}; - -static void do_enable_set(struct work_struct *work) +static void do_enable_set(bool flag) { - struct set_enable *set_enable = container_of(work, - struct set_enable, work); - - if (!set_enable->flag || enable_6lowpan != set_enable->flag) + if (!flag || enable_6lowpan != flag) /* Disconnect existing connections if 6lowpan is * disabled */ disconnect_all_peers(); - enable_6lowpan = set_enable->flag; + enable_6lowpan = flag; mutex_lock(&set_lock); if (listen_chan) { @@ -1109,22 +1101,11 @@ static void do_enable_set(struct work_struct *work) listen_chan = bt_6lowpan_listen(); mutex_unlock(&set_lock); - - kfree(set_enable); } static int lowpan_enable_set(void *data, u64 val) { - struct set_enable *set_enable; - - set_enable = kzalloc_obj(*set_enable); - if (!set_enable) - return -ENOMEM; - - set_enable->flag = !!val; - INIT_WORK(&set_enable->work, do_enable_set); - - schedule_work(&set_enable->work); + do_enable_set(!!val); return 0; } -- cgit v1.2.3 From 518aa9505fa10ea5662349e5d2efd8c9e32a820b Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:12:59 +0800 Subject: Bluetooth: 6lowpan: hold L2CAP conn across debugfs control get_l2cap_conn() looks up an LE hci_conn under hdev protection, but then drops that protection before reading hcon->l2cap_data and before lowpan_control_write() later dereferences conn->hcon. A disconnect or device close can tear down the same L2CAP connection in that window. The buggy scenario involves two paths, with each column showing the order within that path: 6LoWPAN control write: HCI disconnect/device close: 1. get_l2cap_conn() finds hcon 1. hci_disconn_cfm() dispatches and hcon->l2cap_data. the L2CAP disconnect callback. 2. get_l2cap_conn() drops hdev 2. l2cap_conn_del() clears protection and returns conn. hcon->l2cap_data and drops the L2CAP connection reference. 3. lowpan_control_write() reads 3. hci_conn_del() removes and drops conn->hcon. the HCI connection. Take a reference to the L2CAP connection with l2cap_conn_hold_unless_zero() while hdev is still locked, and drop that reference after the debugfs command's last use of conn. This mirrors the existing L2CAP ACL receive-side handoff and keeps the connection dereferenceable after leaving hdev protection. Export the existing helper so the bluetooth_6lowpan module can use the same lifetime primitive. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in lowpan_control_write+0x374/0x520 The buggy address belongs to the object at ffff888111b9d000 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes inside of freed 1024-byte region [ffff888111b9d000, ffff888111b9d400) Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 lowpan_control_write+0x374/0x520 (net/bluetooth/6lowpan.c:1131) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 __debugfs_file_get+0xf7/0x400 full_proxy_write+0x9e/0xd0 vfs_write+0x1b0/0x810 ksys_write+0xd2/0x170 dnotify_flush+0x32/0x220 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 l2cap_conn_add+0x45/0x520 l2cap_chan_connect+0xac6/0xd90 l2cap_sock_connect+0x216/0x350 __sys_connect+0x101/0x130 __x64_sys_connect+0x40/0x50 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 hci_conn_hash_flush+0xc0/0x140 hci_dev_close_sync+0x41a/0xb00 hci_dev_close+0x12f/0x160 hci_sock_ioctl+0x157/0x570 sock_do_ioctl+0xf7/0x210 sock_ioctl+0x32f/0x490 __x64_sys_ioctl+0xc7/0x110 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f kasan_record_aux_stack+0xa7/0xc0 insert_work+0x32/0x100 __queue_work+0x262/0xa60 queue_work_on+0xad/0xb0 l2cap_connect_cfm+0x4ef/0x670 hci_le_remote_feat_complete_evt+0x247/0x430 hci_event_packet+0x360/0x6f0 hci_rx_work+0x2ae/0x7a0 process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Fixes: 6b8d4a6a0314 ("Bluetooth: 6LoWPAN: Use connected oriented channel instead of fixed one") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/6lowpan.c | 21 +++++++++++++++------ net/bluetooth/l2cap_core.c | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index c4b0a4048be2..e7be18a3af33 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1017,16 +1017,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, hci_dev_lock(hdev); hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type); - hci_dev_unlock(hdev); - hci_dev_put(hdev); - - if (!hcon) + if (!hcon) { + hci_dev_unlock(hdev); + hci_dev_put(hdev); return -ENOENT; + } - *conn = (struct l2cap_conn *)hcon->l2cap_data; + *conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data); BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type); + hci_dev_unlock(hdev); + hci_dev_put(hdev); + return 0; } @@ -1154,18 +1157,22 @@ static ssize_t lowpan_control_write(struct file *fp, if (conn) { struct lowpan_peer *peer; - if (!is_bt_6lowpan(conn->hcon)) + if (!is_bt_6lowpan(conn->hcon)) { + l2cap_conn_put(conn); return -EINVAL; + } peer = lookup_peer(conn); if (peer) { BT_DBG("6LoWPAN connection already exists"); + l2cap_conn_put(conn); return -EALREADY; } BT_DBG("conn %p dst %pMR type %d user %u", conn, &conn->hcon->dst, conn->hcon->dst_type, addr_type); + l2cap_conn_put(conn); } ret = bt_6lowpan_connect(&addr, addr_type); @@ -1181,6 +1188,8 @@ static ssize_t lowpan_control_write(struct file *fp, return ret; ret = bt_6lowpan_disconnect(conn, addr_type); + if (conn) + l2cap_conn_put(conn); if (ret < 0) return ret; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a1d249f42be7..4ee3b9e30c65 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7716,6 +7716,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c) return c; } +EXPORT_SYMBOL(l2cap_conn_hold_unless_zero); int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) -- cgit v1.2.3 From 384a4b2fef9ffe5e270ee5558975c0504881c5fb Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Wed, 24 Jun 2026 00:13:28 +0800 Subject: Bluetooth: MGMT: Fix adv monitor add failure cleanup hci_add_adv_monitor() publishes a new adv_monitor in hdev->adv_monitors_idr before the powered MSFT setup step. The MSFT offload add path can then fail either locally before the controller add command completes, or in the MSFT add callback. In the current queued management add flow, hci_cmd_sync_work() still invokes mgmt_add_adv_patterns_monitor_complete() with the original pending command after msft_add_monitor_pattern() returns. The buggy scenario involves two paths, with each column showing the order within that path: MSFT add handling MGMT completion 1. insert monitor and handle 1. receive sync error 2. send MSFT add command 2. call add-monitor completion 3. callback sees bad response 3. load cmd->user_data 4. callback frees monitor 4. read monitor->handle Local MSFT setup failures have the other half of the same ownership bug: they return an error after the IDR insertion, but no later code removes the failed monitor from the IDR. Keep ownership with the pending management command until its completion. For normal management adds, the MSFT add callback now records successful controller state and returns errors to its caller. The management completion frees the monitor on non-success after copying the response handle, while resume/reregister callback-error cleanup remains in the MSFT callback. The success path keeps the existing bookkeeping. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x5f0 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x19f/0x330 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] kasan_report+0xe0/0x110 ? mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] mgmt_add_adv_patterns_monitor_complete+0xfb/0x260 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 ? 0xffffffffc00d00da ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] ? hci_cmd_sync_work+0x1ab/0x210 [bluetooth] hci_cmd_sync_work+0x1c0/0x210 [bluetooth] ? __pfx_mgmt_add_adv_patterns_monitor_complete+0x10/0x10 [bluetooth] process_one_work+0x4fd/0xbc0 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_hci_cmd_sync_work+0x10/0x10 [bluetooth] ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Allocated by task 471 on cpu 3 at 285.205389s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 __kasan_kmalloc+0xaa/0xb0 add_adv_patterns_monitor_rssi+0xd5/0x230 [bluetooth] hci_sock_sendmsg+0x96b/0xf80 [bluetooth] __sys_sendto+0x2bc/0x2d0 __x64_sys_sendto+0x76/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 454 on cpu 2 at 285.217112s: kasan_save_stack+0x33/0x60 kasan_save_track+0x17/0x60 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 msft_add_monitor_sync+0x54a/0x570 [bluetooth] hci_add_adv_monitor+0x133/0x180 [bluetooth] hci_cmd_sync_work+0x187/0x210 [bluetooth] process_one_work+0x4fd/0xbc0 worker_thread+0x2d8/0x570 kthread+0x1ad/0x1f0 ret_from_fork+0x3c9/0x540 ret_from_fork_asm+0x1a/0x30 Fixes: a2a4dedf88ab ("Bluetooth: advmon offload MSFT add monitor") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 2 ++ net/bluetooth/msft.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index dc55763f9e58..733a4b70e10c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5375,6 +5375,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED) monitor->state = ADV_MONITOR_STATE_REGISTERED; hci_update_passive_scan(hdev); + } else { + hci_free_adv_monitor(hdev, monitor); } mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index 2f008167cbaa..d7badce8746c 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -291,7 +291,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode, monitor->state = ADV_MONITOR_STATE_OFFLOADED; unlock: - if (status) + if (status && msft->resuming) hci_free_adv_monitor(hdev, monitor); hci_dev_unlock(hdev); -- cgit v1.2.3 From 0f8a5dcc66648b6e1458a9f3ba4c5a0463a228fc Mon Sep 17 00:00:00 2001 From: Sungwoo Kim Date: Wed, 24 Jun 2026 17:33:04 -0400 Subject: Bluetooth: sco: Fix a race condition in sco_sock_timeout() sco_sock_timeout() runs asynchronously and lock_sock(sk). If the socket is closing while the timer is running, it holds the same lock (lock_sock(sk)) twice, leading to a deadlock. CPU 0 CPU 1 ==================== ====================== sco_sock_close() sco_sock_timeout() lock_sock(sk) // <-- LOCK __sco_sock_close() sco_chan_del() sco_conn_put() sco_conn_free() disable_delayed_work_sync() lock(sk) // <-- SAME LOCK Fix this by moving disable_delayed_work_sync() outside of lock_sock(sk), ensuring that no lock_sock(sk) is held before sco_sock_timeout(). Lockdep splat: WARNING: possible circular locking dependency detected 6.13.0-rc4 #7 Not tainted syz-executor292/9514 is trying to acquire lock: ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_lock_acquire sect/v6.13-rc4/./include/linux/rcupdate.h:337 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: rcu_read_lock sect/v6.13-rc4/./include/linux/rcupdate.h:849 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4137 [inline] ffff8881115d5070 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}, at: __flush_work+0xd1/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195 but task is already holding lock: ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline] ffff88807db3a258 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: sco_sock_close+0x25/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:524 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}: lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849 lock_sock_nested+0x48/0x130 sect/v6.13-rc4/net/core/sock.c:3622 lock_sock sect/v6.13-rc4/./include/net/sock.h:1623 [inline] sco_sock_timeout+0xbe/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:158 process_one_work sect/v6.13-rc4/kernel/workqueue.c:3229 [inline] process_scheduled_works+0xa99/0x18f0 sect/v6.13-rc4/kernel/workqueue.c:3310 worker_thread+0x8a9/0xd80 sect/v6.13-rc4/kernel/workqueue.c:3391 kthread+0x2c6/0x360 sect/v6.13-rc4/kernel/kthread.c:389 ret_from_fork+0x4e/0x80 sect/v6.13-rc4/arch/x86/kernel/process.c:147 ret_from_fork_asm+0x1a/0x30 sect/v6.13-rc4/arch/x86/entry/entry_64.S:244 -> #0 ((work_completion)(&(&conn->timeout_work)->work)){+.+.}-{0:0}: check_prev_add sect/v6.13-rc4/kernel/locking/lockdep.c:3161 [inline] check_prevs_add sect/v6.13-rc4/kernel/locking/lockdep.c:3280 [inline] validate_chain+0x1888/0x5760 sect/v6.13-rc4/kernel/locking/lockdep.c:3904 __lock_acquire+0x13b4/0x2120 sect/v6.13-rc4/kernel/locking/lockdep.c:5226 lock_acquire+0x1c4/0x520 sect/v6.13-rc4/kernel/locking/lockdep.c:5849 touch_work_lockdep_map sect/v6.13-rc4/kernel/workqueue.c:3909 [inline] start_flush_work sect/v6.13-rc4/kernel/workqueue.c:4163 [inline] __flush_work+0x70f/0xc40 sect/v6.13-rc4/kernel/workqueue.c:4195 __cancel_work_sync sect/v6.13-rc4/kernel/workqueue.c:4351 [inline] disable_delayed_work_sync+0xbb/0xf0 sect/v6.13-rc4/kernel/workqueue.c:4514 sco_conn_free sect/v6.13-rc4/net/bluetooth/sco.c:95 [inline] kref_put sect/v6.13-rc4/./include/linux/kref.h:65 [inline] sco_conn_put+0x18f/0x270 sect/v6.13-rc4/net/bluetooth/sco.c:107 sco_chan_del+0xe2/0x210 sect/v6.13-rc4/net/bluetooth/sco.c:236 sco_sock_close+0x8f/0x100 sect/v6.13-rc4/net/bluetooth/sco.c:526 sco_sock_release+0x62/0x2d0 sect/v6.13-rc4/net/bluetooth/sco.c:1300 __sock_release+0xe1/0x2d0 sect/v6.13-rc4/net/socket.c:640 sock_close+0x1c/0x30 sect/v6.13-rc4/net/socket.c:1408 __fput+0x2bd/0xa80 sect/v6.13-rc4/fs/file_table.c:450 __fput_sync+0x15e/0x1c0 sect/v6.13-rc4/fs/file_table.c:535 __do_sys_close sect/v6.13-rc4/fs/open.c:1554 [inline] __se_sys_close sect/v6.13-rc4/fs/open.c:1539 [inline] __x64_sys_close+0x93/0x120 sect/v6.13-rc4/fs/open.c:1539 do_syscall_x64 sect/v6.13-rc4/arch/x86/entry/common.c:52 [inline] do_syscall_64+0xee/0x210 sect/v6.13-rc4/arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: e6720779ae61 ("Bluetooth: SCO: Use kref to track lifetime of sco_conn") Acked-by: Dave Tian Signed-off-by: Sungwoo Kim Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/sco.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index fcc597be5bbd..c05f79b7aa31 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -570,10 +570,23 @@ static void __sco_sock_close(struct sock *sk) /* Must be called on unlocked socket. */ static void sco_sock_close(struct sock *sk) { + struct sco_conn *conn; + + lock_sock(sk); + conn = sco_pi(sk)->conn; + if (conn) + sco_conn_hold(conn); + release_sock(sk); + + if (conn) + disable_delayed_work_sync(&conn->timeout_work); + lock_sock(sk); - sco_sock_clear_timer(sk); __sco_sock_close(sk); release_sock(sk); + + if (conn) + sco_conn_put(conn); } static void sco_sock_init(struct sock *sk, struct sock *parent) -- cgit v1.2.3 From bb067a99a0356196c0b89a95721985485ebce5a5 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:50:58 +0200 Subject: Bluetooth: bnep: pin L2CAP connection during netdev registration bnep_add_connection() reads the L2CAP connection without holding the channel lock, then passes its HCI device to register_netdev(). Controller teardown can clear and release that connection concurrently, leaving the network device registration path to dereference a freed parent device. Take a reference to the L2CAP connection while holding the channel lock. Retain it until register_netdev() has taken the parent device reference. Fixes: 65f53e9802db ("Bluetooth: Access BNEP session addresses through L2CAP channel") Reported-by: syzbot+fed5dce4553262f3b35c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=fed5dce4553262f3b35c Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/bnep/core.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index add9a8f7535d..f7d88c33e23e 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -559,14 +559,18 @@ static int bnep_session(void *arg) return 0; } -static struct device *bnep_get_device(struct bnep_session *session) +static struct l2cap_conn *bnep_get_conn(struct bnep_session *session) { - struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn; + struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan; + struct l2cap_conn *conn; - if (!conn || !conn->hcon) - return NULL; + l2cap_chan_lock(chan); + conn = chan->conn; + if (conn) + l2cap_conn_get(conn); + l2cap_chan_unlock(chan); - return &conn->hcon->dev; + return conn; } static const struct device_type bnep_type = { @@ -578,6 +582,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) u32 valid_flags = BIT(BNEP_SETUP_RESPONSE); struct net_device *dev; struct bnep_session *s, *ss; + struct l2cap_conn *conn = NULL; u8 dst[ETH_ALEN], src[ETH_ALEN]; int err; @@ -637,10 +642,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) bnep_set_default_proto_filter(s); #endif - SET_NETDEV_DEV(dev, bnep_get_device(s)); + conn = bnep_get_conn(s); + if (!conn) { + err = -ENOTCONN; + goto failed; + } + + SET_NETDEV_DEV(dev, &conn->hcon->dev); SET_NETDEV_DEVTYPE(dev, &bnep_type); err = register_netdev(dev); + l2cap_conn_put(conn); + conn = NULL; if (err) goto failed; @@ -662,6 +675,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) return 0; failed: + if (conn) + l2cap_conn_put(conn); up_write(&bnep_session_sem); free_netdev(dev); return err; -- cgit v1.2.3 From 4bd0b274054f2679f28b70222b607bb0afc3ab9a Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Sun, 28 Jun 2026 02:23:05 +0200 Subject: Bluetooth: fix UAF in bt_accept_dequeue() bt_accept_get() takes a temporary reference before dropping the accept queue lock. bt_accept_dequeue() currently drops that reference before bt_accept_unlink(), leaving only the queue reference. bt_accept_unlink() drops the queue reference. The subsequent sock_hold() therefore accesses freed memory if it was the final reference, as observed by KASAN during listening L2CAP socket cleanup. Retain the temporary queue-walk reference through unlink and hand it to the caller on success. Drop it explicitly on the closed and not-yet-connected paths. Fixes: ab1513597c6c ("Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del()") Reported-by: syzbot+674ff7e4d7fdfd572afc@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=674ff7e4d7fdfd572afc Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/af_bluetooth.c | 17 +++-------------- net/bluetooth/l2cap_sock.c | 4 ++-- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index bcbc11c9cb15..a2290ffdc2c1 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -305,7 +305,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) restart: for (sk = bt_accept_get(parent, NULL); sk; sk = next) { - /* Prevent early freeing of sk due to unlink and sock_kill */ + /* The reference from bt_accept_get() keeps sk alive. */ lock_sock(sk); /* Check sk has not already been unlinked via @@ -321,13 +321,11 @@ restart: next = bt_accept_get(parent, sk); - /* sk is safely in the parent list so reduce reference count */ - sock_put(sk); - /* FIXME: Is this check still needed */ if (sk->sk_state == BT_CLOSED) { bt_accept_unlink(sk); release_sock(sk); + sock_put(sk); continue; } @@ -337,16 +335,6 @@ restart: if (newsock) sock_graft(sk, newsock); - /* Hand the caller a reference taken while sk is - * still locked. bt_accept_unlink() just dropped - * the accept-queue reference; without this hold a - * concurrent teardown (e.g. l2cap_conn_del() -> - * l2cap_sock_kill()) could free sk between - * release_sock() and the caller using it. Every - * caller drops this with sock_put() when done. - */ - sock_hold(sk); - release_sock(sk); if (next) sock_put(next); @@ -354,6 +342,7 @@ restart: } release_sock(sk); + sock_put(sk); } return NULL; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4853f1b33449..de56ca691afa 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1492,8 +1492,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) /* Close not yet accepted channels. * - * bt_accept_dequeue() now returns sk with an extra reference held - * (taken while sk was still locked) so a concurrent l2cap_conn_del() + * bt_accept_dequeue() returns sk with its temporary queue-walk + * reference held, so a concurrent l2cap_conn_del() * -> l2cap_sock_kill() cannot free sk under us. * * cleanup_listen() runs under the parent sk lock, so unlike -- cgit v1.2.3 From 6fef032af0092ed5ccb767239a9ac1bc38c08a40 Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Mon, 29 Jun 2026 09:49:58 -0400 Subject: Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb() l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after release_sock(parent). Once the parent lock is dropped the newly enqueued child socket sk is reachable via the accept queue, so another task can accept and free it before the callback dereferences sk, resulting in a use-after-free. Rework the ->new_connection() op so the core, rather than the callback, owns the child channel's lifetime. The op now receives a pre-allocated new_chan and returns an errno instead of allocating and returning a channel. l2cap_new_connection() allocates the child channel and links it into the conn list via __l2cap_chan_add() before invoking the callback, so the conn-list reference keeps the channel alive once release_sock(parent) exposes the socket to other tasks. Channel configuration that was duplicated in l2cap_sock_init() and the various new_connection callbacks is consolidated into l2cap_chan_set_defaults(), which now inherits from the parent channel when one is supplied. Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c") Cc: stable@kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/l2cap.h | 10 ++-- net/bluetooth/6lowpan.c | 18 +------- net/bluetooth/l2cap_core.c | 78 ++++++++++++++++++++++++++------ net/bluetooth/l2cap_sock.c | 103 ++++++++++++++++++++---------------------- net/bluetooth/smp.c | 27 +++-------- 5 files changed, 127 insertions(+), 109 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1640cc9bf83a..ef6ce1c20a4f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -617,7 +617,8 @@ struct l2cap_chan { struct l2cap_ops { char *name; - struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); + int (*new_connection)(struct l2cap_chan *chan, + struct l2cap_chan *new_chan); int (*recv) (struct l2cap_chan * chan, struct sk_buff *skb); void (*teardown) (struct l2cap_chan *chan, int err); @@ -882,9 +883,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) return (seq + 1) % (chan->tx_win_max + 1); } -static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan) +static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { - return NULL; + return -EOPNOTSUPP; } static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb) @@ -961,7 +963,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, void l2cap_chan_busy(struct l2cap_chan *chan, int busy); void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail); int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); -void l2cap_chan_set_defaults(struct l2cap_chan *chan); +void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index e7be18a3af33..d504a363a30f 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void) if (!chan) return NULL; - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; chan->mode = L2CAP_MODE_LE_FLOWCTL; @@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan) ifup(dev->netdev); } -static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) -{ - struct l2cap_chan *chan; - - chan = chan_create(); - if (!chan) - return NULL; - - chan->ops = pchan->ops; - - BT_DBG("chan %p pchan %p", chan, pchan); - - return chan; -} - static void unregister_dev(struct lowpan_btle_dev *dev) { struct hci_dev *hdev = READ_ONCE(dev->hdev); @@ -889,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) static const struct l2cap_ops bt_6lowpan_chan_ops = { .name = "L2CAP 6LoWPAN channel", - .new_connection = chan_new_conn_cb, .recv = chan_recv_cb, .close = chan_close_cb, .state_change = chan_state_change_cb, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4ee3b9e30c65..519cd9552d86 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -522,7 +522,10 @@ void l2cap_chan_put(struct l2cap_chan *c) } EXPORT_SYMBOL_GPL(l2cap_chan_put); -void l2cap_chan_set_defaults(struct l2cap_chan *chan) +/* Initialise @chan with default values, inheriting from the parent channel + * @pchan when it is given. + */ +void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan) { chan->fcs = L2CAP_FCS_CRC16; chan->max_tx = L2CAP_DEFAULT_MAX_TX; @@ -536,6 +539,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + if (pchan) { + BT_DBG("chan %p pchan %p", chan, pchan); + + chan->chan_type = pchan->chan_type; + chan->imtu = pchan->imtu; + chan->omtu = pchan->omtu; + chan->mode = pchan->mode; + chan->fcs = pchan->fcs; + chan->max_tx = pchan->max_tx; + chan->tx_win = pchan->tx_win; + chan->tx_win_max = pchan->tx_win_max; + chan->sec_level = pchan->sec_level; + chan->conf_state = pchan->conf_state; + chan->flags = pchan->flags; + chan->tx_credits = pchan->tx_credits; + chan->rx_credits = pchan->rx_credits; + + if (chan->chan_type == L2CAP_CHAN_FIXED) { + chan->scid = pchan->scid; + chan->dcid = pchan->scid; + } + + return; + } + chan->conf_state = 0; set_bit(CONF_NOT_COMPLETE, &chan->conf_state); @@ -4024,6 +4052,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, return 0; } +/* Allocate and initialise a channel for an incoming connection. + * + * The channel inherits its configuration from @pchan and is linked into @conn + * before ->new_connection() runs, so the conn list reference keeps it alive if + * the callback exposes it (e.g. via the socket accept queue) before this + * returns. The l2cap_chan_create() reference is taken over by the subsystem on + * success and dropped here on failure. + */ +static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn, + struct l2cap_chan *pchan) +{ + struct l2cap_chan *chan; + + chan = l2cap_chan_create(); + if (!chan) + return NULL; + + l2cap_chan_set_defaults(chan, pchan); + chan->ops = pchan->ops; + + __l2cap_chan_add(conn, chan); + + if (pchan->ops->new_connection && + pchan->ops->new_connection(pchan, chan) < 0) { + l2cap_chan_del(chan, 0); + l2cap_chan_put(chan); + return NULL; + } + + return chan; +} + static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data, u8 rsp_code) { @@ -4070,7 +4130,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, goto response; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) goto response; @@ -4088,8 +4148,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, chan->psm = psm; chan->dcid = scid; - __l2cap_chan_add(conn, chan); - dcid = chan->scid; __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); @@ -4972,7 +5030,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, goto response_unlock; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) { result = L2CAP_CR_LE_NO_MEM; goto response_unlock; @@ -4987,8 +5045,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, chan->omtu = mtu; chan->remote_mps = mps; - __l2cap_chan_add(conn, chan); - l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits)); dcid = chan->scid; @@ -5196,7 +5252,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, continue; } - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (!chan) { result = L2CAP_CR_LE_NO_MEM; continue; @@ -5211,8 +5267,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, chan->omtu = mtu; chan->remote_mps = mps; - __l2cap_chan_add(conn, chan); - l2cap_ecred_init(chan, __le16_to_cpu(req->credits)); /* Init response */ @@ -7492,14 +7546,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) goto next; l2cap_chan_lock(pchan); - chan = pchan->ops->new_connection(pchan); + chan = l2cap_new_connection(conn, pchan); if (chan) { bacpy(&chan->src, &hcon->src); bacpy(&chan->dst, &hcon->dst); chan->src_type = bdaddr_src_type(hcon); chan->dst_type = dst_type; - - __l2cap_chan_add(conn, chan); } l2cap_chan_unlock(pchan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index de56ca691afa..4058ff50cc27 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -43,7 +43,8 @@ static struct bt_sock_list l2cap_sk_list = { static const struct proto_ops l2cap_sock_ops; static void l2cap_sock_init(struct sock *sk, struct sock *parent); static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio, int kern); + int proto, gfp_t prio, int kern, + struct l2cap_chan *chan); static void l2cap_sock_cleanup_listen(struct sock *parent); bool l2cap_is_socket(struct socket *sock) @@ -1284,6 +1285,23 @@ done: return err; } +/* Release the sock's ref on chan and clear the pointer so that the ref is + * dropped exactly once even if both l2cap_sock_kill() and + * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other + * task from dereferencing the now-dead sock pointer. + */ +static void l2cap_sock_put_chan(struct sock *sk) +{ + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + + if (!chan) + return; + + chan->data = NULL; + l2cap_pi(sk)->chan = NULL; + l2cap_chan_put(chan); +} + /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket, with l2cap channel lock. */ @@ -1294,13 +1312,9 @@ static void l2cap_sock_kill(struct sock *sk) BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); - /* Sock is dead, so set chan data to NULL, avoid other task use invalid - * sock pointer. - */ - l2cap_pi(sk)->chan->data = NULL; - /* Kill poor orphan */ + l2cap_sock_put_chan(sk); - l2cap_chan_put(l2cap_pi(sk)->chan); + /* Kill poor orphan */ sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } @@ -1543,12 +1557,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) } } -static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) +static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { struct sock *sk, *parent = chan->data; if (!parent) - return NULL; + return -EINVAL; lock_sock(parent); @@ -1556,25 +1571,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); release_sock(parent); - return NULL; + return -ENOBUFS; } sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, - GFP_ATOMIC, 0); + GFP_ATOMIC, 0, new_chan); if (!sk) { release_sock(parent); - return NULL; - } + return -ENOMEM; + } bt_sock_reclassify_lock(sk, BTPROTO_L2CAP); l2cap_sock_init(sk, parent); + /* The conn list reference taken by l2cap_new_connection() keeps new_chan + * alive once release_sock() lets another task free this socket. + */ bt_accept_enqueue(parent, sk, false); release_sock(parent); - return l2cap_pi(sk)->chan; + return 0; } static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) @@ -1871,10 +1889,7 @@ static void l2cap_sock_destruct(struct sock *sk) BT_DBG("sk %p", sk); - if (l2cap_pi(sk)->chan) { - l2cap_pi(sk)->chan->data = NULL; - l2cap_chan_put(l2cap_pi(sk)->chan); - } + l2cap_sock_put_chan(sk); list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) { kfree_skb(rx_busy->skb); @@ -1907,30 +1922,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) BT_DBG("sk %p", sk); if (parent) { - struct l2cap_chan *pchan = l2cap_pi(parent)->chan; - sk->sk_type = parent->sk_type; bt_sk(sk)->flags = bt_sk(parent)->flags; - chan->chan_type = pchan->chan_type; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->conf_state = pchan->conf_state; - chan->mode = pchan->mode; - chan->fcs = pchan->fcs; - chan->max_tx = pchan->max_tx; - chan->tx_win = pchan->tx_win; - chan->tx_win_max = pchan->tx_win_max; - chan->sec_level = pchan->sec_level; - chan->flags = pchan->flags; - chan->tx_credits = pchan->tx_credits; - chan->rx_credits = pchan->rx_credits; - - if (chan->chan_type == L2CAP_CHAN_FIXED) { - chan->scid = pchan->scid; - chan->dcid = pchan->scid; - } - + /* Channel configuration is inherited from the parent by + * l2cap_new_connection(). + */ security_sk_clone(parent, sk); } else { switch (sk->sk_type) { @@ -1956,7 +1953,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->mode = L2CAP_MODE_BASIC; } - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); } /* Default config options */ @@ -1975,10 +1972,10 @@ static struct proto l2cap_proto = { }; static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio, int kern) + int proto, gfp_t prio, int kern, + struct l2cap_chan *chan) { struct sock *sk; - struct l2cap_chan *chan; sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern); if (!sk) @@ -1989,16 +1986,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy); - chan = l2cap_chan_create(); - if (!chan) { - sk_free(sk); - if (sock) - sock->sk = NULL; - return NULL; - } - - l2cap_chan_hold(chan); - + /* The sock takes ownership of the caller's reference on chan. */ l2cap_pi(sk)->chan = chan; return sk; @@ -2008,6 +1996,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; + struct l2cap_chan *chan; BT_DBG("sock %p", sock); @@ -2022,10 +2011,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, sock->ops = &l2cap_sock_ops; - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern); - if (!sk) + chan = l2cap_chan_create(); + if (!chan) return -ENOMEM; + sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan); + if (!sk) { + l2cap_chan_put(chan); + return -ENOMEM; + } + l2cap_sock_init(sk, NULL); bt_sock_link(&l2cap_sk_list, sk); return 0; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 031d3022cb1e..c4470958b0d5 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -3201,34 +3201,19 @@ static const struct l2cap_ops smp_chan_ops = { .get_sndtimeo = l2cap_chan_no_get_sndtimeo, }; -static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan) +static inline int smp_new_conn_cb(struct l2cap_chan *chan, + struct l2cap_chan *new_chan) { - struct l2cap_chan *chan; - - BT_DBG("pchan %p", pchan); - - chan = l2cap_chan_create(); - if (!chan) - return NULL; - - chan->chan_type = pchan->chan_type; - chan->ops = &smp_chan_ops; - chan->scid = pchan->scid; - chan->dcid = chan->scid; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->mode = pchan->mode; + new_chan->ops = &smp_chan_ops; /* Other L2CAP channels may request SMP routines in order to * change the security level. This means that the SMP channel * lock must be considered in its own category to avoid lockdep * warnings. */ - atomic_set(&chan->nesting, L2CAP_NESTING_SMP); - - BT_DBG("created chan %p", chan); + atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP); - return chan; + return 0; } static const struct l2cap_ops smp_root_chan_ops = { @@ -3288,7 +3273,7 @@ create_chan: l2cap_add_scid(chan, cid); - l2cap_chan_set_defaults(chan); + l2cap_chan_set_defaults(chan, NULL); if (cid == L2CAP_CID_SMP) { u8 bdaddr_type; -- cgit v1.2.3 From 9c36951474d8e1127f4946f39cb874a200f34e9f Mon Sep 17 00:00:00 2001 From: Kiran K Date: Tue, 30 Jun 2026 22:29:19 +0530 Subject: Bluetooth: btintel_pcie: Refactor FLR to use device_reprobe() The FLR branch in btintel_pcie_reset_work() open-coded the entire re-init sequence: btintel_pcie_release_hdev() (hci_unregister_dev + hci_free_dev), pci_try_reset_function(), enable_interrupts / config_msix / enable_bt / reset_ia / start_rx, then btintel_pcie_setup_hdev() (hci_alloc_dev_priv + hci_register_dev). Every probe() init step had to be kept in sync with this second copy in the reset path, and any failure mid-sequence left state to unwind by hand. The PLDR path already delegates teardown and re-init to the PCI core via device_reprobe(): .remove() destroys data through devres and unregisters hdev, then .probe() rebuilds everything from scratch. Apply the same model to FLR. Introduce btintel_pcie_perform_flr() mirroring perform_pldr(). It runs pci_try_reset_function() (required to avoid the device_lock ABBA against btintel_pcie_remove(), which calls disable_work_sync(&reset_work) while holding device_lock) followed by device_reprobe(). On success, data is destroyed and a fresh probe re-INIT_WORKs coredump_work with disable count 0, so enable_work() must not be called; on failure, data is still alive and the caller balances the earlier disable_work_sync(). The contract is documented on the helper and reiterated at the reset_work() call site. reset_work() shrinks to interrupt/worker drain, dispatch on reset_type, and the single asymmetry between the two paths. The out_enable label, the manual unregister/register pair, and the forward declaration of btintel_pcie_setup_hdev() are dropped. No intended functional change; FLR and PLDR now share one teardown contract. Fixes: 256ab9520d15 ("Bluetooth: btintel_pcie: Support Function level reset") Assisted-by: GitHub-Copilot:claude-4.7-opus Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel_pcie.c | 102 ++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 9e39327dc1fe..2b7231be5973 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -2127,6 +2127,9 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, if (test_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags)) return -ENODEV; + if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) + return -ENODEV; + /* Due to the fw limitation, the type header of the packet should be * 4 bytes unlike 1 byte for UART. In UART, the firmware can read * the first byte to get the packet type and redirect the rest of data @@ -2485,7 +2488,6 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev, } } -static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data); static void btintel_pcie_reset(struct hci_dev *hdev); static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data) @@ -2596,12 +2598,45 @@ static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data) } } +/* + * Issue a Function Level Reset and hand teardown/re-init off to the PCI + * core via device_reprobe(), mirroring the PLDR path's contract. + * + * Caller must hold pci_lock_rescan_remove() and must have already + * disabled interrupts and drained both rx_work and coredump_work. + */ +static int btintel_pcie_perform_flr(struct btintel_pcie_data *data) +{ + struct pci_dev *pdev = data->pdev; + int err; + + /* pci_try_reset_function() avoids the device_lock ABBA against + * btintel_pcie_remove(): .remove() runs with device_lock held and + * then waits for this work via disable_work_sync(); the blocking + * pci_reset_function() would deadlock by trying to re-acquire + * device_lock here. + */ + err = pci_try_reset_function(pdev); + if (err) { + BT_ERR("Failed resetting the pcie device (%d)", err); + return err; + } + + /* device_reprobe() always detaches the driver first (running + * .remove(), which frees 'data'); any re-probe failure leaves the + * device unbound but 'data' is already gone, so just log it. + */ + if (device_reprobe(&pdev->dev)) + BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev)); + + return 0; +} + static void btintel_pcie_reset_work(struct work_struct *wk) { struct btintel_pcie_data *data = container_of(wk, struct btintel_pcie_data, reset_work); struct pci_dev *pdev = data->pdev; - int err; pci_lock_rescan_remove(); @@ -2621,60 +2656,27 @@ static void btintel_pcie_reset_work(struct work_struct *wk) disable_work_sync(&data->coredump_work); bt_dev_dbg(data->hdev, "Release bluetooth interface"); + + /* Both reset paths follow the same contract: on success they + * destroy 'data' via device_reprobe() (a fresh probe re-INIT_WORKs + * the coredump_work with disable count 0), so enable_work() must + * NOT be called on the success path. Only the FLR path can fail + * with 'data' still alive, in which case we balance the + * disable_work_sync() above so a later successful reset is not + * permanently blocked. + * + * pci_lock_rescan_remove() (held above) serializes against PCI + * device addition/removal (hotplug), so no device can be added to + * or removed from the bus list while this code runs. + */ if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) { - /* This function holds pci_lock_rescan_remove(), which acquires - * pci_rescan_remove_lock. This mutex serializes against PCI device - * addition/removal (hotplug), so no device can be added to or - * removed from the bus list while this code runs. - * - * device_reprobe() inside btintel_pcie_perform_pldr() destroys - * 'data' via .remove(); a fresh probe re-INIT_WORKs the - * coredump_work with disable count 0, so we must not call - * enable_work() on this path. - */ btintel_pcie_perform_pldr(data); goto out; } - btintel_pcie_release_hdev(data); - - /* Use pci_try_reset_function() rather than pci_reset_function() to - * avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core - * calls .remove() with device_lock held, and remove() then waits for - * this work via cancel_work_sync(); pci_reset_function() would in - * turn try to acquire the same device_lock, deadlocking both paths. - */ - err = pci_try_reset_function(pdev); - if (err) { - BT_ERR("Failed resetting the pcie device (%d)", err); - goto out_enable; - } - btintel_pcie_enable_interrupts(data); - btintel_pcie_config_msix(data); - - err = btintel_pcie_enable_bt(data); - if (err) { - BT_ERR("Failed to enable bluetooth hardware after reset (%d)", - err); - goto out_enable; - } - - btintel_pcie_reset_ia(data); - btintel_pcie_start_rx(data); - data->flags = 0; + if (btintel_pcie_perform_flr(data)) + enable_work(&data->coredump_work); - err = btintel_pcie_setup_hdev(data); - if (err) { - BT_ERR("Failed registering hdev (%d)", err); - goto out_enable; - } - -out_enable: - /* Balance disable_work_sync() above on every exit. Leaving the - * counter incremented on a failed reset would permanently disable - * coredump_work even after a later successful reset. - */ - enable_work(&data->coredump_work); out: pci_dev_put(pdev); pci_unlock_rescan_remove(); -- cgit v1.2.3 From e054c1a6ae7310d2815778fddb87da616e11c255 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 1 Jul 2026 18:46:38 +0300 Subject: Bluetooth: ISO: fix malformed ISO_END/CONT handling Core specification (Part C vol 4 sec 5.4.5) does not exclude empty ISO_CONT, ISO_END packets. We currently reject them if they are last. If controller sends malformed sequence ISO_START -> rx_len = 4, ISO_CONT skb->len 4, ISO_START that ends payload in ISO_CONT, we leak conn->rx_skb. If controller sends too long ISO_END, we panic on skb_put. If controller sends too short ISO_END we accept it. Fix by marking unfinished ISO_START via conn->rx_skb != NULL. Check skb->len properly before skb_put. Combine the ISO_CONT/END code paths as they require the same initial checks. Reject too short ISO_END packets. Fixes: 84c24fb151fc ("Bluetooth: ISO: drop ISO_END frames received without prior ISO_START") Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index cd7c7c9ea4fc..2e95a153912c 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -2540,7 +2540,7 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) switch (pb) { case ISO_START: case ISO_SINGLE: - if (conn->rx_len) { + if (conn->rx_skb || conn->rx_len) { BT_ERR("Unexpected start frame (len %d)", skb->len); kfree_skb(conn->rx_skb); conn->rx_skb = NULL; @@ -2621,12 +2621,14 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) break; case ISO_CONT: - BT_DBG("Cont: frag len %d (expecting %d)", skb->len, + case ISO_END: + BT_DBG("%s: frag len %d (expecting %d)", + (pb == ISO_END) ? "End" : "Cont", skb->len, conn->rx_len); - if (!conn->rx_len) { - BT_ERR("Unexpected continuation frame (len %d)", - skb->len); + if (!conn->rx_skb) { + BT_ERR("Unexpected ISO %s frame (len %d)", + (pb == ISO_END) ? "End" : "Cont", skb->len); goto drop; } @@ -2642,17 +2644,9 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), skb->len); conn->rx_len -= skb->len; - break; - case ISO_END: - if (!conn->rx_len) { - BT_ERR("Unexpected end frame (len %d)", skb->len); - goto drop; - } - - skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); - conn->rx_len -= skb->len; + if (pb == ISO_CONT) + break; if (!conn->rx_len) { struct sk_buff *rx_skb = conn->rx_skb; @@ -2663,6 +2657,13 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) */ conn->rx_skb = NULL; iso_recv_frame(conn, rx_skb); + } else { + BT_ERR("ISO fragment incomplete (len %d, expected %d)", + skb->len, conn->rx_len); + kfree_skb(conn->rx_skb); + conn->rx_skb = NULL; + conn->rx_len = 0; + goto drop; } break; } -- cgit v1.2.3 From fd076d8deeab6f9f18ef13400f89e1f550df665b Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 1 Jul 2026 18:46:39 +0300 Subject: Bluetooth: ISO: exclude RFU bits from ISO_SDU_Length slen contains ISO_SDU_Length (12 bits), RFU (2 bits), Packet_Status_Flags (2 bits). Exclude the RFU bits from hci_iso_data_len. Also add masks to the pack macro. Fixes: 4de0fc599eb9 ("Bluetooth: Add definitions for CIS connections") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 38186a245f14..50f0eef71fb1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -3413,8 +3413,9 @@ static inline struct hci_iso_hdr *hci_iso_hdr(const struct sk_buff *skb) #define hci_iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2)) /* ISO data length and flags pack/unpack */ -#define hci_iso_data_len_pack(h, f) ((__u16) ((h) | ((f) << 14))) -#define hci_iso_data_len(h) ((h) & 0x3fff) +#define hci_iso_data_len_pack(h, f) ((__u16) (((h) & 0x0fff) | \ + (((f) & 0x3) << 14))) +#define hci_iso_data_len(h) ((h) & 0x0fff) #define hci_iso_data_flags(h) ((h) >> 14) /* codec transport types */ -- cgit v1.2.3 From dd068ef044128db655f48323a4acfd5907e04903 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 1 Jul 2026 09:06:14 -0700 Subject: Bluetooth: bpa10x: avoid OOB read of revision string in bpa10x_setup() bpa10x_setup() sends the vendor command 0xfc0e and passes the response to bt_dev_info() and hci_set_fw_info() as a "%s" string starting at skb->data + 1, without checking the length: bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); hci_set_fw_info(hdev, "%s", skb->data + 1); A device that returns a one-byte response (status only) leaves skb->data + 1 past the end of the data, and the %s walk reads adjacent slab memory until it meets a NUL. The same happens when the payload is not NUL-terminated within skb->len. The out-of-bounds bytes end up in the kernel log and the firmware-info debugfs file. Print the revision string with a bounded "%.*s" limited to skb->len - 1 instead. This keeps the string readable for well-behaved devices while never reading past the received data, and does not fail setup, so a device returning a short or unterminated response keeps working. Fixes: ddd68ec8f484 ("Bluetooth: bpa10x: Read revision information in setup stage") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi Reported-by: Xiang Mei Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/bpa10x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 2ae38a321c4b..e63d1af250ec 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -255,9 +255,13 @@ static int bpa10x_setup(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); + /* Bounded print: the device controls skb->len. */ + if (skb->len > 1) { + int len = skb->len - 1; - hci_set_fw_info(hdev, "%s", skb->data + 1); + bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1)); + hci_set_fw_info(hdev, "%.*s", len, skb->data + 1); + } kfree_skb(skb); return 0; -- cgit v1.2.3 From 6e1930ece855a4c256f1c7e6632d634cfb9888b5 Mon Sep 17 00:00:00 2001 From: Stig Hornang Date: Fri, 12 Jun 2026 16:38:18 +0200 Subject: Bluetooth: L2CAP: fix tx ident leak for commands without a response Commit 6c3ea155e5ee ("Bluetooth: L2CAP: Fix not tracking outstanding TX ident") changed ident allocation to use an IDA, releasing idents in l2cap_put_ident() when the matching response command is received. But identifiers allocated for commands that have no response defined are never released. In particular L2CAP_LE_CREDITS is sent repeatedly for the lifetime of an LE CoC channel, so a peer streaming data to the host exhausts the 1-255 ident range after 254 credit packets. From then on l2cap_get_ident() fails: kernel: Bluetooth: Unable to allocate ident: -28 and every subsequent L2CAP_LE_CREDITS packet is sent with ident 0, which is invalid (Core Spec, Vol 3, Part A, Section 4: "Signaling identifier 0x00 is an invalid identifier and shall never be used in any command"). Remote stacks that validate the ident drop these commands, never receive new credits, and the channel stalls permanently. With default socket buffers this happens after roughly 0.5 MB of received data (the exact amount depends on the socket receive buffer): < ACL Data TX: Handle 2048 flags 0x00 dlen 12 LE L2CAP: LE Flow Control Credit (0x16) ident 0 len 4 Source CID: 64 Credits: 1 Release the ident immediately after sending L2CAP_LE_CREDITS since no response will ever release it. Use a local variable instead of chan->ident so that an ident that an EXT_FLOWCTL channel may be waiting on (e.g. a pending reconfigure) is not overwritten by a credit packet. Also add the missing L2CAP_LE_CONN_RSP case to l2cap_put_ident() so idents allocated for outgoing L2CAP_LE_CONN_REQ commands are released when the response arrives. Fixes: 6c3ea155e5ee ("Bluetooth: L2CAP: Fix not tracking outstanding TX ident") Link: https://bugzilla.kernel.org/show_bug.cgi?id=221629 Assisted-by: Claude:claude-opus-4.8 Assisted-by: Fable:5 Signed-off-by: Stig Hornang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 519cd9552d86..538ae9aa3479 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4879,6 +4879,7 @@ static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id) case L2CAP_ECHO_RSP: case L2CAP_INFO_RSP: case L2CAP_CONN_PARAM_UPDATE_RSP: + case L2CAP_LE_CONN_RSP: case L2CAP_ECRED_CONN_RSP: case L2CAP_ECRED_RECONF_RSP: /* First do a lookup since the remote may send bogus ids that @@ -6772,6 +6773,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) struct l2cap_conn *conn = chan->conn; struct l2cap_le_credits pkt; u16 return_credits = l2cap_le_rx_credits(chan); + int ident; if (chan->mode != L2CAP_MODE_LE_FLOWCTL && chan->mode != L2CAP_MODE_EXT_FLOWCTL) @@ -6789,9 +6791,18 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) pkt.cid = cpu_to_le16(chan->scid); pkt.credits = cpu_to_le16(return_credits); - chan->ident = l2cap_get_ident(conn); + ident = l2cap_get_ident(conn); - l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); + l2cap_send_cmd(conn, ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); + + /* L2CAP_LE_CREDITS has no response so the ident is never released by + * l2cap_put_ident() - release it right away, otherwise the tx_ida + * range is exhausted after 254 packets and from then on credits are + * sent with the invalid ident 0, which some remote stacks ignore, + * stalling the channel. + */ + if (ident > 0) + ida_free(&conn->tx_ida, ident); } void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail) -- cgit v1.2.3 From 3d04d9f390eeaab4d9e1ed4e9737e3d83581e18b Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Mon, 22 Jun 2026 15:47:36 +0530 Subject: 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; } -- cgit v1.2.3 From c505d54c1a9e99d36d322ced6c9c71a1157424b0 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 1 Jul 2026 16:44:26 +0200 Subject: 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 --- Documentation/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: -- cgit v1.2.3 From 718178f524b98bc920d74bc771aed823c8b81425 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 2 Jul 2026 16:27:45 +0800 Subject: 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); -- cgit v1.2.3 From 86152fef52cac15cd662ed3bfc7604fbfef378f0 Mon Sep 17 00:00:00 2001 From: Ao Sun Date: Mon, 6 Jul 2026 11:43:00 +0000 Subject: 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 @@ out_put_device: } 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); } -- cgit v1.2.3 From 8b0b864c11a2e2ada470f9d5010e1c2bf1eceef2 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Tue, 23 Jun 2026 16:22:15 +0200 Subject: 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); -- cgit v1.2.3 From 3a07249981629ace483ebbef81ef6b34c2d2afec Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sat, 27 Jun 2026 11:51:05 +0100 Subject: 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; - - iodev = bus->range[dev_idx].dev; - -out_unlock: - srcu_read_unlock(&kvm->srcu, srcu_idx); + return NULL; - return iodev; + return bus->range[dev_idx].dev; } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_io_bus_get_dev); -- cgit v1.2.3 From 100baf0184896f859290a684f864b8200d8ac872 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Wed, 1 Jul 2026 16:16:19 -0700 Subject: 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) -- cgit v1.2.3 From f35c08c092505f3a83ce097d94fe51eb8bc9c1b5 Mon Sep 17 00:00:00 2001 From: Oliver Upton Date: Wed, 1 Jul 2026 16:16:20 -0700 Subject: 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) -- cgit v1.2.3 From 85f56708a443ec02a290878f00d79a1ff5110e41 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 6 Jul 2026 12:55:21 +0100 Subject: 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); } -- cgit v1.2.3 From 65a38ddeaeed1a962bc1b4ca03c98201c877fea5 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 6 Jul 2026 12:55:22 +0100 Subject: 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 + tools/testing/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(); +} -- cgit v1.2.3 From 15d9968b68e848a1a38ca875ba553b05dadae03d Mon Sep 17 00:00:00 2001 From: Yu Peng Date: Wed, 3 Jun 2026 14:25:32 +0800 Subject: 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: -- cgit v1.2.3 From c37e0a4b79a6bbb96ce5ffe279d7c001e20529e0 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sun, 7 Jun 2026 15:24:30 +0800 Subject: 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; } -- cgit v1.2.3 From fe58f457ad8d0a2bef4e053cfecca4b5cd266b1a Mon Sep 17 00:00:00 2001 From: Crystal Wood Date: Mon, 8 Jun 2026 23:54:30 -0500 Subject: 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); } /* -- cgit v1.2.3 From a20b08de862c2983eac183b7560ad678acb58608 Mon Sep 17 00:00:00 2001 From: "Wayen.Yan" Date: Thu, 11 Jun 2026 11:52:57 +0800 Subject: 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) { -- cgit v1.2.3 From eb525edd48907795c0d4e498ff57ad168070b289 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 30 Jun 2026 08:53:19 +0000 Subject: clk: spacemit: k3: set hdma clock as critical HDMA clock is responsible for the internal TCM access path of X100 RISC-V core, so set the clock flag as critical to prevent it from being shut off, otherwise the Linux system will hang, for example in the case of a vector instruction access generates a page fault. Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Reported-by: Han Gao Tested-by: Aurelien Jarno Link: https://patch.msgid.link/20260630-06-clk-hdma-critial-v1-1-443c0ac88c5f@kernel.org Signed-off-by: Yixun Lan --- drivers/clk/spacemit/ccu-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 03de04144963..764426359540 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -1026,7 +1026,7 @@ CCU_MUX_DIV_GATE_DEFINE(isim_vclk_out3, isim_vclk_parents, APMU_SNR_ISIM_VCLK_CT /* APMU clocks end */ /* DCIU clocks start */ -CCU_GATE_DEFINE(hdma_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_CLK_EN, BIT(0), 0); +CCU_GATE_DEFINE(hdma_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_CLK_EN, BIT(0), CLK_IS_CRITICAL); CCU_GATE_DEFINE(dma350_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_SDMA_CLK_EN, BIT(0), 0); CCU_GATE_DEFINE(c2_tcm_pipe_clk, CCU_PARENT_HW(axi_clk), DCIU_C2_TCM_PIPE_CLK, BIT(0), 0); CCU_GATE_DEFINE(c3_tcm_pipe_clk, CCU_PARENT_HW(axi_clk), DCIU_C3_TCM_PIPE_CLK, BIT(0), 0); -- cgit v1.2.3 From b983c56426383e4a06fa5970c4e33cee879b1482 Mon Sep 17 00:00:00 2001 From: Farhad Alemi Date: Mon, 6 Jul 2026 10:20:23 +0200 Subject: 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 -- cgit v1.2.3 From cead34ac1ce10046cb745fbc33a4b21cac899753 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Thu, 28 May 2026 14:38:41 -0700 Subject: MAINTAINERS: update ndesaulniers I'm coming back. I will return. I will possess your body, and I'll make LKML burn. Signed-off-by: Nick Desaulniers Acked-by: Nathan Chancellor Acked-by: Will Deacon Signed-off-by: Linus Torvalds --- .mailmap | 1 - Documentation/process/embargoed-hardware-issues.rst | 2 +- Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst | 2 +- MAINTAINERS | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index e7e639aeb23c..833ccaa583d6 100644 --- a/.mailmap +++ b/.mailmap @@ -642,7 +642,6 @@ Nicholas Piggin Nicholas Piggin Nicholas Piggin Nicholas Piggin -Nick Desaulniers Nicolas Ferre Nicolas Pitre Nicolas Pitre diff --git a/Documentation/process/embargoed-hardware-issues.rst b/Documentation/process/embargoed-hardware-issues.rst index 34e00848e0da..d07f16c3c7b8 100644 --- a/Documentation/process/embargoed-hardware-issues.rst +++ b/Documentation/process/embargoed-hardware-issues.rst @@ -308,7 +308,7 @@ an involved disclosed party. The current ambassadors list: Google Kees Cook - LLVM Nick Desaulniers + LLVM Nick Desaulniers ============= ======================================================== If you want your organization to be added to the ambassadors list, please diff --git a/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst b/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst index 9d444b9c46d3..7d4d694967c7 100644 --- a/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst +++ b/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst @@ -287,7 +287,7 @@ revelada involucrada. La lista de embajadores actuales: Google Kees Cook - LLVM Nick Desaulniers + LLVM Nick Desaulniers ============= ======================================================== Si quiere que su organización se añada a la lista de embajadores, por diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b0fd665ce..57a7aef5d8cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6342,7 +6342,7 @@ F: .clang-format CLANG/LLVM BUILD SUPPORT M: Nathan Chancellor -R: Nick Desaulniers +R: Nick Desaulniers R: Bill Wendling R: Justin Stitt L: llvm@lists.linux.dev -- cgit v1.2.3 From 9119ceb76e987c2ec2b549ea100e3268ce3a1c7c Mon Sep 17 00:00:00 2001 From: Tze Yee Ng Date: Wed, 24 Jun 2026 03:06:35 -0700 Subject: firmware: stratix10-svc: fix memory leaks and list corruption bugs Fix a memory leak when gen_pool_alloc() fails by freeing pmem on the error path. Switch pmem allocation from devm_kzalloc() to kzalloc() with explicit kfree() in the free path to match its list-managed lifetime. Remove the erroneous list_del(&svc_data_mem) which corrupted the list head on failed lookups. Fixes: 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver") Cc: stable@vger.kernel.org # 5.0+ Signed-off-by: Tze Yee Ng Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index c24ca5823078..6306b79a14a1 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1857,14 +1857,16 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan, struct gen_pool *genpool = chan->ctrl->genpool; size_t s = roundup(size, 1 << genpool->min_alloc_order); - pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL); + pmem = kzalloc_obj(*pmem); if (!pmem) return ERR_PTR(-ENOMEM); guard(mutex)(&svc_mem_lock); va = gen_pool_alloc(genpool, s); - if (!va) + if (!va) { + kfree(pmem); return ERR_PTR(-ENOMEM); + } memset((void *)va, 0, s); pa = gen_pool_virt_to_phys(genpool, va); @@ -1890,6 +1892,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_allocate_memory); void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) { struct stratix10_svc_data_mem *pmem; + guard(mutex)(&svc_mem_lock); list_for_each_entry(pmem, &svc_data_mem, node) @@ -1898,10 +1901,9 @@ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr) (unsigned long)kaddr, pmem->size); pmem->vaddr = NULL; list_del(&pmem->node); + kfree(pmem); return; } - - list_del(&svc_data_mem); } EXPORT_SYMBOL_GPL(stratix10_svc_free_memory); -- cgit v1.2.3 From 9b9a6e31bdd1ff20c3ffdab87431672d8bfc2a07 Mon Sep 17 00:00:00 2001 From: Genevieve Chan Date: Mon, 6 Jul 2026 13:55:41 +0800 Subject: firmware: stratix10-svc: fix FCS SMC call kernel-doc Correct kernel-doc errors for two FCS SMC calls: INTEL_SIP_SMC_FCS_SEND_CERTIFICATE: - Describe as async to match STD_CALL_VAL usage - Replace INTEL_SIP_SMC_FCS_REJECTED with INTEL_SIP_SMC_REJECTED INTEL_SIP_SMC_FCS_GET_PROVISION_DATA: - Replace FCS-specific status macros with standard status macros (INTEL_SIP_SMC_STATUS_ERROR and INTEL_SIP_SMC_STATUS_REJECTED) - Restore return register documentation for a1 (mailbox error), a2 (physical address), and a3 (structure size) Fixes: 4a4709d470e6 ("firmware: stratix10-svc: add new FCS commands") Fixes: 4b0a32016347 ("firmware: stratix10-svc: change get provision data to async SMC call") Cc: stable@vger.kernel.org # 6.0+ Signed-off-by: Genevieve Chan Signed-off-by: Dinh Nguyen --- include/linux/firmware/intel/stratix10-smc.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 9116512169dc..2c603a24967f 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -606,7 +606,7 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) /** * Request INTEL_SIP_SMC_FUNCID_FCS_SEND_CERTIFICATE - * Sync call to send a signed certificate + * Async call to send a signed certificate * * Call register usage: * a0 INTEL_SIP_SMC_FCS_SEND_CERTIFICATE @@ -615,7 +615,7 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) * a3-a7 not used * * Return status: - * a0 INTEL_SIP_SMC_STATUS_OK or INTEL_SIP_SMC_FCS_REJECTED + * a0 INTEL_SIP_SMC_STATUS_OK or INTEL_SIP_SMC_REJECTED * a1-a3 not used */ #define INTEL_SIP_SMC_FUNCID_FCS_SEND_CERTIFICATE 93 @@ -631,9 +631,11 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) * a1-a7 not used * * Return status: - * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_FCS_ERROR or - * INTEL_SIP_SMC_FCS_REJECTED - * a1-a3 not used + * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_STATUS_ERROR or + * INTEL_SIP_SMC_STATUS_REJECTED + * a1 mailbox error if a0 is INTEL_SIP_SMC_STATUS_ERROR + * a2 physical address for the structure of fuse and key hashes + * a3 the size of structure * */ #define INTEL_SIP_SMC_FUNCID_FCS_GET_PROVISION_DATA 94 -- cgit v1.2.3 From 81bbcff0c053c4f5c711c31a9b72fc492bd96c3f Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Mon, 6 Jul 2026 11:27:07 +1200 Subject: 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))) -- cgit v1.2.3 From ca0676ae2e1a23ac3e858224fc630b99f9e216ea Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 6 Jul 2026 15:46:21 -0700 Subject: 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 @@ out_mondata: kernfs_remove(kn_mondata); out_mongrp: if (resctrl_arch_mon_capable()) { + mon_put_kn_priv(); rdtgroup_unassign_cntrs(&rdtgroup_default); kernfs_remove(kn_mongrp); } -- cgit v1.2.3 From cd053d788c3f13b3eaf16672d427ee828fda16ed Mon Sep 17 00:00:00 2001 From: Hao-Yu Yang Date: Tue, 7 Jul 2026 02:33:04 +0800 Subject: 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; -- cgit v1.2.3 From 07db10de262f4150e24fd631a7a6c428f7bf80c9 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 29 Apr 2026 01:27:12 +0300 Subject: arm64: dts: qcom: sdm850-lenovo-yoga-c630: lower PSCI cluster idle With the default PSCI suspend value for cluster idle state Lenovo Yoga C630 isn't stable enough. For example it might reset if display device isn't probed early enough. Drop the bit 0x4000 from the PSCI suspend value to make C630 work in stable way. The bit was found by expertimenting with the cluster idle PSCI value. Most likely it results in the less deep sleep and more energy beign spent in the suspend state, but it's better than the non-stable system behaviour. Fixes: a1ade6cac5a2 ("arm64: dts: qcom: sdm845: Switch PSCI cpu idle states from PC to OSI") Signed-off-by: Dmitry Baryshkov Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20260429-c630-fix-idle-v2-1-ac867dad6f21@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts index e41200839dbe..8cdaef5360d1 100644 --- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts +++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts @@ -347,6 +347,10 @@ status = "okay"; }; +&cluster_sleep_0 { + arm,psci-suspend-param = <0x41008244>; +}; + &gcc { protected-clocks = , , -- cgit v1.2.3 From 52fce648607e0d6a76eeb443d78708c49df1c554 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 6 Jul 2026 15:46:22 -0700 Subject: 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 @@ unlock: * * 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; } -- cgit v1.2.3 From b9f089723aee892efc77c349ae47a6b452b293c4 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Mon, 6 Jul 2026 15:46:23 -0700 Subject: 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); -- cgit v1.2.3 From 0aec16a93bac6b2db928119bd34cc1e896173af4 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 22 Jun 2026 12:46:05 +0200 Subject: Revert "clk: qcom: regmap-phy-mux: Rework the implementation" This reverts commit e108373c54fbc844b7f541c6fd7ecb31772afd3c. This has been reported to break PCIe on at least SM8350 and Eliza platforms. I had originally tested this on Hamoa (X1E) where there were no adverse effects. It's highly likely that this stems from a difference in how the bootloader configures the clocks. Revert the offending change to fix the issue in the immediate, with the intent to revisit it in the upcoming cycle. Reported-by: Dmitry Baryshkov Reported-by: Abel Vesa Signed-off-by: Konrad Dybcio Fixes: e108373c54fb ("clk: qcom: regmap-phy-mux: Rework the implementation") Closes: https://lore.kernel.org/all/c675lcfptr4xgg4hcjp66unmuozgsvgwvtymh7on6jcipjrdw7@jy4h7fkwqwjg/ Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260622-topic-phymux_revert-v1-1-f6ec85523840@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/clk/qcom/clk-regmap-phy-mux.c | 52 ++++++++++++----------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/drivers/clk/qcom/clk-regmap-phy-mux.c b/drivers/clk/qcom/clk-regmap-phy-mux.c index b7d1c69d62f7..7b7243b7107d 100644 --- a/drivers/clk/qcom/clk-regmap-phy-mux.c +++ b/drivers/clk/qcom/clk-regmap-phy-mux.c @@ -15,66 +15,48 @@ #define PHY_MUX_PHY_SRC 0 #define PHY_MUX_REF_SRC 2 -#define XO_RATE 19200000UL - static inline struct clk_regmap_phy_mux *to_clk_regmap_phy_mux(struct clk_regmap *clkr) { return container_of(clkr, struct clk_regmap_phy_mux, clkr); } -static unsigned long phy_mux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +static int phy_mux_is_enabled(struct clk_hw *hw) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - u32 val; + unsigned int val; regmap_read(clkr->regmap, phy_mux->reg, &val); + val = FIELD_GET(PHY_MUX_MASK, val); + + WARN_ON(val != PHY_MUX_PHY_SRC && val != PHY_MUX_REF_SRC); - switch (FIELD_GET(PHY_MUX_MASK, val)) { - case PHY_MUX_PHY_SRC: - return ULONG_MAX; - case PHY_MUX_REF_SRC: - return XO_RATE; - default: - return 0; - } + return val == PHY_MUX_PHY_SRC; } -static int phy_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +static int phy_mux_enable(struct clk_hw *hw) { - if (req->rate == XO_RATE || req->rate == ULONG_MAX) - return 0; + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - return -EINVAL; + return regmap_update_bits(clkr->regmap, phy_mux->reg, + PHY_MUX_MASK, + FIELD_PREP(PHY_MUX_MASK, PHY_MUX_PHY_SRC)); } -static int phy_mux_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) +static void phy_mux_disable(struct clk_hw *hw) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - u32 val; - - switch (rate) { - case XO_RATE: - val = PHY_MUX_REF_SRC; - break; - case ULONG_MAX: - val = PHY_MUX_PHY_SRC; - break; - default: - return -EINVAL; - } regmap_update_bits(clkr->regmap, phy_mux->reg, PHY_MUX_MASK, - FIELD_PREP(PHY_MUX_MASK, val)); - - return 0; + FIELD_PREP(PHY_MUX_MASK, PHY_MUX_REF_SRC)); } const struct clk_ops clk_regmap_phy_mux_ops = { - .recalc_rate = phy_mux_recalc_rate, - .determine_rate = phy_mux_determine_rate, - .set_rate = phy_mux_set_rate, + .enable = phy_mux_enable, + .disable = phy_mux_disable, + .is_enabled = phy_mux_is_enabled, }; EXPORT_SYMBOL_GPL(clk_regmap_phy_mux_ops); -- cgit v1.2.3 From e35c9cf5512814fb04f369f2eada64f0a7164609 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 16 Jun 2026 14:24:29 -0700 Subject: 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; -- cgit v1.2.3 From 18aaebdf43366954345a6721ed2bff3ac3c9fa61 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 15 Jun 2026 23:15:32 -0700 Subject: 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)) { -- cgit v1.2.3 From 1ba02717e821cf14ece642273958647e79698d3d Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 8 Jun 2026 18:12:42 -0700 Subject: 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 @@ again: 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; -- cgit v1.2.3 From f3858d5b1432098c1936e03d6e03dd0e33facf60 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Mon, 6 Jul 2026 22:08:41 +0800 Subject: 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) -- cgit v1.2.3 From 95fc02722edde02946d0d475221f2b2054d3d8ba Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Mon, 6 Jul 2026 22:35:07 +0800 Subject: 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); -- cgit v1.2.3 From 0c2ed186bbe14304415476d6707b747dddcd8583 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Mon, 6 Jul 2026 23:24:18 +0800 Subject: 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) { -- cgit v1.2.3 From 4e72459683b5185568e9ffe2584a7b834f7902b5 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Mon, 6 Jul 2026 22:27:52 +0300 Subject: 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; -- cgit v1.2.3 From d0e69d9afa59b93c30294eba89b1f15f69e91105 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Mon, 6 Jul 2026 22:37:56 +0300 Subject: 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); -- cgit v1.2.3 From d5e4586546974179feca305a94e07fac3e9727fe Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Tue, 7 Jul 2026 10:53:34 +0800 Subject: 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 | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 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 (len < sizeof(fc)) + return; + + fc = mgmt->frame_control; - if (WARN_ON(len < 2)) + 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(mgmt->frame_control)) + 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 (len < sizeof(fc)) + return; - if (WARN_ON(len < 2)) + 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(mgmt->frame_control)) + if (ieee80211_is_deauth(fc)) cfg80211_process_deauth(wdev, buf, len, reconnect); else cfg80211_process_disassoc(wdev, buf, len, reconnect); -- cgit v1.2.3 From b760113aeca2e9362d56bf9e9263373ffe6c8eb3 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Tue, 7 Jul 2026 10:53:35 +0800 Subject: 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); -- cgit v1.2.3 From 5b4396e12cb10fa89ca0a272fae2b5408cfeaa91 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 2 Jul 2026 09:19:58 -0700 Subject: 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. -- cgit v1.2.3 From 34dd2253414d228fc9bf8a70b5f9f83107eda3e1 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 2 Jul 2026 09:19:59 -0700 Subject: 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)) -- cgit v1.2.3 From 2a665946e0407a05a3f81bd56a08553c446498e0 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 19 Jun 2026 14:44:01 +0800 Subject: 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 */ -- cgit v1.2.3 From 3be28e2c9cd0230cb51fd4967df095273afd3848 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 30 Jun 2026 15:15:51 -0400 Subject: net/tls: Consume empty data records in tls_sw_read_sock() A peer may send a zero-length TLS application_data record; TLS 1.3 explicitly permits these as a traffic-analysis countermeasure (RFC 8446, Section 5.1). After decryption such a record has full_len == 0. tls_sw_read_sock() hands it to the read_actor, which has no payload to consume and returns zero. The loop treats a zero return as backpressure (used <= 0), requeues the skb at the head of rx_list, and stops. rx_list is serviced head-first on the next call, so the empty record is dequeued, fails the same way, and is requeued again; every later record on the connection is blocked behind it. tls_sw_recvmsg() does not stall on this: a zero-length data record copies nothing and falls through to consume_skb(). Mirror that in the read_sock() path by recognizing an empty data record before the actor runs, consuming it, and continuing. Fixes: 662fbcec32f4 ("net/tls: implement ->read_sock()") Signed-off-by: Chuck Lever Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260630191551.875664-1-cel@kernel.org Signed-off-by: Paolo Abeni --- net/tls/tls_sw.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 9324e4ed20a3..d4afc90fd796 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2115,6 +2115,17 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, goto read_sock_requeue; } + /* An empty data record (legal in TLS 1.3) gives a zero + * read_actor return, indistinguishable from the consumer + * stalling; the used <= 0 path would requeue it at the + * head of rx_list and block all later records. Consume it + * here instead. + */ + if (rxm->full_len == 0) { + consume_skb(skb); + continue; + } + used = read_actor(desc, skb, rxm->offset, rxm->full_len); if (used <= 0) { if (!copied) -- cgit v1.2.3 From 240c8d2c717b3f8153e7e877b22a82518d78dbdc Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Sat, 27 Jun 2026 21:13:13 +0800 Subject: 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); -- cgit v1.2.3 From d9d6d67f4c0877fde783c9d5beee013bcf1b1e85 Mon Sep 17 00:00:00 2001 From: Dong Yibo Date: Wed, 1 Jul 2026 11:22:08 +0800 Subject: net: rnpgbe: fix mailbox endianness and remove pointer casts The rnpgbe mailbox exchanges data through 32-bit MMIO registers in little-endian wire format. The original code had two problems: 1. FW structs (with __le16/__le32 fields) were cast to (u32 *) before reaching the mailbox transport, hiding the endian annotations from sparse. 2. No cpu_to_le32()/le32_to_cpu() conversion was done between CPU-endian MMIO values and the little-endian payload, causing data corruption on big-endian systems. Fix by adding the missing byte-order conversions in the transport layer and introducing union wrappers (mbx_fw_cmd_req_u, mbx_fw_cmd_reply_u) that overlay each FW struct with a __le32 dwords[] array. Callers fill named fields using cpu_to_le16/32(), then pass dwords[] to the transport, which now takes explicit __le32 * instead of u32 *. This eliminates all pointer casts on the mailbox data path and lets sparse verify the conversions. Fixes: 4543534c3ef5 ("net: rnpgbe: Add basic mbx ops support") Signed-off-by: Dong Yibo Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260701032208.1843156-2-dong100@mucse.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c | 26 ++++--- drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h | 5 +- drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c | 82 +++++++++++++---------- drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h | 14 ++++ 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c index de5e29230b3c..c46408698263 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c @@ -166,18 +166,23 @@ static void mucse_mbx_inc_pf_ack(struct mucse_hw *hw) * * Return: 0 on success, negative errno on failure **/ -static int mucse_read_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) +static int mucse_read_mbx_pf(struct mucse_hw *hw, __le32 *msg, u16 size) { - const int size_in_words = size / sizeof(u32); + const int size_in_words = size / sizeof(__le32); struct mucse_mbx_info *mbx = &hw->mbx; + int off = MUCSE_MBX_FWPF_SHM; int err; err = mucse_obtain_mbx_lock_pf(hw); if (err) return err; + /* memcpy_fromio() is unsuitable: the mailbox uses 32-bit MMIO + * registers, not byte-addressable RAM. readl() guarantees + * the required 32-bit access width. + */ for (int i = 0; i < size_in_words; i++) - msg[i] = mbx_data_rd32(mbx, MUCSE_MBX_FWPF_SHM + 4 * i); + msg[i] = cpu_to_le32(mbx_data_rd32(mbx, off + 4 * i)); /* Hw needs write data_reg at last */ mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0); /* flush reqs as we have read this request data */ @@ -236,7 +241,7 @@ static int mucse_poll_for_msg(struct mucse_hw *hw) * Return: 0 if it successfully received a message notification and * copied it into the receive buffer, negative errno on failure **/ -int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size) +int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size) { int err; @@ -290,9 +295,9 @@ static void mucse_mbx_inc_pf_req(struct mucse_hw *hw) * Return: 0 if it successfully copied message into the buffer, * negative errno on failure **/ -static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) +static int mucse_write_mbx_pf(struct mucse_hw *hw, const __le32 *msg, u16 size) { - const int size_in_words = size / sizeof(u32); + const int size_in_words = size / sizeof(__le32); struct mucse_mbx_info *mbx = &hw->mbx; int err; @@ -300,8 +305,12 @@ static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size) if (err) return err; + /* memcpy_toio() would decompose into arbitrary-width accesses; + * the mailbox requires 32-bit MMIO writes via writel(). + */ for (int i = 0; i < size_in_words; i++) - mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, msg[i]); + mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, + le32_to_cpu(msg[i])); /* flush acks as we are overwriting the message buffer */ hw->mbx.fw_ack = mucse_mbx_get_fwack(mbx); @@ -360,7 +369,8 @@ static int mucse_poll_for_ack(struct mucse_hw *hw) * Return: 0 if it successfully copied message into the buffer and * received an ack to that message within delay * timeout_cnt period **/ -int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size) +int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, const __le32 *msg, + u16 size) { int err; diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h index e6fcc8d1d3ca..75b88b18b04d 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h @@ -14,7 +14,8 @@ #define MUCSE_MBX_REQ BIT(0) /* Request a req to mailbox */ #define MUCSE_MBX_PFU BIT(3) /* PF owns the mailbox buffer */ -int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size); +int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, + const __le32 *msg, u16 size); void mucse_init_mbx_params_pf(struct mucse_hw *hw); -int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size); +int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size); #endif /* _RNPGBE_MBX_H */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c index 8c8bd5e8e1db..5ba74997beac 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c @@ -20,32 +20,32 @@ * Return: 0 on success, negative errno on failure **/ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw, - struct mbx_fw_cmd_req *req, - struct mbx_fw_cmd_reply *reply) + union mbx_fw_cmd_req_u *req, + union mbx_fw_cmd_reply_u *reply) { - int len = le16_to_cpu(req->datalen); + int len = le16_to_cpu(req->r.datalen); int retry_cnt = 3; int err; mutex_lock(&hw->mbx.lock); - err = mucse_write_and_wait_ack_mbx(hw, (u32 *)req, len); + err = mucse_write_and_wait_ack_mbx(hw, req->dwords, len); if (err) goto out; do { - err = mucse_poll_and_read_mbx(hw, (u32 *)reply, - sizeof(*reply)); + err = mucse_poll_and_read_mbx(hw, reply->dwords, + sizeof(reply->r)); if (err) goto out; /* mucse_write_and_wait_ack_mbx return 0 means fw has * received request, wait for the expect opcode * reply with 'retry_cnt' times. */ - } while (--retry_cnt >= 0 && reply->opcode != req->opcode); + } while (--retry_cnt >= 0 && reply->r.opcode != req->r.opcode); out: mutex_unlock(&hw->mbx.lock); if (!err && retry_cnt < 0) return -ETIMEDOUT; - if (!err && reply->error_code) + if (!err && reply->r.error_code) return -EIO; return err; @@ -61,17 +61,19 @@ out: **/ static int mucse_mbx_get_info(struct mucse_hw *hw) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(GET_HW_INFO), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(GET_HW_INFO), + }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; int err; err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply); if (!err) hw->pfvfnum = FIELD_GET(GENMASK_U16(7, 0), - le16_to_cpu(reply.hw_info.pfnum)); + le16_to_cpu(reply.r.hw_info.pfnum)); return err; } @@ -111,21 +113,23 @@ int mucse_mbx_sync_fw(struct mucse_hw *hw) **/ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(sizeof(req.powerup) + - MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(POWER_UP), - .powerup = { - /* fw needs this to reply correct cmd */ - .version = cpu_to_le32(GENMASK_U32(31, 0)), - .status = cpu_to_le32(is_powerup ? 1 : 0), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(sizeof(req.r.powerup) + + MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(POWER_UP), + .powerup = { + /* fw needs this to reply correct cmd */ + .version = cpu_to_le32(GENMASK_U32(31, 0)), + .status = cpu_to_le32(is_powerup ? 1 : 0), + }, }, }; int len, err; - len = le16_to_cpu(req.datalen); + len = le16_to_cpu(req.r.datalen); mutex_lock(&hw->mbx.lock); - err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len); + err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len); mutex_unlock(&hw->mbx.lock); return err; @@ -142,11 +146,13 @@ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup) **/ int mucse_mbx_reset_hw(struct mucse_hw *hw) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(RESET_HW), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(RESET_HW), + }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; return mucse_fw_send_cmd_wait_resp(hw, &req, &reply); } @@ -166,24 +172,26 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum, u8 *mac_addr, int port) { - struct mbx_fw_cmd_req req = { - .datalen = cpu_to_le16(sizeof(req.get_mac_addr) + - MUCSE_MBX_REQ_HDR_LEN), - .opcode = cpu_to_le16(GET_MAC_ADDRESS), - .get_mac_addr = { - .port_mask = cpu_to_le32(BIT(port)), - .pfvf_num = cpu_to_le32(pfvfnum), + union mbx_fw_cmd_req_u req = { + .r = { + .datalen = cpu_to_le16(sizeof(req.r.get_mac_addr) + + MUCSE_MBX_REQ_HDR_LEN), + .opcode = cpu_to_le16(GET_MAC_ADDRESS), + .get_mac_addr = { + .port_mask = cpu_to_le32(BIT(port)), + .pfvf_num = cpu_to_le32(pfvfnum), + }, }, }; - struct mbx_fw_cmd_reply reply = {}; + union mbx_fw_cmd_reply_u reply = {}; int err; err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply); if (err) return err; - if (le32_to_cpu(reply.mac_addr.ports) & BIT(port)) - memcpy(mac_addr, reply.mac_addr.addrs[port].mac, ETH_ALEN); + if (le32_to_cpu(reply.r.mac_addr.ports) & BIT(port)) + memcpy(mac_addr, reply.r.mac_addr.addrs[port].mac, ETH_ALEN); else return -ENODATA; diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h index fb24fc12b613..fe996aeffc4d 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h @@ -80,6 +80,20 @@ struct mbx_fw_cmd_reply { }; } __packed; +/* Union wrappers to expose struct as __le32 dword array for mailbox + * transport, eliminating the need for pointer casts. The __packed + * structs have no padding, so dwords[] overlays the fields exactly. + */ +union mbx_fw_cmd_req_u { + struct mbx_fw_cmd_req r; + __le32 dwords[sizeof(struct mbx_fw_cmd_req) / sizeof(__le32)]; +}; + +union mbx_fw_cmd_reply_u { + struct mbx_fw_cmd_reply r; + __le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)]; +}; + int mucse_mbx_sync_fw(struct mucse_hw *hw); int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup); int mucse_mbx_reset_hw(struct mucse_hw *hw); -- cgit v1.2.3 From 5c0e3ba4f500fd4314ceb42f07f16bc445156431 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 1 Jul 2026 00:08:47 -0400 Subject: net/liquidio: drop cached VF pci_dev LUT The PF SR-IOV enable path caches VF pci_dev pointers in dpiring_to_vfpcidev_lut[] by iterating with pci_get_device(). Those entries do not own a reference, because the iterator drops the previous device reference on each step. The cached pointer is then dereferenced later when handling OCTEON_VF_FLR_REQUEST. Replace the cached VF mapping with runtime lookup on the mailbox DPI ring: derive the VF index from q_no, resolve the VF via exported PCI IOV helpers, validate it with the PF pointer and VF ID, then issue pcie_flr() and drop the reference with pci_dev_put(). Remove the unused VF lookup table initialization and cleanup. Fixes: ca6139ffc67ee ("liquidio CN23XX: sysfs VF config support") Fixes: 8c978d059224 ("liquidio CN23XX: Mailbox support") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260701040847.1897845-1-dbgh9129@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 27 ------------------ .../net/ethernet/cavium/liquidio/octeon_device.h | 3 -- .../net/ethernet/cavium/liquidio/octeon_mailbox.c | 33 +++++++++++++++++++++- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 0db08ac3d098..e303956b4bf1 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3779,9 +3779,7 @@ setup_nic_dev_done: static int octeon_enable_sriov(struct octeon_device *oct) { unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced; - struct pci_dev *vfdev; int err; - u32 u; if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) { err = pci_enable_sriov(oct->pci_dev, @@ -3794,23 +3792,6 @@ static int octeon_enable_sriov(struct octeon_device *oct) return err; } oct->sriov_info.sriov_enabled = 1; - - /* init lookup table that maps DPI ring number to VF pci_dev - * struct pointer - */ - u = 0; - vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, - OCTEON_CN23XX_VF_VID, NULL); - while (vfdev) { - if (vfdev->is_virtfn && - (vfdev->physfn == oct->pci_dev)) { - oct->sriov_info.dpiring_to_vfpcidev_lut[u] = - vfdev; - u += oct->sriov_info.rings_per_vf; - } - vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, - OCTEON_CN23XX_VF_VID, vfdev); - } } return num_vfs_alloced; @@ -3818,8 +3799,6 @@ static int octeon_enable_sriov(struct octeon_device *oct) static int lio_pci_sriov_disable(struct octeon_device *oct) { - int u; - if (pci_vfs_assigned(oct->pci_dev)) { dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n"); return -EPERM; @@ -3827,12 +3806,6 @@ static int lio_pci_sriov_disable(struct octeon_device *oct) pci_disable_sriov(oct->pci_dev); - u = 0; - while (u < MAX_POSSIBLE_VFS) { - oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL; - u += oct->sriov_info.rings_per_vf; - } - oct->sriov_info.num_vfs_alloced = 0; dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n", oct->pf_num); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 19344b21f8fb..858a0fff2cc0 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -390,9 +390,6 @@ struct octeon_sriov_info { struct lio_trusted_vf trusted_vf; - /*lookup table that maps DPI ring number to VF pci_dev struct pointer*/ - struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS]; - u64 vf_macaddr[MAX_POSSIBLE_VFS]; u16 vf_vlantci[MAX_POSSIBLE_VFS]; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c index ad685f5d0a13..697fcdc41e3c 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c @@ -26,6 +26,31 @@ #include "octeon_mailbox.h" #include "cn23xx_pf_device.h" +static struct pci_dev *lio_vf_pci_dev_by_qno(struct octeon_device *oct, u32 q_no) +{ + struct pci_dev *vfdev = NULL; + int vfidx; + + if (!oct->sriov_info.rings_per_vf) + return NULL; + + if (q_no % oct->sriov_info.rings_per_vf) + return NULL; + + vfidx = q_no / oct->sriov_info.rings_per_vf; + if (vfidx >= oct->sriov_info.num_vfs_alloced) + return NULL; + + while ((vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, + OCTEON_CN23XX_VF_VID, vfdev))) { + if (pci_physfn(vfdev) && pci_physfn(vfdev) == oct->pci_dev && + pci_iov_vf_id(vfdev) == vfidx) + return vfdev; + } + + return NULL; +} + /** * octeon_mbox_read: * @mbox: Pointer mailbox @@ -237,6 +262,7 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, struct octeon_mbox_cmd *mbox_cmd) { struct octeon_device *oct = mbox->oct_dev; + struct pci_dev *vfdev; switch (mbox_cmd->msg.s.cmd) { case OCTEON_VF_ACTIVE: @@ -260,7 +286,12 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox, dev_info(&oct->pci_dev->dev, "got a request for FLR from VF that owns DPI ring %u\n", mbox->q_no); - pcie_flr(oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no]); + vfdev = lio_vf_pci_dev_by_qno(oct, mbox->q_no); + if (!vfdev) + break; + + pcie_flr(vfdev); + pci_dev_put(vfdev); break; case OCTEON_PF_CHANGED_VF_MACADDR: -- cgit v1.2.3 From 7993211bde166471dffac074dc965489f86531f8 Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Thu, 2 Jul 2026 08:50:14 +0900 Subject: ipv4: igmp: remove multicast group from hash table on device destruction When a device is destroyed under RTNL, ip_mc_destroy_dev() iterates through the multicast list and calls ip_ma_put() on each membership, scheduling them for RCU reclamation. However, they are not unlinked from the device's multicast hash table (mc_hash). Since the device remains published in dev->ip_ptr until after ip_mc_destroy_dev() completes, concurrent RCU readers traversing mc_hash can still locate and access the multicast group after its refcount is decremented. If the RCU callback runs and frees the group while a reader is accessing it, a use-after-free occurs. Fix this by unlinking the multicast group from mc_hash using ip_mc_hash_remove() before scheduling it for reclamation. BUG: KASAN: slab-use-after-free in ip_check_mc_rcu+0x149/0x3f0 Read of size 4 at addr ffff888009bf1408 by task mausezahn/2276 Call Trace: dump_stack_lvl+0x67/0x90 print_report+0x175/0x7c0 kasan_report+0x147/0x180 ip_check_mc_rcu+0x149/0x3f0 udp_v4_early_demux+0x36d/0x12d0 ip_rcv_finish_core+0xb8b/0x1390 ip_rcv_finish+0x54/0x120 NF_HOOK+0x213/0x2b0 __netif_receive_skb+0x126/0x340 process_backlog+0x4f2/0xf00 __napi_poll+0x92/0x2c0 net_rx_action+0x583/0xc60 handle_softirqs+0x236/0x7f0 do_softirq+0x57/0x80 Allocated by task 2239: kasan_save_track+0x3e/0x80 __kasan_kmalloc+0x72/0x90 ____ip_mc_inc_group+0x31a/0xa40 __ip_mc_join_group+0x334/0x3f0 do_ip_setsockopt+0x16fa/0x2010 ip_setsockopt+0x3f/0x90 do_sock_setsockopt+0x1ad/0x300 Freed by task 0: kasan_save_track+0x3e/0x80 kasan_save_free_info+0x40/0x50 __kasan_slab_free+0x3a/0x60 __rcu_free_sheaf_prepare+0xd4/0x220 rcu_free_sheaf+0x36/0x190 rcu_core+0x8d9/0x12f0 handle_softirqs+0x236/0x7f0 Fixes: e9897071350b ("igmp: hash a hash table to speedup ip_check_mc_rcu()") Cc: stable@vger.kernel.org Signed-off-by: Yuyang Huang Reviewed-by: Kuniyuki Iwashima Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260701235014.73505-1-yuyanghuang@google.com Signed-off-by: Paolo Abeni --- net/ipv4/igmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index b6337a47c141..d520ea4f6d14 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1922,6 +1922,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev) #endif while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) { + ip_mc_hash_remove(in_dev, i); in_dev->mc_list = i->next_rcu; WRITE_ONCE(in_dev->mc_count, in_dev->mc_count - 1); ip_mc_clear_src(i); -- cgit v1.2.3 From 8c6db30d79528279abbeb416e4f533f1f91b8724 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 2 Jul 2026 10:38:40 +0000 Subject: 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)); -- cgit v1.2.3 From a6b49d27c17909608d54523220bb6f3498d4a1df Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Thu, 2 Jul 2026 10:38:41 +0000 Subject: 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; -- cgit v1.2.3 From 6a7a181f6921db3d9aed1bba7e15547fefd7eedc Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 2 Jul 2026 10:38:42 +0000 Subject: 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)) -- cgit v1.2.3 From 2bd3c6c702f3a9e2bcb3b536b0fbbaa645005d71 Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Thu, 2 Jul 2026 10:38:43 +0000 Subject: 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 @@ out_unlock: 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 -- cgit v1.2.3 From 60444706aa17616efc03190d099ac347e28b3d0a Mon Sep 17 00:00:00 2001 From: Enrico Pozzobon Date: Wed, 1 Jul 2026 16:47:23 +0200 Subject: net: usb: lan78xx: disable VLAN filter in promiscuous mode The hardware VLAN filter (RFE_CTL_VLAN_FILTER_) drops VLAN-tagged frames whose VID has not been registered via lan78xx_vlan_rx_add_vid(). It is left enabled in promiscuous mode, so packet capture (e.g. tcpdump or Wireshark) does not see tagged frames for unregistered VIDs. Clear the filter while the interface is promiscuous and restore it from NETIF_F_HW_VLAN_CTAG_FILTER otherwise. Enforce the same condition in lan78xx_set_features() so netdev_update_features() cannot re-enable the filter while promiscuous. Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver") Signed-off-by: Enrico Pozzobon Reviewed-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260701-lan78xx-vlan-promisc-v3-1-232266d32743@dissecto.com Signed-off-by: Paolo Abeni --- drivers/net/usb/lan78xx.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index c4cebacabcb5..cb782d81d84f 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1499,6 +1499,17 @@ multicast_write_done: return; } +static void lan78xx_update_vlan_filter(struct lan78xx_priv *pdata, + struct net_device *netdev, + netdev_features_t features) +{ + if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && + !(netdev->flags & IFF_PROMISC)) + pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; + else + pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; +} + static void lan78xx_set_multicast(struct net_device *netdev) { struct lan78xx_net *dev = netdev_priv(netdev); @@ -1533,6 +1544,8 @@ static void lan78xx_set_multicast(struct net_device *netdev) } } + lan78xx_update_vlan_filter(pdata, dev->net, dev->net->features); + if (netdev_mc_count(dev->net)) { struct netdev_hw_addr *ha; int i; @@ -3074,10 +3087,7 @@ static int lan78xx_set_features(struct net_device *netdev, else pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_; - if (features & NETIF_F_HW_VLAN_CTAG_FILTER) - pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; - else - pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; + lan78xx_update_vlan_filter(pdata, netdev, features); spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); -- cgit v1.2.3 From 252f8c681adc8614b70f844ba3de3a138c33a783 Mon Sep 17 00:00:00 2001 From: Christopher Mackle Date: Sat, 20 Jun 2026 01:39:16 +0000 Subject: 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); -- cgit v1.2.3 From 9f32f38265014fac7f5dc9490fb01a638ce6e121 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 18 Jun 2026 13:47:09 +0200 Subject: 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; -- cgit v1.2.3 From e8422d89e8af41d87f0e9db564be8e2634f4c602 Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Sat, 4 Jul 2026 14:58:15 +0800 Subject: 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); -- cgit v1.2.3 From 151edde741f8bc7f2931c5f44ab376d32b0c8beb Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Sat, 4 Jul 2026 14:58:16 +0800 Subject: 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); -- cgit v1.2.3 From a1fc19d61f661d47204f095b593de507884849f7 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:46:05 +0200 Subject: 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; } -- cgit v1.2.3 From ed51de4a86e173c3b0ef78e039c2e49e08b11f16 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:25 +0200 Subject: 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; } } -- cgit v1.2.3 From ef61d628dfad38fead1fd2e08979ae9126d011d5 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:26 +0200 Subject: 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. */ -- cgit v1.2.3 From 5a752a616e756844388a1a45404db9fc29fec655 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:27 +0200 Subject: 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; -- cgit v1.2.3 From f8001e1a516ba3b495728c65b61f799cbfad6bd0 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:28 +0200 Subject: 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. */ -- cgit v1.2.3 From f9654207e92283e0acac5d64fe5f8835383b5a23 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:29 +0200 Subject: 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: -- cgit v1.2.3 From 3bf39f711ff27c64be8680a8938bcc5001982e81 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:30 +0200 Subject: 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; -- cgit v1.2.3 From 1463ca3ec6601cbb097d8d87dbf5dcf1cb86a344 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 22 May 2026 02:45:31 +0200 Subject: 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; -- cgit v1.2.3 From cb8afea4655ff004fa7feee825d5c79783525383 Mon Sep 17 00:00:00 2001 From: "HE WEI (ギカク)" Date: Tue, 7 Jul 2026 18:48:28 +0900 Subject: 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]; -- cgit v1.2.3 From 4e28aa8f7ee26d67a4addee6e3980f1cbf861b49 Mon Sep 17 00:00:00 2001 From: Christian König Date: Sat, 4 Jul 2026 10:41:33 +0200 Subject: 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 | 24 ++++++++++++++++-------- include/drm/drm_exec.h | 34 ++++++++++++++++++---------------- 3 files changed, 36 insertions(+), 24 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; + __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); - drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0); - drm_exec_until_all_locked(&exec) { - break; + __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 -- cgit v1.2.3 From d775b9451eb8f52021dea6483ad758fef81dbf1e Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Thu, 2 Jul 2026 17:22:12 +0800 Subject: 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 | 79 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 12 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,11 +786,38 @@ 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); + } + } + } + + 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; - /* Mask out interrupts */ + 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); @@ -782,18 +825,27 @@ static int dwapb_gpio_suspend(struct device *dev) return 0; } -static int dwapb_gpio_resume(struct device *dev) +static int dwapb_gpio_resume_noirq(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; + int err; + + if (gpio->clocks_on_for_wake) + return 0; err = clk_bulk_prepare_enable(DWAPB_NR_CLOCKS, gpio->clks); - if (err) { + if (err) dev_err(gpio->dev, "Cannot reenable APB/Debounce clocks\n"); - return err; - } + + 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; 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 = { -- cgit v1.2.3 From aaf7766ba3b99a3834319e7cf939838afc705574 Mon Sep 17 00:00:00 2001 From: Liang Hao Date: Sun, 5 Jul 2026 15:47:59 +0800 Subject: 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; -- cgit v1.2.3 From ac7c6b4308c5e757eb278d47b9c0761c31ac9d6b Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Fri, 3 Jul 2026 09:32:30 +0200 Subject: 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; } -- cgit v1.2.3 From 4d6941c0a8fd44bcc9693a1c8201a1d26dd21bc8 Mon Sep 17 00:00:00 2001 From: Andreas Ziegler Date: Wed, 24 Jun 2026 05:33:41 +0200 Subject: 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" -- cgit v1.2.3 From 3383ffb7ef937317361713ffcc21921a7848511a Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 2 Jul 2026 10:38:38 +0000 Subject: 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, -- cgit v1.2.3 From b4d961351aa84fdf0148783fb1f3a1391b8a0adb Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Thu, 2 Jul 2026 10:38:39 +0000 Subject: 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; } -- cgit v1.2.3 From 335f7860846732477a9ce24f0db04dd253ba268c Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Mon, 6 Jul 2026 17:56:14 -0700 Subject: 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), -- cgit v1.2.3 From 883d0cae6d5f840629c5bcacf029c6aa9a9106f4 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Mon, 6 Jul 2026 17:56:15 -0700 Subject: 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, -- cgit v1.2.3 From dba0bfded433d412506eb674d6af77cdca82e259 Mon Sep 17 00:00:00 2001 From: Longlong Xia Date: Mon, 6 Jul 2026 12:26:39 +0900 Subject: 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 -- cgit v1.2.3 From af05b4e06279bbe34f8044b1e82f0725b5da91b7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 16 Jun 2026 18:55:38 +0100 Subject: 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); -- cgit v1.2.3 From f67be28fdf8b5d31ac1cc1152bb17250f9f8f513 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 21:24:19 +0800 Subject: 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); } -- cgit v1.2.3 From d5c6785f94ead7cd76b38af301ac63cb673485bc Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 17 Jun 2026 11:58:22 +0100 Subject: 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); -- cgit v1.2.3 From e5d8524108fef14970db85451b7d06e5f6fa51ce Mon Sep 17 00:00:00 2001 From: Yudistira Putra Date: Sun, 21 Jun 2026 05:51:53 -0400 Subject: 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; -- cgit v1.2.3 From c3e94604675e3db186111b8942650d86577df9b0 Mon Sep 17 00:00:00 2001 From: Yuanhe Shu Date: Wed, 24 Jun 2026 14:17:15 +0800 Subject: 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); -- cgit v1.2.3 From 535fcf4b8a261fbb8cc4f91e4597343c135a90f2 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 30 Jun 2026 18:54:12 -0400 Subject: 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 ) ); -- cgit v1.2.3 From 0a6070839b1ef276d5b05bedfb787743e140fb17 Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Wed, 1 Jul 2026 18:28:46 +0800 Subject: 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 @@ backtrack: } } } -EXPORT_SYMBOL(glob_match); -- cgit v1.2.3 From 1e67bb60bb03518d3bdd59a48c65d69e6d773bb8 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 1 Jul 2026 13:27:44 -0400 Subject: 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; \ -- cgit v1.2.3 From 596b3678326d3d1aed7c19423b6746f1ce09688a Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 23 Jun 2026 07:15:05 -0700 Subject: 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); -- cgit v1.2.3 From a483b1a91b33b7533280e7c3efd2bc1275caef18 Mon Sep 17 00:00:00 2001 From: Tapio Reijonen Date: Mon, 15 Jun 2026 06:38:40 +0000 Subject: 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; -- cgit v1.2.3 From 22dd2777e6c180e1c945b00f6d18550979436324 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 6 Jul 2026 20:03:32 +0200 Subject: 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); } -- cgit v1.2.3 From 4c2509f3b79756679a02bea649c6a7501b58f52c Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Tue, 7 Jul 2026 09:00:33 -0600 Subject: 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. -- cgit v1.2.3 From 8d187d4b33c262c0f3e44842553521151d8629e8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 7 Jul 2026 17:29:35 +0100 Subject: 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; } -- cgit v1.2.3 From 14f172eff9c19f8043a9858845f33cd034f3a41e Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 15:12:28 -0700 Subject: 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 @@ again: 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 @@ again: up_write(&xdna->notifier_lock); return 0; } - kref_get(&mapp->refcnt); + up_write(&xdna->notifier_lock); mm = mapp->notifier.mm; -- cgit v1.2.3 From 66ff5c0eee02c4be67f8ba7fb6c63709ef1c92a3 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 22:56:58 -0700 Subject: 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); -- cgit v1.2.3 From c8d2530791cb53602ac06ec2db6287d99f51cdbc Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 22:57:32 -0700 Subject: 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 @@ free_cus: 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); -- cgit v1.2.3 From 928caf71e566ddc5f303f15fb9c33f16a9915f30 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Mon, 6 Jul 2026 22:58:18 -0700 Subject: 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 -- cgit v1.2.3 From cdadb298b182d4615521380a520eb2c7cb762843 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Tue, 30 Jun 2026 16:26:35 +0200 Subject: 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, -- cgit v1.2.3 From 2726b5758f80a546a4ddeec5019e72035a7fa166 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 7 Jul 2026 17:58:52 +0530 Subject: 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, }; -- cgit v1.2.3 From 50fd6dd755c6e48a38af2fa4621167eea56829c2 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 7 Jul 2026 12:59:11 -0400 Subject: 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 | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 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,17 +399,39 @@ error: 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) { - list_del_rcu(&enabler->mm_enablers_link); + 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, locked); + 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); + + /* + * 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, int attempt) { @@ -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; -- cgit v1.2.3 From 05074bb90af94f2acbbc8f8ffaa507f914c273c8 Mon Sep 17 00:00:00 2001 From: Yu Peng Date: Wed, 3 Jun 2026 14:25:33 +0800 Subject: 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); -- cgit v1.2.3 From 2f8b8593c7832fad655290cef9e99af05b1b52b3 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 3 Jul 2026 12:33:35 -0300 Subject: 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, -- cgit v1.2.3 From 6b59c53c8adc2b522327407af5e1793a65b67e4b Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Sat, 27 Jun 2026 13:12:05 +0000 Subject: 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: -- cgit v1.2.3 From dedf4ccbcbd249e2435344d137247d94c0aa10ee Mon Sep 17 00:00:00 2001 From: Baojun Xu Date: Thu, 2 Jul 2026 18:44:29 +0800 Subject: 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, -- cgit v1.2.3 From 030db7005efe6a0705ddf07fced494c364a1c915 Mon Sep 17 00:00:00 2001 From: Liang Luo Date: Tue, 7 Jul 2026 17:45:38 +0800 Subject: 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``. -- cgit v1.2.3 From 5aa31cb1dfb08f19408c9c33da71f46525f6cba9 Mon Sep 17 00:00:00 2001 From: Guopeng Zhang Date: Tue, 7 Jul 2026 18:21:46 +0800 Subject: 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 -- cgit v1.2.3 From 451a7467570d9578b5f7fba9aa6e0ed1ac1fcfec Mon Sep 17 00:00:00 2001 From: Guopeng Zhang Date: Tue, 7 Jul 2026 18:21:47 +0800 Subject: 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 -- cgit v1.2.3 From 97fef602584458b100d383afed9d176c0bc689ab Mon Sep 17 00:00:00 2001 From: Guopeng Zhang Date: Tue, 7 Jul 2026 18:21:48 +0800 Subject: 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. -- cgit v1.2.3 From 5893013efabb056399a01e267f410cf76eba25eb Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:36 +0530 Subject: 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); } -- cgit v1.2.3 From f7697ecf6eab9d4887dd731038b3dc405c7e755e Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:37 +0530 Subject: 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; -- cgit v1.2.3 From dec5aaa27603e1d7b426ce3504af6d1a62e4d444 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Tue, 7 Jul 2026 11:29:38 +0530 Subject: 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; -- cgit v1.2.3 From 70b439e46effd782097f640c0b64f5f8b0e79913 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 7 Jul 2026 17:38:27 +0200 Subject: 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) -- cgit v1.2.3 From 9ab8656548cd737b98d0b19c4253aff8d68e97f4 Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Tue, 28 Apr 2026 12:12:26 +0800 Subject: hwmon: (corsair-psu) Stop device IO before calling hid_hw_stop hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within corsairpsu_probe(). If the probe operation fails after "io start" has been initiated, this race condition will result in a uaf vulnerability [1]. CPU0 CPU1 ==== ==== corsairpsu_probe() hid_device_io_start() ... unlock driver_input_lock hid_hw_stop() kfree(hidraw) __hid_input_report() ... acquire driver_input_lock hid_report_raw_event() hidraw_report_event() ... access hidraw's list_lock // trigger uaf Consequently, when corsairpsu_probe() fails and hid_hw_stop() needs to be executed, the io_started flag is first cleared while holding the driver_input_lock to prevent potential race conditions involving input reports. [1] BUG: KASAN: slab-use-after-free in rt_spin_lock+0x83/0x400 kernel/locking/spinlock_rt.c:56 Call Trace: hidraw_report_event+0x5d/0x3a0 drivers/hid/hidraw.c:577 hid_report_raw_event+0x311/0x1730 drivers/hid/hid-core.c:2076 __hid_input_report drivers/hid/hid-core.c:2152 [inline] hid_input_report+0x44e/0x580 drivers/hid/hid-core.c:2174 hid_irq_in+0x47e/0x6d0 drivers/hid/usbhid/hid-core.c:286 __usb_hcd_giveback_urb+0x3b3/0x5e0 drivers/usb/core/hcd.c:1657 dummy_timer+0x8a9/0x47d0 drivers/usb/gadget/udc/dummy_hcd.c:2005 Allocated by task 10: hidraw_connect+0x57/0x430 drivers/hid/hidraw.c:606 hid_connect+0x5bf/0x19d0 drivers/hid/hid-core.c:2277 hid_hw_start+0xa8/0x120 drivers/hid/hid-core.c:2387 corsairpsu_probe+0xd9/0x3c0 drivers/hwmon/corsair-psu.c:782 Freed by task 10: hidraw_disconnect+0x4f/0x60 drivers/hid/hidraw.c:662 hid_disconnect drivers/hid/hid-core.c:2362 [inline] hid_hw_stop+0x101/0x1e0 drivers/hid/hid-core.c:2407 corsairpsu_probe+0x327/0x3c0 drivers/hwmon/corsair-psu.c:826 Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Fixes: d115b51e0e56 ("hwmon: add Corsair PSU HID controller driver") Reported-by: syzbot+9eebf5f6544c5e873858@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=9eebf5f6544c5e873858 Tested-by: syzbot+9eebf5f6544c5e873858@syzkaller.appspotmail.com Signed-off-by: Edward Adam Davis Link: https://lore.kernel.org/r/tencent_BB7C33EB9EA41B7B4B5F1B8B25C0BA13BB08@qq.com [groeck: Updated subject and description; call hid_device_io_stop() only if IO has been started] Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-psu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index 76f3e1da68d0..ce958cdaef58 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -822,6 +822,7 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id fail_and_close: hid_hw_close(hdev); + hid_device_io_stop(hdev); fail_and_stop: hid_hw_stop(hdev); return ret; -- cgit v1.2.3 From 94c87871b051d7ad758828a805215a2ec194512a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Jul 2026 17:52:54 -0700 Subject: hwmon: (corsair-cpro) Stop device IO before calling hid_hw_stop Calling hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within the driver probe function. If the probe operation fails after "io start" has been initiated, this race condition will result in a UAF vulnerability. Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Reported-by: Sashiko Fixes: 40c3a44542257 ("hwmon: add Corsair Commander Pro driver") Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-cpro.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index b6e508e43fa1..8354a002f4c5 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -645,6 +645,7 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) out_hw_close: hid_hw_close(hdev); + hid_device_io_stop(hdev); out_hw_stop: hid_hw_stop(hdev); return ret; -- cgit v1.2.3 From ff0c5c53d08274e200b48a4d53aa078265e873cb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Jul 2026 17:59:10 -0700 Subject: hwmon: (gigabyte_waterforce) Stop device IO before calling hid_hw_stop Calling hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within the driver probe function. If the probe operation fails after "io start" has been initiated, this race condition will result in a UAF vulnerability. Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Reported-by: Sashiko Fixes: 42ac68e3d4ba0 ("hwmon: Add driver for Gigabyte AORUS Waterforce AIO coolers") Signed-off-by: Guenter Roeck --- drivers/hwmon/gigabyte_waterforce.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/gigabyte_waterforce.c b/drivers/hwmon/gigabyte_waterforce.c index 27487e215bdd..4eea05f8b569 100644 --- a/drivers/hwmon/gigabyte_waterforce.c +++ b/drivers/hwmon/gigabyte_waterforce.c @@ -371,13 +371,15 @@ static int waterforce_probe(struct hid_device *hdev, const struct hid_device_id if (IS_ERR(priv->hwmon_dev)) { ret = PTR_ERR(priv->hwmon_dev); hid_err(hdev, "hwmon registration failed with %d\n", ret); - goto fail_and_close; + goto fail_and_io_stop; } waterforce_debugfs_init(priv); return 0; +fail_and_io_stop: + hid_device_io_stop(hdev); fail_and_close: hid_hw_close(hdev); fail_and_stop: -- cgit v1.2.3 From 59d104b54b0b42e30fd2a68d24ee5c49dcc54d1e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Jul 2026 18:00:32 -0700 Subject: hwmon: (nzxt-smart2) Stop device IO before calling hid_hw_stop Calling hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within the driver probe function. If the probe operation fails after "io start" has been initiated, this race condition will result in a UAF vulnerability. Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Reported-by: Sashiko Fixes: 53e68c20aeb1e ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2") Signed-off-by: Guenter Roeck --- drivers/hwmon/nzxt-smart2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index 58ef9fa0184b..e2316c46629d 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -768,7 +768,7 @@ static int nzxt_smart2_hid_probe(struct hid_device *hdev, out_hw_close: hid_hw_close(hdev); - + hid_device_io_stop(hdev); out_hw_stop: hid_hw_stop(hdev); return ret; -- cgit v1.2.3 From f151d0143ac4e086f92f52328ebdbdc50933d8ef Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 7 Jul 2026 18:01:58 -0700 Subject: hwmon: (nzxt-kraken3) Stop device IO before calling hid_hw_stop Calling hid_hw_stop() does not stop the device IO. This results in a race condition between hid_input_report() and the point immediately following the execution of hid_device_io_start() within the driver probe function. If the probe operation fails after "io start" has been initiated, this race condition will result in a UAF vulnerability. Fix the problem by calling hid_device_io_stop() before calling hid_hw_stop(). Reported-by: Sashiko Fixes: f3b4b146eb107 ("hwmon: Add driver for NZXT Kraken X and Z series AIO CPU coolers") Signed-off-by: Guenter Roeck --- drivers/hwmon/nzxt-kraken3.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/nzxt-kraken3.c b/drivers/hwmon/nzxt-kraken3.c index d00409bcab93..05525406c5fb 100644 --- a/drivers/hwmon/nzxt-kraken3.c +++ b/drivers/hwmon/nzxt-kraken3.c @@ -948,7 +948,7 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id ret = kraken3_init_device(hdev); if (ret < 0) { hid_err(hdev, "device init failed with %d\n", ret); - goto fail_and_close; + goto fail_and_stop_io; } ret = kraken3_get_fw_ver(hdev); @@ -960,13 +960,15 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id if (IS_ERR(priv->hwmon_dev)) { ret = PTR_ERR(priv->hwmon_dev); hid_err(hdev, "hwmon registration failed with %d\n", ret); - goto fail_and_close; + goto fail_and_stop_io; } kraken3_debugfs_init(priv, device_name); return 0; +fail_and_stop_io: + hid_device_io_stop(hdev); fail_and_close: hid_hw_close(hdev); fail_and_stop: -- cgit v1.2.3 From 0f092793a7b527dfb2cde323d4e5630d43447b84 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 7 Jul 2026 10:23:07 -0700 Subject: 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); -- cgit v1.2.3 From 44d8fddf1c87d6bb6b65983041a0ce6c2af66bb9 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 7 Jul 2026 10:23:23 -0700 Subject: 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) -- cgit v1.2.3 From c69dbbf0212734e22219dfc31c0922bd7c9ffbb0 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 7 Jul 2026 13:15:56 -0700 Subject: 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); -- cgit v1.2.3 From 917d0a4b95ea7ba01ed6296fb808f752d5d81107 Mon Sep 17 00:00:00 2001 From: TJ Adams Date: Mon, 6 Jul 2026 14:00:53 -0700 Subject: 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 */ -- cgit v1.2.3 From 793bf193b18e9bff6c4280268bbffd16a5b533e5 Mon Sep 17 00:00:00 2001 From: TJ Adams Date: Mon, 6 Jul 2026 14:00:54 -0700 Subject: 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 */ -- cgit v1.2.3 From d3386e17393bec1341cfeedb9d08d6846ccd6fb2 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 8 Jul 2026 11:18:45 +0800 Subject: 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; -- cgit v1.2.3 From 95e19a4d8dd3072d51209081a29467d887c84158 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 7 Jul 2026 21:39:39 +0800 Subject: 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); -- cgit v1.2.3 From e0776dde101ac9eae7e1421147f3a6ae7a556da7 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Wed, 1 Jul 2026 08:52:14 -0400 Subject: 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 -- cgit v1.2.3 From dbff3646369cb9add901b6c1b66d44cfb4a91ac5 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 1 Jul 2026 08:52:15 -0400 Subject: 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. -- cgit v1.2.3 From a914034334c48a85303a716b106a9a3167439876 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Wed, 1 Jul 2026 08:52:16 -0400 Subject: 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. -- cgit v1.2.3 From 4ad805f0e41acf56ac54855c666d34c59ad66fe7 Mon Sep 17 00:00:00 2001 From: Cihan Karadag Date: Tue, 7 Jul 2026 17:57:05 -0600 Subject: 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 -- cgit v1.2.3 From 1a3267a8c9ecabb8e27f5cbda6d19295d5e41beb Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 1 Jul 2026 20:26:52 -0700 Subject: net: mdio: select REGMAP_MMIO instead of depending on it REGMAP_MMIO is a hidden (non-user-visible) tristate symbol. Using depends on it is incorrect because there is no way for the user to enable it directly. Change to select, which is the convention used by every other driver in the tree that needs REGMAP_MMIO. Fixes: 8057cbb8335c ("net: mdio: mscc-miim: Add depend of REGMAP_MMIO on MDIO_MSCC_MIIM") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260702032653.1580616-1-rosenp@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/mdio/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index c591eec8e97a..e57121019153 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -122,7 +122,8 @@ config MDIO_MVUSB config MDIO_MSCC_MIIM tristate "Microsemi MIIM interface support" - depends on HAS_IOMEM && REGMAP_MMIO + depends on HAS_IOMEM + select REGMAP_MMIO help This driver supports the MIIM (MDIO) interface found in the network switches of the Microsemi SoCs; it is recommended to switch on -- cgit v1.2.3 From cd9993d22a577339a93dcb401574e874ea29b143 Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Sun, 5 Jul 2026 12:49:17 +0200 Subject: 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) -- cgit v1.2.3 From b7f97cae7ec1b6c3c32843c42be218690d310467 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jul 2026 00:07:59 +0000 Subject: net/sched: cake: reject overhead values that underflow length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CAKE accepts signed overhead values and stores them in an s16, but the adjusted packet length calculation uses unsigned arithmetic. A negative effective length can therefore wrap to a large value. Such configurations make rate accounting depend on integer wraparound rather than on the packet size userspace intended to model. A static netlink lower bound is not enough because packets reaching CAKE can be smaller than any reasonable manual-overhead allowance. Fold the signed overhead adjustment into the existing datapath MPU clamp so negative adjusted lengths are clamped before link-layer framing adjustments. Fixes: a729b7f0bd5b ("sch_cake: Add overhead compensation support to the rate shaper") Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260702000758.297407.e5c888d9d99d.cake-overhead-underflow@trailofbits.com Signed-off-by: Paolo Abeni --- net/sched/sch_cake.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index a3c185505afc..f78f8e950776 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1389,10 +1389,7 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off) if (qd->min_netlen > len) WRITE_ONCE(qd->min_netlen, len); - len += q->rate_overhead; - - if (len < q->rate_mpu) - len = q->rate_mpu; + len = max((s32)len + q->rate_overhead, (s32)q->rate_mpu); if (q->atm_mode == CAKE_ATM_ATM) { len += 47; -- cgit v1.2.3 From 235acadd310533ba386ae61ad155b72bee381559 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Thu, 2 Jul 2026 09:04:51 +0530 Subject: octeontx2-pf: check DMAC extraction support before filtering Currently, configuring a VF MAC address via the PF (e.g., 'ip link set vf 0 mac ') blindly attempts to install a DMAC-based hardware filter. However, the hardware parser profile might not support DMAC extraction. Check if the hardware parsing profile supports DMAC extraction before adding the filter. Additionally, emit a warning message to inform the operator if the MAC filter installation fails due to missing DMAC extraction support. Update config->mac only after hardware programming succeeds in otx2_set_vf_mac(). Fixes: f0c2982aaf98 ("octeontx2-pf: Add support for SR-IOV management functions") Signed-off-by: Suman Ghosh Signed-off-by: Nitin Shetty J Reviewed-by: Harshitha Ramamurthy Link: https://patch.msgid.link/20260702033451.2969880-1-nshettyj@marvell.com Signed-off-by: Paolo Abeni --- .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 43 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 88ac85354445..2e33b33ec993 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -2516,10 +2516,42 @@ EXPORT_SYMBOL(otx2_config_hwtstamp_set); static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac) { + struct npc_get_field_status_req *freq; + struct npc_get_field_status_rsp *frsp; struct npc_install_flow_req *req; int err; mutex_lock(&pf->mbox.lock); + + /* Skip installing the DMAC filter if the hardware parser profile + * does not support DMAC extraction. + */ + freq = otx2_mbox_alloc_msg_npc_get_field_status(&pf->mbox); + if (!freq) { + err = -ENOMEM; + goto out; + } + + freq->field = NPC_DMAC; + err = otx2_sync_mbox_msg(&pf->mbox); + if (err) + goto out; + + frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp + (&pf->mbox.mbox, 0, &freq->hdr); + if (IS_ERR(frsp)) { + err = PTR_ERR(frsp); + goto out; + } + + if (!frsp->enable) { + netdev_warn(pf->netdev, + "VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n", + vf); + err = -EOPNOTSUPP; + goto out; + } + req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox); if (!req) { err = -ENOMEM; @@ -2558,13 +2590,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) if (!is_valid_ether_addr(mac)) return -EINVAL; - config = &pf->vf_configs[vf]; - ether_addr_copy(config->mac, mac); - ret = otx2_do_set_vf_mac(pf, vf, mac); - if (ret == 0) - dev_info(&pdev->dev, - "Load/Reload VF driver\n"); + if (ret == 0) { + config = &pf->vf_configs[vf]; + ether_addr_copy(config->mac, mac); + dev_info(&pdev->dev, "Load/Reload VF driver\n"); + } return ret; } -- cgit v1.2.3 From 07c60dda9c059c09f83d42a3ebda2e7cc1cf3bc2 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Wed, 8 Jul 2026 16:02:04 +0530 Subject: 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; } -- cgit v1.2.3 From 2d184128f45afad7bfdc17d946339765555dbd38 Mon Sep 17 00:00:00 2001 From: Agustin Luzardo Date: Sun, 5 Jul 2026 15:42:27 -0300 Subject: 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 */ -- cgit v1.2.3 From 5c3f4b583c25911fad9890b463632761291f65ab Mon Sep 17 00:00:00 2001 From: Rohit Sinha Date: Tue, 7 Jul 2026 02:48:34 +0530 Subject: 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), -- cgit v1.2.3 From 0752734163a20c9185a12c0d0786995751ab7426 Mon Sep 17 00:00:00 2001 From: Kshamendra Kumar Mishra Date: Tue, 7 Jul 2026 22:07:44 +0530 Subject: 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), -- cgit v1.2.3 From bc0d4444f384d183e2113b72c34643069f93ab63 Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 3 Jul 2026 13:04:32 +0530 Subject: 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 + Documentation/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 -- cgit v1.2.3 From 4214220c314435bc1b8ca492b7657ae5ad52145e Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 3 Jul 2026 13:04:33 +0530 Subject: 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 -- cgit v1.2.3 From 0d037985725a2123b9d92dbf98ccb5f81e2bb8b4 Mon Sep 17 00:00:00 2001 From: Ravi Hothi Date: Fri, 3 Jul 2026 13:04:34 +0530 Subject: 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"}, -- cgit v1.2.3 From 735a461d060d4eeb2f9732aba1295bc32a3982e2 Mon Sep 17 00:00:00 2001 From: Madhu M Date: Fri, 19 Jun 2026 21:03:11 +0530 Subject: 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); } -- cgit v1.2.3 From 195e667c8719480c320cd48ac0fbf1cb81d6ffe0 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 17 Jun 2026 10:06:13 +0800 Subject: 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; -- cgit v1.2.3 From c5371e0b91b24159a3ebaa61e70b0980bcf03c0a Mon Sep 17 00:00:00 2001 From: Sam Day Date: Fri, 26 Jun 2026 14:29:10 +1000 Subject: 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; -- cgit v1.2.3 From 30adce93d5c4a5a1ec29d9249e3fdfcc391d406b Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 14:46:17 +0800 Subject: 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; } -- cgit v1.2.3 From 5fc3f333c001f1e308bbcdeecdec0d054d24338b Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 26 Jun 2026 15:06:07 +0800 Subject: 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; -- cgit v1.2.3 From b9399d25fbb34a05bbe76eeedd730f62ff2670e9 Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 30 Jun 2026 15:14:19 +0800 Subject: 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; -- cgit v1.2.3 From 95f90eea070837f7c72207d5520f805bdefc3bc5 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 8 Jul 2026 13:08:58 +0200 Subject: 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); -- cgit v1.2.3 From 21b5bf155435008e0fb0736795289788e63d426f Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 8 Jul 2026 13:08:59 +0200 Subject: 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++)) { -- cgit v1.2.3 From b4ecbdc4f8830f5586c4a5cfc384c00f20f8f8b3 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 7 Jul 2026 00:10:49 +0900 Subject: 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) { -- cgit v1.2.3 From e0f844d9d74200d311c6438a0f04270834ba5365 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 3 Jul 2026 17:20:33 +0100 Subject: 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); } } } -- cgit v1.2.3 From 0ef7cc27da8b9e315a4a5a665c68c44206f5e559 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 1 Jul 2026 20:40:06 +0900 Subject: 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; -- cgit v1.2.3 From 4e8ba83ac4d311992e6a4c21de5dd705010df06e Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 1 Jul 2026 21:16:25 +0900 Subject: 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); -- cgit v1.2.3 From 010382937fb69892b3469ac4d30af072262f59e8 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Fri, 12 Jun 2026 13:20:05 +0800 Subject: 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; } -- cgit v1.2.3 From 67e511d2989eb1c8c588b599ce2fcc6bb8e6f7ea Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Thu, 25 Jun 2026 15:37:04 +0800 Subject: 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); -- cgit v1.2.3 From 49f6e3c3ef19f04f6657ed8dce550e36c763abb8 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 3 Jul 2026 17:40:32 +0300 Subject: 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. -- cgit v1.2.3 From 42c37c4b75d38b51d84f31a8e29427f5e06a7c2a Mon Sep 17 00:00:00 2001 From: 胡连勤 Date: Fri, 3 Jul 2026 17:40:33 +0300 Subject: 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; } -- cgit v1.2.3 From b229b22b0a945d52dee887c856991ad08744d08e Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 1 Jun 2026 15:55:02 +0200 Subject: 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; -- cgit v1.2.3 From 43ae2f90b70cda374c487c1639a01d0f14e5d583 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Thu, 2 Jul 2026 15:13:29 -0400 Subject: 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; -- cgit v1.2.3 From 7c4a234bd31a64a8dbd0140dc812da592c5e0787 Mon Sep 17 00:00:00 2001 From: Paul Menzel Date: Fri, 3 Jul 2026 13:07:37 +0200 Subject: 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 }, -- cgit v1.2.3 From 9cff680e47632b7723cb19f9c5e63669063c3417 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Thu, 4 Jun 2026 18:50:24 +0800 Subject: 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); -- cgit v1.2.3 From e8da46d99d3710106e7c44db14566bf9b57386b5 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Mon, 6 Jul 2026 23:53:12 +0900 Subject: 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); -- cgit v1.2.3 From b11c513ad943f35cf5e8007d3a56279c79b7ed4b Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 7 Jul 2026 16:23:58 -0700 Subject: 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); } -- cgit v1.2.3 From c7eaea5c6eeb391d445583fa6419c957ca74a86b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Jun 2026 16:33:14 +0200 Subject: 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); -- cgit v1.2.3 From 7b19c0f81ed1fdaec6bc522569be367199a9edf3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:54 +0000 Subject: ipv4: igmp: Fix potential UAF in igmp_gq_start_timer() A race condition exists between device teardown (inetdev_destroy) and incoming IGMP query processing (igmp_rcv), leading to a Use-After-Free in the IGMP timer callback. During device destruction, inetdev_destroy() drops the primary reference to in_device, which can drop its refcount to 0. The actual freeing of in_device memory is deferred via RCU (using call_rcu()). Concurrently, igmp_rcv() runs under RCU read lock and obtains the in_device pointer. Because the memory is RCU-protected, CPU-0 can safely dereference in_device even if its refcount has hit 0. However, if CPU-0 calls igmp_gq_start_timer() and re-arms the timer, it attempts to acquire a reference using in_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the in_device memory is still scheduled to be freed after the RCU grace period (as the free callback does not check the refcount again), the device is freed while the timer is still armed. When the timer expires, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not arm the timer. A similar issue in IPv6 MLD is fixed in a subsequent patch. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Zero Day Initiative Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-2-edumazet@google.com Signed-off-by: Paolo Abeni --- include/linux/inetdevice.h | 5 +++++ net/ipv4/igmp.c | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index dccbeb25f701..6032eea2539a 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -293,6 +293,11 @@ static inline void in_dev_put(struct in_device *idev) #define __in_dev_put(idev) refcount_dec(&(idev)->refcnt) #define in_dev_hold(idev) refcount_inc(&(idev)->refcnt) +static inline bool in_dev_hold_safe(struct in_device *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + #endif /* __KERNEL__ */ static __inline__ __be32 inet_make_mask(int logmask) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index d520ea4f6d14..3a1cb2a827f3 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -248,16 +248,20 @@ static void igmp_gq_start_timer(struct in_device *in_dev) return; in_dev->mr_gq_running = 1; - if (!mod_timer(&in_dev->mr_gq_timer, exp)) - in_dev_hold(in_dev); + if (in_dev_hold_safe(in_dev)) { + if (mod_timer(&in_dev->mr_gq_timer, exp)) + in_dev_put(in_dev); + } } static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) { - int tv = get_random_u32_below(delay); + if (in_dev_hold_safe(in_dev)) { + int tv = get_random_u32_below(delay); - if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) - in_dev_hold(in_dev); + if (mod_timer(&in_dev->mr_ifc_timer, jiffies + tv + 2)) + in_dev_put(in_dev); + } } static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) -- cgit v1.2.3 From 9b26518b6896a16b809b1e42986f4ebac7bccc1e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:55 +0000 Subject: ipv6: mcast: Fix potential UAF in MLD delayed work A race condition exists between device teardown and incoming MLD query processing, leading to a Use-After-Free in the MLD delayed work. During device destruction, the primary reference to inet6_dev is dropped, which can drop its refcount to 0. The actual freeing of inet6_dev memory is deferred via RCU. Concurrently, the packet receive path runs under RCU read lock and obtains the inet6_dev pointer. Because the memory is RCU-protected, CPU-0 can safely dereference inet6_dev even if its refcount has hit 0. However, if CPU-0 calls igmp6_event_query() and schedules delayed work, it attempts to acquire a reference using in6_dev_hold(). This increments the refcount from 0 to 1, triggering a "refcount_t: addition on 0" warning. Since the inet6_dev memory is still scheduled to be freed after the RCU grace period, the device is freed while the work is still scheduled. When the work runs, it accesses the freed memory, causing a kernel panic. Fix this by using refcount_inc_not_zero() (via a new helper in6_dev_hold_safe()) to prevent acquiring a reference if the device is already being destroyed. If the refcount is 0, we do not schedule the work. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-3-edumazet@google.com Signed-off-by: Paolo Abeni --- include/net/addrconf.h | 5 +++++ net/ipv6/mcast.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 539bbbe54b14..8ced27a8229b 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -446,6 +446,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev) refcount_inc(&idev->refcnt); } +static inline bool in6_dev_hold_safe(struct inet6_dev *idev) +{ + return refcount_inc_not_zero(&idev->refcnt); +} + /* called with rcu_read_lock held */ static inline bool ip6_ignore_linkdown(const struct net_device *dev) { diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 04b811b3be97..4d2b9377ba2d 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1083,8 +1083,10 @@ static void mld_gq_start_work(struct inet6_dev *idev) mc_assert_locked(idev); idev->mc_gq_running = 1; - if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_gq_stop_work(struct inet6_dev *idev) @@ -1102,8 +1104,10 @@ static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_ifc_stop_work(struct inet6_dev *idev) @@ -1121,8 +1125,10 @@ static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay) mc_assert_locked(idev); - if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) - in6_dev_hold(idev); + if (in6_dev_hold_safe(idev)) { + if (mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2)) + in6_dev_put(idev); + } } static void mld_dad_stop_work(struct inet6_dev *idev) @@ -1395,18 +1401,23 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld, void igmp6_event_query(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); + bool put = false; if (!idev || idev->dead) goto out; spin_lock_bh(&idev->mc_query_lock); - if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) { + if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS && + in6_dev_hold_safe(idev)) { __skb_queue_tail(&idev->mc_query_queue, skb); - if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) - in6_dev_hold(idev); + if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0)) + put = true; skb = NULL; } spin_unlock_bh(&idev->mc_query_lock); + + if (put) + in6_dev_put(idev); out: kfree_skb(skb); } @@ -1570,18 +1581,23 @@ static void mld_query_work(struct work_struct *work) void igmp6_event_report(struct sk_buff *skb) { struct inet6_dev *idev = __in6_dev_get(skb->dev); + bool put = false; if (!idev || idev->dead) goto out; spin_lock_bh(&idev->mc_report_lock); - if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) { + if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS && + in6_dev_hold_safe(idev)) { __skb_queue_tail(&idev->mc_report_queue, skb); - if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) - in6_dev_hold(idev); + if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0)) + put = true; skb = NULL; } spin_unlock_bh(&idev->mc_report_lock); + + if (put) + in6_dev_put(idev); out: kfree_skb(skb); } -- cgit v1.2.3 From 3546deaa0c30a14c7cdb5dc8f2432cb428f0cd36 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 5 Jul 2026 18:17:56 +0000 Subject: ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer() When a timer is deleted and not re-armed in igmp_mod_timer(), or stopped in igmp_stop_timer(), the code currently decrements the reference counter of the multicast list entry @im using refcount_dec(&im->refcnt). However, both functions can be called from the RCU reader path: - igmp_mod_timer() via igmp_heard_query() -> for_each_pmc_rcu() - igmp_stop_timer() via igmp_rcv() -> igmp_heard_report() If the group im was concurrently removed from the list by ip_mc_dec_group(), its reference count might have already been decremented to 1. In this case, timer_delete() succeeds, and refcount_dec() decrements the refcount from 1 to 0. Since refcount_dec() does not free the object when it hits 0 (unlike ip_ma_put()), the im structure is leaked. Fix this by using ip_ma_put(im) instead of refcount_dec(&im->refcnt), and deferring the put until after the spinlock is released. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260705181756.963063-4-edumazet@google.com Signed-off-by: Paolo Abeni --- net/ipv4/igmp.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 3a1cb2a827f3..bb2d4441a492 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -217,13 +217,18 @@ static void ip_sf_list_clear_all(struct ip_sf_list *psf) static void igmp_stop_timer(struct ip_mc_list *im) { + bool put = false; + spin_lock_bh(&im->lock); if (timer_delete(&im->timer)) - refcount_dec(&im->refcnt); + put = true; WRITE_ONCE(im->tm_running, 0); WRITE_ONCE(im->reporter, 0); im->unsolicit_count = 0; spin_unlock_bh(&im->lock); + + if (put) + ip_ma_put(im); } /* It must be called with locked im->lock */ @@ -266,6 +271,8 @@ static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) { + bool put = false; + spin_lock_bh(&im->lock); im->unsolicit_count = 0; if (timer_delete(&im->timer)) { @@ -275,10 +282,13 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) spin_unlock_bh(&im->lock); return; } - refcount_dec(&im->refcnt); + put = true; } igmp_start_timer(im, max_delay); spin_unlock_bh(&im->lock); + + if (put) + ip_ma_put(im); } -- cgit v1.2.3 From f47b6b313df9a17010a72b3f389f0a5e9e49db3b Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Tue, 7 Jul 2026 22:17:14 +1000 Subject: 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"); } -- cgit v1.2.3 From 2094dab19d45c487285617b7b68913d0cc0c1211 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 2 Jul 2026 09:20:00 -0700 Subject: 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, -- cgit v1.2.3 From d128ffd2baa708da0362b562f0b7f9df96e43653 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sun, 5 Jul 2026 12:23:58 +0800 Subject: 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); } -- cgit v1.2.3 From f88caa121eac3b6a050262ac3de0c133ee17f7b2 Mon Sep 17 00:00:00 2001 From: Jinliang Zheng Date: Thu, 2 Jul 2026 19:56:11 +0800 Subject: 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; -- cgit v1.2.3 From 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f Mon Sep 17 00:00:00 2001 From: Shoichiro Miyamoto Date: Tue, 7 Jul 2026 20:23:58 +0900 Subject: 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) -- cgit v1.2.3 From 3b08fed5b7e0d5e3a25d73ef3ba09cd33ade16c9 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 5 Jul 2026 16:36:29 -0700 Subject: netfilter: nf_conntrack_reasm: guard mac_header adjustment after IPv6 defrag nf_ct_frag6_reasm() slides the packet head forward to drop the IPv6 fragment header and then unconditionally advances skb->mac_header: skb->mac_header += sizeof(struct frag_hdr); On the NF_INET_LOCAL_OUT defrag path the skb has no link-layer header yet, so skb->mac_header is still the "not set" sentinel (u16)~0U. Adding sizeof(struct frag_hdr) wraps it to a small value (0xffff + 8 == 7), after which skb_mac_header_was_set() wrongly reports a MAC header is present and skb_mac_header() points into the headroom. The reassembler has done this unconditional add since it was introduced; it was harmless while mac_header was a bare pointer, but wrong once mac_header became a u16 offset whose unset state is the ~0U sentinel tested by skb_mac_header_was_set(). The sibling net/ipv6/reassembly.c does the same relocation and does guard the adjustment; mirror the guard here. Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal --- net/ipv6/netfilter/nf_conntrack_reasm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 64ab23ff559b..3637b20d3fa4 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -348,7 +348,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb, skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0]; memmove(skb->head + sizeof(struct frag_hdr), skb->head, (skb->data - skb->head) - sizeof(struct frag_hdr)); - skb->mac_header += sizeof(struct frag_hdr); + if (skb_mac_header_was_set(skb)) + skb->mac_header += sizeof(struct frag_hdr); skb->network_header += sizeof(struct frag_hdr); skb_reset_transport_header(skb); -- cgit v1.2.3 From a622d2e9608c9dff47fc2e5759ac7aa3a836b45d Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 5 Jul 2026 14:58:00 -0700 Subject: netfilter: ebtables: terminate table name before find_table_lock() update_counters() and compat_update_counters() forward a user-supplied 32-byte table name to find_table_lock() without NUL-terminating it. On a lookup miss, find_inlist_lock() calls try_then_request_module(..., "%s%s", "ebtable_", name), and vsnprintf() reads past the name field and the stack object until it hits a zero byte. BUG: KASAN: stack-out-of-bounds in string (lib/vsprintf.c:648 lib/vsprintf.c:730) Read of size 1 at addr ffff8880119dfb20 by task exploit/147 Call Trace: ... string (lib/vsprintf.c:648 lib/vsprintf.c:730) vsnprintf (lib/vsprintf.c:2945) __request_module (kernel/module/kmod.c:150) do_update_counters.isra.0 (net/bridge/netfilter/ebtables.c:371 net/bridge/netfilter/ebtables.c:380) update_counters (net/bridge/netfilter/ebtables.c:1440) do_ebt_set_ctl (net/bridge/netfilter/ebtables.c:2573) nf_setsockopt (net/netfilter/nf_sockopt.c:101) ip_setsockopt (net/ipv4/ip_sockglue.c:1424) raw_setsockopt (net/ipv4/raw.c:847) __sys_setsockopt (net/socket.c:2393) ... compat_do_replace() shares the same unterminated name via compat_copy_ebt_replace_from_user(); terminate it there too so all find_table_lock() callers behave alike. The other callers already terminate the name after the copy. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Florian Westphal --- net/bridge/netfilter/ebtables.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index f20c039e44c8..5b74ff827493 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1434,6 +1434,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len) if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) return -EFAULT; + hlp.name[sizeof(hlp.name) - 1] = '\0'; + if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) return -EINVAL; @@ -2273,6 +2275,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl, memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry)); + repl->name[sizeof(repl->name) - 1] = '\0'; + /* starting with hook_entry, 32 vs. 64 bit structures are different */ for (i = 0; i < NF_BR_NUMHOOKS; i++) repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]); @@ -2395,6 +2399,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg, if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) return -EFAULT; + hlp.name[sizeof(hlp.name) - 1] = '\0'; + /* try real handler in case userland supplied needed padding */ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) return update_counters(net, arg, len); -- cgit v1.2.3 From cbfe53599eebffd188938ab6774cc41794f6f9d5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 4 Jul 2026 10:23:31 +0200 Subject: netfilter: ebtables: zero chainstack array sashiko reports: looking at ebtables table translation, could a sparse cpu_possible_mask lead to an uninitialized pointer free? If cpu_possible_mask is sparse (for example, CPU 0 and CPU 2 are possible, but CPU 1 is not), the allocation loop skips CPU 1. If vmalloc_node() fails at CPU 2, the cleanup loop will blindly decrement and call vfree() on newinfo->chainstack[1]. Not a real-world bug, such allocation isn't expected to fail in the first place. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Florian Westphal --- net/bridge/netfilter/ebtables.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 5b74ff827493..48187598cdd0 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -921,8 +921,7 @@ static int translate_table(struct net *net, const char *name, * if an error occurs */ newinfo->chainstack = - vmalloc_array(nr_cpu_ids, - sizeof(*(newinfo->chainstack))); + vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack))); if (!newinfo->chainstack) return -ENOMEM; for_each_possible_cpu(i) { -- cgit v1.2.3 From 084d23f818321390509e9738a0b08bbf46df6425 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 4 Jul 2026 12:05:15 +0200 Subject: netfilter: ebtables: module names must be null-terminated We need to explicitly check the length, else we may pass non-null terminated string to request_module(). Cc: stable@vger.kernel.org Fixes: bcf493428840 ("netfilter: ebtables: Fix extension lookup with identical name") Signed-off-by: Florian Westphal --- net/bridge/netfilter/ebtables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 48187598cdd0..96c9a8f57c87 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -403,6 +403,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par, left - sizeof(struct ebt_entry_match) < m->match_size) return -EINVAL; + if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN) + return -EINVAL; + match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision); if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) { if (!IS_ERR(match)) -- cgit v1.2.3 From 42e74d8f21ce448fe12ba3dd59db817b3e977f49 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 7 Jul 2026 14:02:40 -0400 Subject: 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 --- tools/testing/selftests/user_events/abi_test.c | 29 ++++++++++++++++++++++++- tools/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) { -- cgit v1.2.3 From e6107a4c74b54cb33e3bce162a63048ae5a6b198 Mon Sep 17 00:00:00 2001 From: Tamaki Yanagawa Date: Fri, 3 Jul 2026 16:22:57 +0000 Subject: netfilter: nft_lookup: fix catchall element handling with inverted lookups nft_lookup_eval() decides whether a lookup matched (`found`) from the direct set lookup and priv->invert before falling back to the catchall element used by interval sets (e.g. nft_set_rbtree) for the open-ended default range. Since `found` is never recomputed after `ext` is replaced by the catchall lookup, inverted lookups (NFT_LOOKUP_F_INV, "!= @set") can wrongly match or wrongly skip the catchall element, producing the wrong verdict. Fold the catchall lookup into `ext` before computing `found`, matching the order already used by nft_objref_map_eval(). Fixes: aaa31047a6d2 ("netfilter: nftables: add catch-all set element support") Signed-off-by: Tamaki Yanagawa Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Florian Westphal --- net/netfilter/nft_lookup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index ba512e94b402..19887439847d 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -103,13 +103,13 @@ void nft_lookup_eval(const struct nft_expr *expr, bool found; ext = nft_set_do_lookup(net, set, ®s->data[priv->sreg]); + if (!ext) + ext = nft_set_catchall_lookup(net, set); + found = !!ext ^ priv->invert; if (!found) { - ext = nft_set_catchall_lookup(net, set); - if (!ext) { - regs->verdict.code = NFT_BREAK; - return; - } + regs->verdict.code = NFT_BREAK; + return; } if (ext) { -- cgit v1.2.3 From 5d0c22e73656d050daffad10a2ba8765ce8441c8 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 2 Jul 2026 15:46:57 +0200 Subject: netfilter: ipset: mark the rcu locked areas properly When we bump the uref counter, there's no need to keep the rcu lock because the referred hash table can't disappear. Also, from the same reason in mtype_gc we need the rcu lock and not a spinlock. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index dedf59b661dd..c9a071766243 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -569,9 +569,10 @@ mtype_gc(struct work_struct *work) set = gc->set; h = set->data; - spin_lock_bh(&set->lock); - t = ipset_dereference_set(h->table, set); + rcu_read_lock_bh(); + t = rcu_dereference_bh(h->table); atomic_inc(&t->uref); + rcu_read_unlock_bh(); numof_locks = ahash_numof_locks(t->htable_bits); r = gc->region++; if (r >= numof_locks) { @@ -580,7 +581,6 @@ mtype_gc(struct work_struct *work) next_run = (IPSET_GC_PERIOD(set->timeout) * HZ) / numof_locks; if (next_run < HZ/10) next_run = HZ/10; - spin_unlock_bh(&set->lock); mtype_gc_do(set, h, t, r); @@ -860,15 +860,13 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, key = HKEY(value, h->initval, t->htable_bits); r = ahash_region(key); atomic_inc(&t->uref); + rcu_read_unlock_bh(); elements = t->hregion[r].elements; maxelem = t->maxelem; if (elements >= maxelem) { u32 e; - if (SET_WITH_TIMEOUT(set)) { - rcu_read_unlock_bh(); + if (SET_WITH_TIMEOUT(set)) mtype_gc_do(set, h, t, r); - rcu_read_lock_bh(); - } maxelem = h->maxelem; elements = 0; for (e = 0; e < ahash_numof_locks(t->htable_bits); e++) @@ -876,7 +874,6 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (elements >= maxelem && SET_WITH_FORCEADD(set)) forceadd = true; } - rcu_read_unlock_bh(); spin_lock_bh(&t->hregion[r].lock); n = rcu_dereference_bh(hbucket(t, key)); -- cgit v1.2.3 From cffcf57bf03cb7f7e83d10f760b5f34e5c51d9b3 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 2 Jul 2026 15:46:58 +0200 Subject: netfilter: ipset: exclude gc when resize is in progress Zhengchuan Liang and Eulgyu Kim reported that because resize does not copy the comment extension into the resized set but uses it's pointer, ongoing gc can free the extension in the original set which then results stale pointer in the resized one. The proposed patch was to recreate the extensions for every element in the resized set. It is both expensive and wastes memory, so better exclude gc when resizing in progress detected: resizing will destroy the original set anyway, so doing gc on it is unnecessary. Introduce a new spinlock to exclude parallel gc and resize. Because we just set and check a bool value, there's no need for the parameter to be atomic_t and rename it for better readability. Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Reported by: Zhengchuan Liang Reported by: Eulgyu Kim Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_hash_gen.h | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index c9a071766243..8104dbac02fa 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -75,12 +75,13 @@ struct hbucket { struct htable_gc { struct delayed_work dwork; struct ip_set *set; /* Set the gc belongs to */ + spinlock_t lock; /* Lock to exclude gc and resize */ u32 region; /* Last gc run position */ }; /* The hash table: the table size stored here in order to make resizing easy */ struct htable { - atomic_t ref; /* References for resizing */ + bool resizing; /* Mark ongoing resize */ atomic_t uref; /* References for dumping and gc */ u8 htable_bits; /* size of hash table == 2^htable_bits */ u32 maxelem; /* Maxelem per region */ @@ -582,9 +583,12 @@ mtype_gc(struct work_struct *work) if (next_run < HZ/10) next_run = HZ/10; - mtype_gc_do(set, h, t, r); + spin_lock_bh(&gc->lock); + if (!t->resizing) + mtype_gc_do(set, h, t, r); + spin_unlock_bh(&gc->lock); - if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) { + if (atomic_dec_and_test(&t->uref) && t->resizing) { pr_debug("Table destroy after resize by expire: %p\n", t); mtype_ahash_destroy(set, t, false); } @@ -672,11 +676,13 @@ retry: spin_lock_init(&t->hregion[i].lock); /* There can't be another parallel resizing, - * but dumping, gc, kernel side add/del are possible + * but dumping and kernel side add/del are possible */ orig = ipset_dereference_bh_nfnl(h->table); - atomic_set(&orig->ref, 1); atomic_inc(&orig->uref); + spin_lock_bh(&h->gc.lock); + orig->resizing = true; + spin_unlock_bh(&h->gc.lock); pr_debug("attempt to resize set %s from %u to %u, t %p\n", set->name, orig->htable_bits, htable_bits, orig); for (r = 0; r < ahash_numof_locks(orig->htable_bits); r++) { @@ -792,7 +798,9 @@ out: cleanup: rcu_read_unlock_bh(); - atomic_set(&orig->ref, 0); + spin_lock_bh(&h->gc.lock); + orig->resizing = false; + spin_unlock_bh(&h->gc.lock); atomic_dec(&orig->uref); mtype_ahash_destroy(set, t, false); if (ret == -EAGAIN) @@ -1000,7 +1008,7 @@ overwrite_extensions: ret = 0; resize: spin_unlock_bh(&t->hregion[r].lock); - if (atomic_read(&t->ref) && ext->target) { + if (t->resizing && ext && ext->target) { /* Resize is in process and kernel side add, save values */ struct mtype_resize_ad *x; @@ -1027,7 +1035,7 @@ set_full: unlock: spin_unlock_bh(&t->hregion[r].lock); out: - if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) { + if (atomic_dec_and_test(&t->uref) && t->resizing) { pr_debug("Table destroy after resize by add: %p\n", t); mtype_ahash_destroy(set, t, false); } @@ -1090,7 +1098,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, #endif ip_set_ext_destroy(set, data); - if (atomic_read(&t->ref) && ext->target) { + if (t->resizing && ext && ext->target) { /* Resize is in process and kernel side del, * save values */ @@ -1141,7 +1149,7 @@ out: list_add(&x->list, &h->ad); spin_unlock_bh(&set->lock); } - if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) { + if (atomic_dec_and_test(&t->uref) && t->resizing) { pr_debug("Table destroy after resize by del: %p\n", t); mtype_ahash_destroy(set, t, false); } @@ -1350,7 +1358,7 @@ mtype_uref(struct ip_set *set, struct netlink_callback *cb, bool start) rcu_read_unlock_bh(); } else if (cb->args[IPSET_CB_PRIVATE]) { t = (struct htable *)cb->args[IPSET_CB_PRIVATE]; - if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) { + if (atomic_dec_and_test(&t->uref) && t->resizing) { pr_debug("Table destroy after resize " " by dump: %p\n", t); mtype_ahash_destroy(set, t, false); @@ -1590,6 +1598,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, return -ENOMEM; } h->gc.set = set; + spin_lock_init(&h->gc.lock); for (i = 0; i < ahash_numof_locks(hbits); i++) spin_lock_init(&t->hregion[i].lock); h->maxelem = maxelem; -- cgit v1.2.3 From 672321302ed682ccb903004f435bbdb353534a9c Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 2 Jul 2026 15:46:59 +0200 Subject: netfilter: ipset: cleanup the add/del backlog when resize failed Sashiko pointed out that the add/del backlog was not cleaned up when resize failed. Fix it in the corresponding error path. Also, make sure that the add/del backlog is htable-specific so when resize creates a new htable, old/new backlog can't be mixed up. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_hash_gen.h | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 8104dbac02fa..c0132d0f4cc0 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -85,6 +85,7 @@ struct htable { atomic_t uref; /* References for dumping and gc */ u8 htable_bits; /* size of hash table == 2^htable_bits */ u32 maxelem; /* Maxelem per region */ + struct list_head ad; /* Resize add|del backlist */ struct ip_set_region *hregion; /* Region locks and ext sizes */ struct hbucket __rcu *bucket[]; /* hashtable buckets */ }; @@ -302,7 +303,6 @@ struct htype { u8 netmask; /* netmask value for subnets to store */ union nf_inet_addr bitmask; /* stores bitmask */ #endif - struct list_head ad; /* Resize add|del backlist */ struct mtype_elem next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_NETS struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */ @@ -452,13 +452,14 @@ static void mtype_destroy(struct ip_set *set) { struct htype *h = set->data; + struct htable *t = (__force struct htable *)h->table; struct list_head *l, *lt; - mtype_ahash_destroy(set, (__force struct htable *)h->table, true); - list_for_each_safe(l, lt, &h->ad) { + list_for_each_safe(l, lt, &t->ad) { list_del(l); kfree(l); } + mtype_ahash_destroy(set, t, true); kfree(h); set->data = NULL; @@ -672,6 +673,7 @@ retry: } t->htable_bits = htable_bits; t->maxelem = h->maxelem / ahash_numof_locks(htable_bits); + INIT_LIST_HEAD(&t->ad); for (i = 0; i < ahash_numof_locks(htable_bits); i++) spin_lock_init(&t->hregion[i].lock); @@ -774,7 +776,7 @@ retry: * Kernel-side add cannot trigger a resize and userspace actions * are serialized by the mutex. */ - list_for_each_safe(l, lt, &h->ad) { + list_for_each_safe(l, lt, &orig->ad) { x = list_entry(l, struct mtype_resize_ad, list); if (x->ad == IPSET_ADD) { mtype_add(set, &x->d, &x->ext, &x->mext, x->flags); @@ -801,10 +803,21 @@ cleanup: spin_lock_bh(&h->gc.lock); orig->resizing = false; spin_unlock_bh(&h->gc.lock); + /* Make sure parallel readers see that orig->resizing is false + * before we decrement uref */ + synchronize_rcu(); atomic_dec(&orig->uref); mtype_ahash_destroy(set, t, false); if (ret == -EAGAIN) goto retry; + + /* Cleanup the backlog of ADD/DEL elements */ + spin_lock_bh(&set->lock); + list_for_each_safe(l, lt, &orig->ad) { + list_del(l); + kfree(l); + } + spin_unlock_bh(&set->lock); goto out; hbwarn: @@ -1022,7 +1035,7 @@ resize: memcpy(&x->mext, mext, sizeof(struct ip_set_ext)); x->flags = flags; spin_lock_bh(&set->lock); - list_add_tail(&x->list, &h->ad); + list_add_tail(&x->list, &t->ad); spin_unlock_bh(&set->lock); } goto out; @@ -1146,7 +1159,7 @@ out: spin_unlock_bh(&t->hregion[r].lock); if (x) { spin_lock_bh(&set->lock); - list_add(&x->list, &h->ad); + list_add(&x->list, &t->ad); spin_unlock_bh(&set->lock); } if (atomic_dec_and_test(&t->uref) && t->resizing) { @@ -1625,9 +1638,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, } t->htable_bits = hbits; t->maxelem = h->maxelem / ahash_numof_locks(hbits); + INIT_LIST_HEAD(&t->ad); RCU_INIT_POINTER(h->table, t); - - INIT_LIST_HEAD(&h->ad); set->data = h; #ifndef IP_SET_PROTO_UNDEF if (set->family == NFPROTO_IPV4) { -- cgit v1.2.3 From 724f32699aeabcbd294377904b40b456fd5c67eb Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 2 Jul 2026 15:47:00 +0200 Subject: netfilter: ipset: allocate the proper memory for the generic hash structure Because a single create function is emitted for every hash type, from the IPv4 and IPv6 generic hash structure definitions the last one, i.e. the IPv6 was in effect for IPv4 too. Use the proper size when allocating the structure. Comment properly that because create() refers to elements of the generic hash structure, all referred ones must come before the IPv4/IPv6 dependent 'next' member. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index c0132d0f4cc0..8231317b0f1f 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -303,10 +303,13 @@ struct htype { u8 netmask; /* netmask value for subnets to store */ union nf_inet_addr bitmask; /* stores bitmask */ #endif - struct mtype_elem next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_NETS struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */ #endif + /* Because 'next' is IPv4/IPv6 dependent, no elements of this + * structure and referred in create() may come after 'next'. + */ + struct mtype_elem next; /* temporary storage for uadd */ }; /* ADD|DEL entries saved during resize */ @@ -1584,7 +1587,13 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, if (tb[IPSET_ATTR_MAXELEM]) maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); - hsize = sizeof(*h); +#ifdef IP_SET_PROTO_UNDEF + hsize = sizeof(struct htype); +#else + hsize = set->family == NFPROTO_IPV6 ? + sizeof(struct IPSET_TOKEN(HTYPE, 6)) : + sizeof(struct IPSET_TOKEN(HTYPE, 4)); +#endif h = kzalloc(hsize, GFP_KERNEL); if (!h) return -ENOMEM; -- cgit v1.2.3 From c328b90c17fc5fa7786503695152880b2afb9326 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:54 +0200 Subject: netfilter: flowtable: use dst in this direction when pushing IPIP header When pushing the IPIP header, the route of the other direction is used to calculate the headroom, use the route in this direction. Accessing the other tuple to set the IP source and destination is fine because this tuple does not provide such information to avoid storing redundant information. However, this tuple already provides the dst for this direction, this went unnoticed because this bug affects headroom and iph->frag_off only at this stage. Fixes: d30301ba4b07 ("netfilter: flowtable: Add IPIP tx sw acceleration") Fixes: 93cf357fa797 ("netfilter: flowtable: Add IP6IP6 tx sw acceleration") Cc: stable@vger.kernel.org Acked-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- net/netfilter/nf_flow_table_ip.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 29e93ac1e2e4..089f2bc19972 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -590,10 +590,10 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id, static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - __be32 *ip_daddr) + struct dst_entry *dst, __be32 *ip_daddr) { struct iphdr *iph = (struct iphdr *)skb_network_header(skb); - struct rtable *rt = dst_rtable(tuple->dst_cache); + struct rtable *rt = dst_rtable(dst); u8 tos = iph->tos, ttl = iph->ttl; __be16 frag_off = iph->frag_off; u32 headroom = sizeof(*iph); @@ -636,21 +636,22 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb, static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, - __be32 *ip_daddr) + struct dst_entry *dst, __be32 *ip_daddr) { if (tuple->tun_num) - return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr); + return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr); return 0; } static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, + struct dst_entry *dst, struct in6_addr **ip6_daddr) { struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb); - struct rtable *rt = dst_rtable(tuple->dst_cache); __u8 dsfield = ipv6_get_dsfield(ip6h); + struct rtable *rt = dst_rtable(dst); struct flowi6 fl6 = { .daddr = tuple->tun.src_v6, .saddr = tuple->tun.dst_v6, @@ -696,10 +697,11 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb, static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb, struct flow_offload_tuple *tuple, + struct dst_entry *dst, struct in6_addr **ip6_daddr) { if (tuple->tun_num) - return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr); + return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr); return 0; } @@ -842,7 +844,8 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, other_tuple = &flow->tuplehash[!dir].tuple; ip_daddr = other_tuple->src_v4.s_addr; - if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0) + if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, + tuplehash->tuple.dst_cache, &ip_daddr) < 0) return NF_DROP; switch (tuplehash->tuple.xmit_type) { @@ -1158,6 +1161,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ip6_daddr = &other_tuple->src_v6; if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple, + tuplehash->tuple.dst_cache, &ip6_daddr) < 0) return NF_DROP; -- cgit v1.2.3 From 6c5dcab95f4cd42a1648739ec9300fbb4b1a021f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:55 +0200 Subject: netfilter: flowtable: IPIP tunnel hardware offload is not yet support No driver supports for IPIP tunnels yet, give up early on setting up the hardware offload for this scenario. This patch adds a stub that can be enhanced to add more configuration that are currently not supported. As of now, the offload work is enqueued to the worker, then ignored if the hardware offload configuration is not supported. Check the NF_FLOW_HW flag to know if this entry was already tried once to be offloaded so this is not retried on refresh when unsupported. Move NF_FLOW_HW flag check to nf_flow_offload_add(). If this NF_FLOW_HW flag is unset the _del and _stats variants are never called. This can be updated later on to skip hardware offload work to be queued in case hardware offload does not support it. Fixes: d98103575dcd ("netfilter: flowtable: Add IP6IP6 rx sw acceleration") Fixes: ab427db17885 ("netfilter: flowtable: Add IPIP rx sw acceleration") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Reported-by: Zhengyang Chen Signed-off-by: Pablo Neira Ayuso Acked-by: Lorenzo Bianconi Signed-off-by: Florian Westphal --- include/net/netfilter/nf_flow_table.h | 2 ++ net/netfilter/nf_flow_table_core.c | 7 +++---- net/netfilter/nf_flow_table_offload.c | 22 ++++++++++++++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index 7b23b245a5a8..dc5c9b48e65a 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -357,6 +357,8 @@ static inline int nf_flow_register_bpf(void) void nf_flow_offload_add(struct nf_flowtable *flowtable, struct flow_offload *flow); +void nf_flow_offload_refresh(struct nf_flowtable *flowtable, + struct flow_offload *flow); void nf_flow_offload_del(struct nf_flowtable *flowtable, struct flow_offload *flow); void nf_flow_offload_stats(struct nf_flowtable *flowtable, diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 99c5b9d671a0..d06ce0848b68 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -345,10 +345,8 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) nf_ct_refresh(flow->ct, NF_CT_DAY); - if (nf_flowtable_hw_offload(flow_table)) { - __set_bit(NF_FLOW_HW, &flow->flags); + if (nf_flowtable_hw_offload(flow_table)) nf_flow_offload_add(flow_table, flow); - } return 0; } @@ -369,7 +367,8 @@ void flow_offload_refresh(struct nf_flowtable *flow_table, test_bit(NF_FLOW_CLOSING, &flow->flags)) return; - nf_flow_offload_add(flow_table, flow); + if (test_bit(NF_FLOW_HW, &flow->flags)) + nf_flow_offload_refresh(flow_table, flow); } EXPORT_SYMBOL_GPL(flow_offload_refresh); diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index 002ec15d988b..801a3dd9ceea 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -1101,9 +1101,17 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable, return offload; } +static bool nf_flow_offload_unsupported(struct flow_offload *flow) +{ + if (flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.tun_num || + flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.tun_num) + return true; -void nf_flow_offload_add(struct nf_flowtable *flowtable, - struct flow_offload *flow) + return false; +} + +void nf_flow_offload_refresh(struct nf_flowtable *flowtable, + struct flow_offload *flow) { struct flow_offload_work *offload; @@ -1114,6 +1122,16 @@ void nf_flow_offload_add(struct nf_flowtable *flowtable, flow_offload_queue_work(offload); } +void nf_flow_offload_add(struct nf_flowtable *flowtable, + struct flow_offload *flow) +{ + if (nf_flow_offload_unsupported(flow)) + return; + + set_bit(NF_FLOW_HW, &flow->flags); + nf_flow_offload_refresh(flowtable, flow); +} + void nf_flow_offload_del(struct nf_flowtable *flowtable, struct flow_offload *flow) { -- cgit v1.2.3 From fa7395c02d95e51bad2952325d2d6503bfbad437 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 30 Jun 2026 11:40:56 +0200 Subject: netfilter: flowtable: support IPIP tunnel with direct xmit The combination of IPIP tunnel with direct xmit, eg. bridge device, breaks because no dst_entry is provided to check the skb headroom and to set the iph->frag_off field. This leads to invalid dst usage and can trigger a crash in the tunnel transmit path. Fix this by moving dst_cache and dst_cookie out of the runtime union so that they can be shared by neighbour, xfrm, and direct tunnel flows. For FLOW_OFFLOAD_XMIT_DIRECT tuples carrying tunnel metadata, preserve route state in these shared fields and release it through the common dst release path. Since dst_entry is now available to the three supported xmit modes and dst_release() already deals with NULL dst, remove the xmit type check in nft_flow_dst_release(). Moreover, skip the check if the dst entry is NULL in nf_flow_dst_check() which is now the case for the direct xmit case. Based on patch from Rein Wei . Fixes: d30301ba4b07 ("netfilter: flowtable: Add IPIP tx sw acceleration") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Reported-by: Zhengyang Chen Reported-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Acked-by: Lorenzo Bianconi Signed-off-by: Florian Westphal --- include/net/netfilter/nf_flow_table.h | 5 +++-- net/netfilter/nf_flow_table_core.c | 12 ++++++++---- net/netfilter/nf_flow_table_ip.c | 3 +-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index dc5c9b48e65a..ce414118962f 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -155,11 +155,12 @@ struct flow_offload_tuple { tun_num:2, in_vlan_ingress:2; u16 mtu; + u32 dst_cookie; + struct dst_entry *dst_cache; + union { struct { - struct dst_entry *dst_cache; u32 ifidx; - u32 dst_cookie; }; struct { u32 ifidx; diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index d06ce0848b68..2a829b5e8240 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -127,12 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow, switch (route->tuple[dir].xmit_type) { case FLOW_OFFLOAD_XMIT_DIRECT: + if (flow_tuple->tun_num) { + flow_tuple->dst_cache = dst; + flow_tuple->dst_cookie = + flow_offload_dst_cookie(flow_tuple); + } memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest, ETH_ALEN); memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source, ETH_ALEN); flow_tuple->out.ifidx = route->tuple[dir].out.ifindex; - dst_release(dst); + if (!flow_tuple->tun_num) + dst_release(dst); break; case FLOW_OFFLOAD_XMIT_XFRM: case FLOW_OFFLOAD_XMIT_NEIGH: @@ -152,9 +158,7 @@ static int flow_offload_fill_route(struct flow_offload *flow, static void nft_flow_dst_release(struct flow_offload *flow, enum flow_offload_tuple_dir dir) { - if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH || - flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) - dst_release(flow->tuplehash[dir].tuple.dst_cache); + dst_release(flow->tuplehash[dir].tuple.dst_cache); } void flow_offload_route_init(struct flow_offload *flow, diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 089f2bc19972..0b78decce8a9 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -299,8 +299,7 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu) static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple) { - if (tuple->xmit_type != FLOW_OFFLOAD_XMIT_NEIGH && - tuple->xmit_type != FLOW_OFFLOAD_XMIT_XFRM) + if (!tuple->dst_cache) return true; return dst_check(tuple->dst_cache, tuple->dst_cookie); -- cgit v1.2.3 From da5b58478a9c1b85608c9e40a3b8432d071b409e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 5 Jul 2026 15:29:13 +0200 Subject: netfilter: handle unreadable frags sashiko reports: When an skb with unreadable fragments (such as from devmem TCP, where skb_frags_readable(skb) returns false) is processed by the u32 module, skb_copy_bits() will safely return a negative error code [..] xt_u32: bail out with hotdrop in this case. gather_frags: return -1, just as if we had no fragment header. nfnetlink_queue: restrict to the linear part. nfnetlink_log: restrict to the linear part. v2: - skb_zerocopy helpers don't copy readable flag, i.e. nfnetlink_queue is broken too xt_u32 shouldn't return true if hotdrop was set. Fixes: 65249feb6b3d ("net: add support for skbs with unreadable frags") Cc: stable@vger.kernel.org Acked-by: Mina Almasry Signed-off-by: Florian Westphal --- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/netfilter/nfnetlink_log.c | 26 +++++++++++++++++--------- net/netfilter/nfnetlink_queue.c | 16 ++++++++++++---- net/netfilter/xt_u32.c | 16 +++++++++++----- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 3637b20d3fa4..599c49bf0a0a 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -419,7 +419,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) return -1; } if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) - BUG(); + return -1; if (nexthdr == NEXTHDR_AUTH) hdrlen = ipv6_authlen(&hdr); else diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index fa3657599861..5fee61b3813c 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -676,7 +676,7 @@ __build_packet_message(struct nfnl_log_net *log, goto nla_put_failure; if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) - BUG(); + goto nla_put_failure; } nlh->nlmsg_len = inst->skb->tail - old_tail; @@ -698,6 +698,21 @@ static const struct nf_loginfo default_loginfo = { }, }; +static unsigned int nfulnl_get_copy_len(const struct nf_loginfo *li, + const struct sk_buff *skb, + unsigned int copy_len) +{ + unsigned int len = skb->len; + + if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) && + li->u.ulog.copy_len < copy_len) + copy_len = li->u.ulog.copy_len; + if (!skb_frags_readable(skb)) + len = skb_headlen(skb); + + return min(len, copy_len); +} + /* log handler for internal netfilter logging api */ static void nfulnl_log_packet(struct net *net, @@ -790,14 +805,7 @@ nfulnl_log_packet(struct net *net, break; case NFULNL_COPY_PACKET: - data_len = inst->copy_range; - if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) && - (li->u.ulog.copy_len < data_len)) - data_len = li->u.ulog.copy_len; - - if (data_len > skb->len) - data_len = skb->len; - + data_len = nfulnl_get_copy_len(li, skb, inst->copy_range); size += nla_total_size(data_len); break; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 35d4c6c628ff..b8aaf39cb4d8 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -690,6 +690,17 @@ static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr, } #endif +static unsigned int nfqnl_get_data_len(const struct sk_buff *entskb, + unsigned int copy_range) +{ + unsigned int data_len = entskb->len; + + if (!skb_frags_readable(entskb)) + data_len = skb_headlen(entskb); + + return min(data_len, copy_range); +} + static struct sk_buff * nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry, @@ -755,10 +766,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, nf_queue_checksum_help(entskb)) return NULL; - data_len = READ_ONCE(queue->copy_range); - if (data_len > entskb->len) - data_len = entskb->len; - + data_len = nfqnl_get_data_len(entskb, READ_ONCE(queue->copy_range)); hlen = skb_zerocopy_headlen(entskb); hlen = min_t(unsigned int, hlen, data_len); size += sizeof(struct nlattr) + hlen; diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index ec1a21e3b6e2..dabbaa742874 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -14,8 +14,8 @@ #include #include -static bool u32_match_it(const struct xt_u32 *data, - const struct sk_buff *skb) +static int u32_match_it(const struct xt_u32 *data, + const struct sk_buff *skb) { const struct xt_u32_test *ct; unsigned int testind; @@ -40,7 +40,8 @@ static bool u32_match_it(const struct xt_u32 *data, return false; if (skb_copy_bits(skb, pos, &n, sizeof(n)) < 0) - BUG(); + return -1; + val = ntohl(n); nnums = ct->nnums; @@ -68,7 +69,7 @@ static bool u32_match_it(const struct xt_u32 *data, if (skb_copy_bits(skb, at + pos, &n, sizeof(n)) < 0) - BUG(); + return -1; val = ntohl(n); break; } @@ -90,9 +91,14 @@ static bool u32_match_it(const struct xt_u32 *data, static bool u32_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_u32 *data = par->matchinfo; - bool ret; + int ret; ret = u32_match_it(data, skb); + if (ret < 0) { + par->hotdrop = true; + return false; + } + return ret ^ data->invert; } -- cgit v1.2.3 From bae7ce7bafb59e42dc0e0e2999fdd9d1cffe3866 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:22 +0800 Subject: ipvs: pass parsed transport offset to state handlers IPVS callers already parse the packet into struct ip_vs_iphdr before updating connection state. For IPv6 this records the real transport-header offset after extension headers in iph.len. Pass this parsed transport offset through ip_vs_set_state() and the protocol state_transition() callback so protocol handlers can use the same packet context as scheduling and NAT handling. This patch only changes the common callback plumbing and adapts the protocol callback signatures; TCP and SCTP start using the value in follow-up patches. Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- include/net/ip_vs.h | 3 ++- net/netfilter/ipvs/ip_vs_core.c | 10 +++++----- net/netfilter/ipvs/ip_vs_proto_sctp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_tcp.c | 3 ++- net/netfilter/ipvs/ip_vs_proto_udp.c | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 49297fec448a..417ff51f62fc 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -752,7 +752,8 @@ struct ip_vs_protocol { void (*state_transition)(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd); + struct ip_vs_proto_data *pd, + unsigned int iph_len); int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 906f2c361676..f79c09869636 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -398,10 +398,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) static inline void ip_vs_set_state(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, unsigned int iph_len) { if (likely(pd->pp->state_transition)) - pd->pp->state_transition(cp, direction, skb, pd); + pd->pp->state_transition(cp, direction, skb, pd, iph_len); } static inline int @@ -803,7 +803,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_in_stats(cp, skb); /* set state */ - ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len); /* transmit the first SYN packet */ ret = cp->packet_xmit(skb, cp, pd->pp, iph); @@ -1484,7 +1484,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, after_nat: ip_vs_out_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len); skb->ipvs_property = 1; if (!(cp->flags & IP_VS_CONN_F_NFCT)) ip_vs_notrack(skb); @@ -2233,7 +2233,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet"); ip_vs_in_stats(cp, skb); - ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp, pp, &iph); /* do not touch skb anymore */ diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 63c78a1f3918..394367b7b388 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -468,7 +468,8 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, static void sctp_state_transition(struct ip_vs_conn *cp, int direction, - const struct sk_buff *skb, struct ip_vs_proto_data *pd) + const struct sk_buff *skb, struct ip_vs_proto_data *pd, + unsigned int iph_len) { spin_lock_bh(&cp->lock); set_sctp_state(pd, cp, direction, skb); diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 8cc0a8ce6241..2d3f6aeafe52 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -579,7 +579,8 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, static void tcp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, + unsigned int iph_len) { struct tcphdr _tcph, *th; diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index f9de632e38cd..58f9e255927e 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -444,7 +444,8 @@ static const char * udp_state_name(int state) static void udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, - struct ip_vs_proto_data *pd) + struct ip_vs_proto_data *pd, + unsigned int iph_len) { if (unlikely(!pd)) { pr_err("UDP no ns data\n"); -- cgit v1.2.3 From 2500fa3958b1ba51c2b065e39db1b04dfa7e23a2 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:23 +0800 Subject: ipvs: use parsed transport offset in TCP state lookup TCP state handling reparses the skb to find the TCP header. For IPv6 it uses sizeof(struct ipv6hdr), while the surrounding IPVS code already parsed the packet with ip_vs_fill_iph_skb() and has the real transport-header offset in iph.len. This makes TCP state handling look at the wrong bytes when an IPv6 packet carries extension headers. Use the parsed transport offset passed down from ip_vs_set_state() when reading the TCP header. For IPv4 and for IPv6 packets without extension headers, the passed offset matches the previous value. Fixes: 0bbdd42b7efa6 ("IPVS: Extend protocol DNAT/SNAT and state handlers") Link: https://lore.kernel.org/netdev/20260705125659.37744-1-zhaoyz24@mails.tsinghua.edu.cn/ Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_proto_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 2d3f6aeafe52..f86b763efcc4 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -584,13 +584,7 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, { struct tcphdr _tcph, *th; -#ifdef CONFIG_IP_VS_IPV6 - int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); -#else - int ihl = ip_hdrlen(skb); -#endif - - th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph); + th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph); if (th == NULL) return; -- cgit v1.2.3 From 2f75c0faa3361b28e36cc0512b3299e163e25789 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 6 Jul 2026 18:16:24 +0800 Subject: ipvs: use parsed transport offset in SCTP state lookup set_sctp_state() reads the SCTP chunk header again in order to drive the IPVS SCTP state table. For IPv6 it computes the offset with sizeof(struct ipv6hdr), while the surrounding IPVS code uses iph.len from ip_vs_fill_iph_skb(), where ipv6_find_hdr() has already skipped extension headers and found the real transport header. This makes the state machine read from the wrong offset for IPv6 SCTP packets that carry extension headers. For example, an INIT packet with an 8-byte destination options header can be scheduled correctly by sctp_conn_schedule(), but set_sctp_state() reads the first byte of the SCTP verification tag as a DATA chunk type. The connection then moves from NONE to ESTABLISHED instead of INIT1, gets the longer established timeout, and updates the active/inactive destination counters incorrectly. This happens even though the SCTP handshake has not completed. Use the parsed transport offset passed down from ip_vs_set_state() for the SCTP chunk-header lookup. For IPv4 and IPv6 packets without extension headers this preserves the existing offset. Fixes: 2906f66a5682 ("ipvs: SCTP Trasport Loadbalancing Support") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/netdev/20260705123040.35755-1-zhaoyz24@mails.tsinghua.edu.cn/ Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: Claude Code:GLM-5.2 Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 394367b7b388..c67317be17df 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -372,20 +372,15 @@ static const char *sctp_state_name(int state) static inline void set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, - int direction, const struct sk_buff *skb) + int direction, const struct sk_buff *skb, + unsigned int iph_len) { struct sctp_chunkhdr _sctpch, *sch; unsigned char chunk_type; int event, next_state; - int ihl, cofs; - -#ifdef CONFIG_IP_VS_IPV6 - ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr); -#else - ihl = ip_hdrlen(skb); -#endif + int cofs; - cofs = ihl + sizeof(struct sctphdr); + cofs = iph_len + sizeof(struct sctphdr); sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch); if (sch == NULL) return; @@ -472,7 +467,7 @@ sctp_state_transition(struct ip_vs_conn *cp, int direction, unsigned int iph_len) { spin_lock_bh(&cp->lock); - set_sctp_state(pd, cp, direction, skb); + set_sctp_state(pd, cp, direction, skb, iph_len); spin_unlock_bh(&cp->lock); } -- cgit v1.2.3 From 3f7a535ff0fa627a0132803e4c2f903ceffcbc1c Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Tue, 7 Jul 2026 21:25:46 +0300 Subject: ipvs: ensure inner headers in ICMP errors are in headroom Sashiko points out that after stripping the outer headers with pskb_pull() we should ensure the inner IP headers in ICMP errors from tunnels are present in the skb headroom for functions like ipv4_update_pmtu(), icmp_send() and IP_VS_DBG(). Also, add more checks for the length of the inner headers. Fixes: f2edb9f7706d ("ipvs: implement passive PMTUD for IPIP packets") Link: https://sashiko.dev/#/patchset/20260702073430.67680-1-zhaoyz24%40mails.tsinghua.edu.cn Signed-off-by: Julian Anastasov Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_core.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index f79c09869636..35cbe821c259 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1767,6 +1767,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, bool tunnel, new_cp = false; union nf_inet_addr *raddr; char *outer_proto = "IPIP"; + unsigned int hlen_ipip; int ulen = 0; *related = 1; @@ -1804,9 +1805,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Now find the contained IP header */ offset += sizeof(_icmph); cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ raddr = (union nf_inet_addr *)&cih->daddr; + hlen_ipip = cih->ihl * 4; /* Special case for errors for IPIP/UDP/GRE tunnel packets */ tunnel = false; @@ -1822,9 +1824,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Only for known tunnel */ if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP) return NF_ACCEPT; - offset += cih->ihl * 4; + offset += hlen_ipip; cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ tunnel = true; } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */ @@ -1836,7 +1838,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Non-first fragment has no UDP/GRE header */ if (unlikely(cih->frag_off & htons(IP_OFFSET))) return NF_ACCEPT; - offset2 = offset + cih->ihl * 4; + offset2 = offset + hlen_ipip; if (cih->protocol == IPPROTO_UDP) { ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, raddr, &iproto); @@ -1905,6 +1907,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, } if (tunnel) { + unsigned int hlen_orig = cih->ihl * 4; __be32 info = ic->un.gateway; __u8 type = ic->type; __u8 code = ic->code; @@ -1921,6 +1924,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, goto ignore_tunnel; offset2 -= ihl + sizeof(_icmph); skb_reset_network_header(skb); + /* Ensure the IP header is present in headroom */ + if (!pskb_may_pull(skb, hlen_ipip)) + goto ignore_tunnel; IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n", outer_proto, &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, mtu); @@ -1936,8 +1942,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); } - if (mtu > 68 + sizeof(struct iphdr) + ulen) - mtu -= sizeof(struct iphdr) + ulen; + if (mtu > 68 + hlen_ipip + ulen) + mtu -= hlen_ipip + ulen; info = htonl(mtu); } /* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of @@ -1946,6 +1952,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (pskb_pull(skb, offset2) == NULL) goto ignore_tunnel; skb_reset_network_header(skb); + /* Ensure the IP header is present in headroom */ + if (!pskb_may_pull(skb, hlen_orig)) + goto ignore_tunnel; IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n", &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, type, code, ntohl(info)); -- cgit v1.2.3 From 7362ba0f9c96ac3ad6a2ca3995bd9fc9a28a8661 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Tue, 7 Jul 2026 10:18:03 +0000 Subject: watchdog: pretimeout: Fix UAF in watchdog_unregister_governor() When a watchdog governor is unregistered, it updates existing watchdog devices that were using this governor by falling back to `default_gov`. If the governor being unregistered is currently set as `default_gov`, the `default_gov` is never cleared. This leads to 2 use-after-free issues: 1. New watchdog devices registered after this point will inherit the dangling `default_gov`. 2. Existing watchdog devices using the unregistered governor will have their `wdd->gov` reassigned to the dangling `default_gov`. Fix the UAF by clearing `default_gov` if it matches the governor being unregistered. Fixes: da0d12ff2b82 ("watchdog: pretimeout: add panic pretimeout governor") Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20260707101803.3598173-1-tzungbi@kernel.org Signed-off-by: Guenter Roeck --- drivers/watchdog/watchdog_pretimeout.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c index 19eb2ed2c7cb..02e09b9e396d 100644 --- a/drivers/watchdog/watchdog_pretimeout.c +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -167,6 +167,8 @@ void watchdog_unregister_governor(struct watchdog_governor *gov) } spin_lock_irq(&pretimeout_lock); + if (default_gov == gov) + default_gov = NULL; list_for_each_entry(p, &pretimeout_list, entry) if (p->wdd->gov == gov) p->wdd->gov = default_gov; -- cgit v1.2.3 From bcfcd7619f277842430d197556463b401b839ee9 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 8 Jul 2026 10:41:54 +0800 Subject: watchdog: airoha: Prevent division by zero when clock frequency is zero clk_get_rate() can return 0 when the clock provider is not properly configured or the clock is unmanaged. The driver uses wdt_freq as a divisor directly in airoha_wdt_probe() to compute max_timeout and in airoha_wdt_get_timeleft() to compute the remaining time, which results in a division by zero. Add a check for wdt_freq == 0 in probe and return -EINVAL with dev_err_probe() to prevent the division by zero and provide a diagnostic message. Fixes: 3cf67f3769b8 ("watchdog: Add support for Airoha EN7851 watchdog") Signed-off-by: Wayen Yan Link: https://lore.kernel.org/r/178347932594.81327.4834644880399144119@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/airoha_wdt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/watchdog/airoha_wdt.c b/drivers/watchdog/airoha_wdt.c index dc8ca11c14d8..4bd333189b87 100644 --- a/drivers/watchdog/airoha_wdt.c +++ b/drivers/watchdog/airoha_wdt.c @@ -147,6 +147,9 @@ static int airoha_wdt_probe(struct platform_device *pdev) /* Watchdog ticks at half the bus rate */ airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2; + if (!airoha_wdt->wdt_freq) + return dev_err_probe(dev, -EINVAL, + "invalid clock frequency\n"); /* Initialize struct watchdog device */ wdog_dev = &airoha_wdt->wdog_dev; -- cgit v1.2.3 From abf76d3239dee97b66e7241ad04811f1ce562e28 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 9 Jun 2026 13:37:36 -0400 Subject: 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 @@ bail: mutex_unlock(&dev->lock); destruction: mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "release success"); return rv; } -- cgit v1.2.3 From af791d295737ea6b6ff2c8d8488462a49c14af01 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 6 Jul 2026 21:48:26 -0700 Subject: 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; -- cgit v1.2.3 From b7577fe4c47a31ca7c99714c53244a44af03cdfe Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 22 Jun 2026 16:31:24 +0200 Subject: 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; -- cgit v1.2.3 From ec84aad4c3594307d103af563991b4415ac5c8ab Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 30 Jun 2026 19:32:14 +0200 Subject: 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); -- cgit v1.2.3 From 575f87b9d4882b0d621192fdd754d7e09dbd5789 Mon Sep 17 00:00:00 2001 From: Rongguang Wei Date: Fri, 26 Jun 2026 13:58:20 +0800 Subject: 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 -- cgit v1.2.3 From 7d5c2f679147859c1db97d9f66366ff16909abee Mon Sep 17 00:00:00 2001 From: Bastian Blank Date: Thu, 18 Jun 2026 18:12:42 +0200 Subject: 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 -- cgit v1.2.3 From 6aa38ef0eab32df5409b72d62509de6ba09cea50 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 9 Jun 2026 14:26:40 +0800 Subject: 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 -- cgit v1.2.3 From 99611233f8cda833169fa6487d5dacdf189e5cb0 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 9 Jun 2026 14:26:41 +0800 Subject: 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[] = { -- cgit v1.2.3 From 6d07e4f7144b636b112fbe177d32e1cf85e2558e Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Tue, 7 Jul 2026 13:15:26 -0700 Subject: 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; -- cgit v1.2.3 From 027a84ac6b50c12ef767c15abfc58aa865820e9e Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:27:10 +0800 Subject: 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, -- cgit v1.2.3 From 72422525f641f68bed6ca3389d29ee3f41fdea33 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 10 Jun 2026 22:39:10 +0800 Subject: 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), }; -- cgit v1.2.3 From 25e252bcf1593b420b12a7231d9dd64b885a2ae2 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 10 Jun 2026 22:39:11 +0800 Subject: 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, }, }; -- cgit v1.2.3 From f576c75f95a52c71b30167d7efb6d47148f9c279 Mon Sep 17 00:00:00 2001 From: Jens Glathe Date: Sat, 30 May 2026 10:20:22 +0200 Subject: 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) { -- cgit v1.2.3 From 0bfeec21984fedd32987f4e4c0cde34b445af404 Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 18 Jun 2026 20:40:29 +0800 Subject: 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); } -- cgit v1.2.3 From 6df47500b557e01737eef6f6b07b12f97a35d841 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 5 Jun 2026 11:00:58 +0200 Subject: 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) { -- cgit v1.2.3 From f74e6e15485b68b92b2807071e822db6309b7e38 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 8 Jul 2026 11:34:30 +0100 Subject: 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, -- cgit v1.2.3 From af9ea231c0b4530edc389a3126a69e0699b7699d Mon Sep 17 00:00:00 2001 From: Shengming Hu Date: Mon, 6 Jul 2026 21:39:09 +0800 Subject: mm/slub: fix lost local objects when bulk remote free batch fills In free_to_pcs_bulk(), when remote_objects[] fills to PCS_BATCH_MAX, the code jumps to flush_remote to free the batch. If all remote entries have already been compacted out of p[] via tail swaps while local objects remain, the flush_remote path returns early since `i < size` no longer holds. The leftover local objects are then neither cached in the sheaf nor returned to the slab freelist, causing a memory leak. For illustration: size = 64, local objects at p[0..31], remote objects at p[32..63] After scanning all remotes: i = 32, size = 32 p[0..31] local objects are dropped. Harry pointed out that, although the logic contains a real leak, it does not appear to be triggerable with the current in-tree users. To hit this path, at least PCS_BATCH_MAX objects, currently hardcoded to 32, need to be collected in remote_objects[]. Looking at current kmem_cache_free_bulk() users: * maple_node has sheaf_capacity = 32 * skbuff_head_cache has sheaf_capacity = 28 * panthor and msm drivers have sheaf_capacity = 4 The sheaf capacity is, at least for now, derived purely from the object size, with the user-requested capacity used as a minimum. Therefore, among the current users, only maple_node has a sheaf_capacity large enough to reach PCS_BATCH_MAX. However, for the bug to trigger in maple_node, all objects in the sheaf would have to be from remote nodes. In that case, there would be no local objects left to leak. So this issue was found by code review rather than from a runtime report, and it does not seem to be triggerable by current users. Still, the bug could become reachable with future users, a different sheaf capacity, or a change to PCS_BATCH_MAX. Fix the logic by freeing a full remote batch in place during the scan and then continuing to process the compacted array. This keeps all local objects on the normal fast path, while the tail path only handles any leftover partial remote batch. The redundant next_remote_batch jump label is removed as well. Fixes: 989b09b73978 ("slab: skip percpu sheaves for remote object freeing") Signed-off-by: Shengming Hu Link: https://patch.msgid.link/202607062139095043SOsLi6TIf403tcjPf8fm@zte.com.cn Cc: stable@vger.kernel.org Reviewed-by: Harry Yoo (Oracle) Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 9ec774dc7009..65febe957886 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -6217,7 +6217,6 @@ static void free_to_pcs_bulk(struct kmem_cache *s, size_t size, void **p) void *remote_objects[PCS_BATCH_MAX]; unsigned int remote_nr = 0; -next_remote_batch: while (i < size) { struct slab *slab = virt_to_slab(p[i]); @@ -6232,8 +6231,11 @@ next_remote_batch: if (unlikely(!can_free_to_pcs(slab))) { remote_objects[remote_nr] = p[i]; p[i] = p[--size]; - if (++remote_nr >= PCS_BATCH_MAX) - goto flush_remote; + if (++remote_nr >= PCS_BATCH_MAX) { + __kmem_cache_free_bulk(s, remote_nr, &remote_objects[0]); + stat_add(s, FREE_SLOWPATH, remote_nr); + remote_nr = 0; + } continue; } @@ -6317,10 +6319,6 @@ flush_remote: if (remote_nr) { __kmem_cache_free_bulk(s, remote_nr, &remote_objects[0]); stat_add(s, FREE_SLOWPATH, remote_nr); - if (i < size) { - remote_nr = 0; - goto next_remote_batch; - } } } -- cgit v1.2.3 From a3320469e3c8a58c808a090f3e1b5bfccb4c59c6 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Mon, 27 Apr 2026 09:32:10 +0800 Subject: 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 -- cgit v1.2.3 From 3fb29495b486b1ec61708248cb388745fc607278 Mon Sep 17 00:00:00 2001 From: Jia Wang Date: Fri, 15 May 2026 09:18:05 +0800 Subject: 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 -- cgit v1.2.3 From ed446e8aa894883c08892cfee69782fdf8f6c3ca Mon Sep 17 00:00:00 2001 From: leixiang Date: Mon, 22 Jun 2026 15:51:01 +0800 Subject: 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); -- cgit v1.2.3 From a2ac823d8a228e392bc6a79ea63b550ab84a55b2 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Tue, 7 Jul 2026 16:23:48 +0800 Subject: 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 -- cgit v1.2.3 From 9285e4070df2c40585c3d7ec9571faa7a2b97e17 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Wed, 24 Jun 2026 15:05:16 -0700 Subject: 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); -- cgit v1.2.3 From ebdac7554abb347ca4197be241116842161acd9b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 12 Jun 2026 07:56:41 -0700 Subject: 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; -- cgit v1.2.3 From 3e6dd2b9b7b2884743ed7a0873b8743cc0df6d40 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 12 Jun 2026 07:56:42 -0700 Subject: 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 -- cgit v1.2.3 From 29bacb096d43bd04062f6655e50ceeed27fe6d2f Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 4 May 2026 06:28:18 -0400 Subject: 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) { -- cgit v1.2.3 From 46bc86c833956219bbfd246c1ffd832a479c5199 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 4 May 2026 06:28:19 -0400 Subject: 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); -- cgit v1.2.3 From 18d62044cda7a2b40f59d910659c0b0d6accad37 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 8 Jul 2026 10:43:27 +0200 Subject: 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); } -- cgit v1.2.3 From 65dfde57d1e29ce2b76fc23dd565eccd5c0bc0f0 Mon Sep 17 00:00:00 2001 From: Ricardo Robaina Date: Thu, 2 Jul 2026 11:04:11 -0300 Subject: 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); -- cgit v1.2.3 From 4ec10f38ff901dc10503d57cbdcf941248419ac1 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 8 Jul 2026 09:46:48 +0200 Subject: 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; -- cgit v1.2.3 From cfe950d79f524e72bc263f2b153a6d905a75a794 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 8 Jul 2026 09:46:49 +0200 Subject: 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 + tools/testing/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) -- cgit v1.2.3 From 91f0063ca99eb6d96c85bfe6b65405570511b99f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:29:26 +0800 Subject: 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 = { -- cgit v1.2.3 From 87063bab451963c2e424154437cd14c926127c42 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:43:52 +0800 Subject: 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 = { -- cgit v1.2.3 From 5140f099ecd8a2f2808b7f7b720ee1bad8468974 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Thu, 11 Jun 2026 17:02:15 -0400 Subject: 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; -- cgit v1.2.3 From 27934d02cbeb8a957dd11c985a579e58d30c5270 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Tue, 7 Jul 2026 11:07:16 -0400 Subject: 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, -- cgit v1.2.3 From 58aad119f4d65a8f91262687db302046a94fa9ac Mon Sep 17 00:00:00 2001 From: Kanala Ramalingeswara Reddy Date: Wed, 17 Jun 2026 18:04:36 +0530 Subject: 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)) -- cgit v1.2.3 From e2469bde3fcaa087dcfeccbc6306a9e76a79344e Mon Sep 17 00:00:00 2001 From: Kanala Ramalingeswara Reddy Date: Wed, 17 Jun 2026 18:04:53 +0530 Subject: 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; -- cgit v1.2.3 From 9c809e489802e40d7845f4daf5a9d828830a8724 Mon Sep 17 00:00:00 2001 From: Suresh Guttula Date: Fri, 26 Jun 2026 11:39:51 +0530 Subject: 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; -- cgit v1.2.3 From 18a7826aea6fd09f2d371c02cec70c7234fc4879 Mon Sep 17 00:00:00 2001 From: Kenneth Feng Date: Thu, 25 Jun 2026 17:48:22 +0800 Subject: 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) -- cgit v1.2.3 From 614e8a989d462876c4f6a659f56e1075e72ad515 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Wed, 1 Jul 2026 18:17:03 +0800 Subject: 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; -- cgit v1.2.3 From 04cc4aa3617b0ed67e859f91f09de5d896a46f3a Mon Sep 17 00:00:00 2001 From: Shahyan Soltani Date: Mon, 6 Jul 2026 08:15:21 -0400 Subject: 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) -- cgit v1.2.3 From 9743132a41f4d9d0e54c5f2adcb821b04796bab1 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Thu, 2 Jul 2026 21:43:39 -0400 Subject: 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); -- cgit v1.2.3 From a868196f03c2b19418ae3d2b69e195d668a271e5 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 2 Jul 2026 00:27:35 +0000 Subject: 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. -- cgit v1.2.3 From 31d6e6c0ba8d5a7bd59660035a089307100c5e8e Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 1 Jul 2026 18:13:50 +0200 Subject: 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 */ -- cgit v1.2.3 From 5bcd4d3058ebaf46ad2e163829d87dd4870c7a45 Mon Sep 17 00:00:00 2001 From: Ming-Hung Tsai Date: Tue, 30 Jun 2026 20:17:44 +0800 Subject: 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; } -- cgit v1.2.3 From d9c631e3fbd44246a2be781d26cfacbb9b8ec127 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Mon, 29 Jun 2026 15:47:40 +0000 Subject: 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; -- cgit v1.2.3 From da991cbd6767282000e995247c7dad39d22874c2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 20 Jun 2026 21:33:03 +0200 Subject: 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) >> -- cgit v1.2.3 From 981ccd97f7153d310dfa92a534525bbaf46752c2 Mon Sep 17 00:00:00 2001 From: Ingo Blechschmidt Date: Wed, 17 Jun 2026 16:33:42 +0200 Subject: 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; -- cgit v1.2.3 From 9ae672606c17891d90b282e3490b817620549599 Mon Sep 17 00:00:00 2001 From: Cao Guanghui Date: Wed, 17 Jun 2026 14:00:52 +0800 Subject: 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; -- cgit v1.2.3 From 8d4dd2db7f4f3af0d3cd51111d050301c1f00a5c Mon Sep 17 00:00:00 2001 From: Cao Guanghui Date: Wed, 17 Jun 2026 14:00:53 +0800 Subject: 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)) { -- cgit v1.2.3 From f0262c3a3f14d60140f6b826d40d44edf62c36d6 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 26 Jun 2026 16:29:13 -0400 Subject: 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 */ -- cgit v1.2.3 From 3888a656e8143e4b40f01143648c768fecbfefc6 Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 12 May 2026 14:49:41 -0400 Subject: 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; } -- cgit v1.2.3 From 8a93f77aec65574b8152c88eb73613336f92f86b Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 30 Jun 2026 09:58:18 -0400 Subject: 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 --- drivers/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; } -- cgit v1.2.3 From a88419af8dbd5abf8b1dfdfd9346376bce5413b4 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 1 Jul 2026 16:08:12 +0800 Subject: 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); -- cgit v1.2.3 From bb52249fbbe948875155ccd45cd8d74bf4ae747b Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 21 May 2026 09:18:59 -0400 Subject: 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 { -- cgit v1.2.3 From e987eabc02646920cd13ab75902693e99735eca0 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 6 Jul 2026 09:29:24 +0800 Subject: 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 --- .../gpu/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; } -- cgit v1.2.3 From 47ea05f246bebc81c7796f56265cffd812cf0601 Mon Sep 17 00:00:00 2001 From: David Francis Date: Mon, 6 Jul 2026 10:19:04 -0400 Subject: 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 --- .../gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 6 ++++++ .../gpu/drm/amd/amdkfd/kfd_device_queue_manager.h | 2 ++ drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 3 ++- .../gpu/drm/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 @@ out: 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]... -- cgit v1.2.3 From 3238c634725afbb2a137fdda762208510828f71d Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 6 Jul 2026 21:01:09 +0530 Subject: 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) -- cgit v1.2.3 From 0b043279e73880bee21d3b1f221bafda5af1b27e Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 21:30:17 +0800 Subject: 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)); } -- cgit v1.2.3 From dde3929e8d901ae9a7c29d0577ce9ca5cb4db35d Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 7 Jul 2026 11:03:18 +0200 Subject: 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; -- cgit v1.2.3 From 608362facd2d0f2667f68b7f42207707d59a0071 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 7 Jul 2026 11:03:56 +0200 Subject: 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; -- cgit v1.2.3 From 75f5c412fa867efa0bf9b646bffe0d912109e84a Mon Sep 17 00:00:00 2001 From: Zizhi Wo Date: Sat, 4 Jul 2026 09:53:20 +0800 Subject: 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) -- cgit v1.2.3 From d38f8bd771c4999b797d7074b348cf201414bd34 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 8 Jul 2026 18:57:22 -0500 Subject: 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; } -- cgit v1.2.3 From 46aea2c64e110ed1878fa5363f86ddc8fd79c9c4 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 30 Jun 2026 18:54:12 -0400 Subject: scsi: 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 Fixes: 583e518e7100 ("scsi: ufs: core: Add hba parameter to trace events") Signed-off-by: Steven Rostedt Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260630185412.283c26c5@gandalf.local.home Signed-off-by: Martin K. Petersen --- 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 ) ); -- cgit v1.2.3 From 1d3a742afeb761eaead774691bde1ced699e9a5d Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 7 Jul 2026 11:08:45 +0800 Subject: scsi: sg: Report request-table problems when any status is set SG_GET_REQUEST_TABLE reports per-request diagnostic state through sg_req_info::problem. The field is meant to indicate whether there is an error to report for a completed request. sg_fill_request_table() currently combines masked_status, host_status and driver_status with bitwise AND. This only reports a problem when all three status fields are non-zero at the same time. A normal target check condition, for example, has masked_status set while host_status and driver_status may both be zero, so the request is incorrectly reported as clean. Use the same condition as sg_new_read(), which sets SG_INFO_CHECK when any of the three status fields is non-zero. Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/54B60C19F7DB8889+20260707030845.970018-1-raoxu@uniontech.com Signed-off-by: Martin K. Petersen --- drivers/scsi/sg.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 74cd4e8a61c2..5408f002e6c0 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -863,10 +863,9 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) if (val >= SG_MAX_QUEUE) break; rinfo[val].req_state = srp->done + 1; - rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & - srp->header.driver_status; + rinfo[val].problem = srp->header.masked_status || + srp->header.host_status || + srp->header.driver_status; if (srp->done) rinfo[val].duration = srp->header.duration; -- cgit v1.2.3 From 1bd28625e25be549ee7c47532e7c3ef91c682410 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 7 Jul 2026 12:23:02 +0530 Subject: scsi: lpfc: Fix memory leak in lpfc_sli4_driver_resource_setup() The memory allocated for mboxq using mempool_alloc() is not freed in some of the early exit error paths. Fix that by moving the mempool_free() call to an earlier point after last use. Fixes: d79c9e9d4b3d ("scsi: lpfc: Support dynamic unbounded SGL lists on G7 hardware.") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Reviewed-by: Justin Tee Link: https://patch.msgid.link/20260707065304.949135-1-nihaal@cse.iitm.ac.in Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 82af59c913e9..23355f12fbff 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -8189,6 +8189,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) mempool_free(mboxq, phba->mbox_mem_pool); goto out_free_bsmbx; } + mempool_free(mboxq, phba->mbox_mem_pool); /* * 1 for cmd, 1 for rsp, NVME adds an extra one @@ -8311,8 +8312,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_sg_dma_buf; } - mempool_free(mboxq, phba->mbox_mem_pool); - /* Verify OAS is supported */ lpfc_sli4_oas_verify(phba); -- cgit v1.2.3 From 06c2b8d7ea2bcb014dd974fc3bc6d128d65d7477 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 2 Jul 2026 16:50:13 +0200 Subject: 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)) -- cgit v1.2.3 From 7b69729046a4c58f4cb457184e5ac4aaa179bff4 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 24 Jun 2026 14:19:10 +0800 Subject: 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 @@ unpin2: unpin1: unpin_user_page(aibv_page); out: + kvm_s390_gisc_unregister(kvm, fib->fmt0.isc); return rc; } -- cgit v1.2.3 From 866d03de6def89c386cdfd457b28a1f566e02565 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 2 Jul 2026 17:23:59 +0200 Subject: 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; } } -- cgit v1.2.3 From 4d4a21e38f1b87a76b3e63d4f837ff4e9b52d5a6 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 2 Jul 2026 17:24:05 +0200 Subject: 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; -- cgit v1.2.3 From 9489220fe0e69d2ca141e5062dd3ef3e2e55959f Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Thu, 2 Jul 2026 17:24:06 +0200 Subject: 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); -- cgit v1.2.3 From f4ef35efbb49527293309f668ea73ec5de9b8e7a Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Thu, 2 Jul 2026 10:59:49 +0800 Subject: selftests/net: fix EVP_MD_CTX leak in tcp_mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In tcp_mmap.c, both child_thread() and main() allocate an EVP_MD_CTX via EVP_MD_CTX_new() when integrity checking is enabled, but neither function releases the context. child_thread() misses the free in its common cleanup block, and main() returns without freeing the context. This results in a SHA256 context leak on every run that uses the ‑i (integrity) option. Add the missing EVP_MD_CTX_free() calls to the appropriate cleanup paths to fix the leak. Fixes: 5c5945dc695c ("selftests/net: Add SHA256 computation over data sent in tcp_mmap") Signed-off-by: Wang Yan Link: https://patch.msgid.link/20260702025949.442523-1-wangyan01@kylinos.cn Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/tcp_mmap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c index 4fcce5150850..2544ae35d07a 100644 --- a/tools/testing/selftests/net/tcp_mmap.c +++ b/tools/testing/selftests/net/tcp_mmap.c @@ -313,6 +313,8 @@ end: tcp_info_get_rcv_mss(fd)); } error: + if (ctx) + EVP_MD_CTX_free(ctx); munmap(buffer, buffer_sz); close(fd); if (zflg) @@ -606,6 +608,8 @@ int main(int argc, char *argv[]) EVP_DigestFinal_ex(ctx, digest, &digest_len); send(fd, digest, (size_t)SHA256_DIGEST_LENGTH, 0); } + if (ctx) + EVP_MD_CTX_free(ctx); close(fd); munmap(buffer, buffer_sz); return 0; -- cgit v1.2.3 From 2e2a83b4998af4384e677d3b2ac08565274279bf Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 1 Jul 2026 21:12:36 -0700 Subject: net: mana: Validate the packet length reported by the NIC Validate the packet length reported in the RX CQE before passing it to skb processing. The CQE is supplied by the NIC device and should not be blindly trusted. Cc: stable@vger.kernel.org Reviewed-by: Haiyang Zhang Signed-off-by: Dexuan Cui Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Link: https://patch.msgid.link/20260702041237.617719-2-decui@microsoft.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microsoft/mana/mana_en.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 7438ea6b3f26..dd4d1c0c582e 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2246,12 +2246,25 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, rxbuf_oob = &rxq->rx_oobs[curr]; WARN_ON_ONCE(rxbuf_oob->wqe_inf.wqe_size_in_bu != 1); - mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + if (unlikely(pktlen > rxq->datasize)) { + /* Increase it even if mana_rx_skb() isn't called. */ + rxq->rx_cq.work_done++; - /* Unsuccessful refill will have old_buf == NULL. - * In this case, mana_rx_skb() will drop the packet. - */ - mana_rx_skb(old_buf, old_fp, oob, rxq, i); + ++ndev->stats.rx_dropped; + netdev_warn_once(ndev, + "Dropped oversized RX packet: len=%u, datasize=%u\n", + pktlen, rxq->datasize); + + /* Reuse the RX buffer since rxbuf_oob is unchanged. */ + } else { + + mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + + /* Unsuccessful refill will have old_buf == NULL. + * In this case, mana_rx_skb() will drop the packet. + */ + mana_rx_skb(old_buf, old_fp, oob, rxq, i); + } mana_move_wq_tail(rxq->gdma_rq, rxbuf_oob->wqe_inf.wqe_size_in_bu); -- cgit v1.2.3 From c72a0f09c57f92113df69f9b902d11c9e4b132f5 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 1 Jul 2026 21:12:37 -0700 Subject: net: mana: Sync page pool RX frags for CPU MANA allocates RX buffers from page pool fragments when frag_count is greater than 1. In that case the buffers remain DMA mapped by page pool and the RX completion path does not call dma_unmap_single(). As a result, the implicit sync-for-CPU normally performed by dma_unmap_single() is missing before the packet data is passed to the networking stack. This breaks RX on configurations which require explicit DMA syncing, for example when booted with swiotlb=force. Fix this by recording the page pool page and DMA sync offset when the RX buffer is allocated, and syncing the received packet range for CPU access before handing the RX buffer to the stack. Fixes: 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead of full pages to improve memory efficiency.") Cc: stable@vger.kernel.org Reviewed-by: Haiyang Zhang Signed-off-by: Dexuan Cui Link: https://patch.msgid.link/20260702041237.617719-3-decui@microsoft.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/microsoft/mana/mana_en.c | 40 ++++++++++++++++++++++----- include/net/mana/mana.h | 8 ++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index dd4d1c0c582e..9d9bfd116dab 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2120,12 +2120,16 @@ drop: } static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, - dma_addr_t *da, bool *from_pool) + dma_addr_t *da, bool *from_pool, + struct page **pp_page, u32 *dma_sync_offset) { struct page *page; u32 offset; void *va; + *from_pool = false; + *pp_page = NULL; + *dma_sync_offset = 0; /* Don't use fragments for jumbo frames or XDP where it's 1 fragment * per page. @@ -2163,31 +2167,47 @@ static void *mana_get_rxfrag(struct mana_rxq *rxq, struct device *dev, va = page_to_virt(page) + offset; *da = page_pool_get_dma_addr(page) + offset + rxq->headroom; *from_pool = true; + *pp_page = page; + *dma_sync_offset = offset + rxq->headroom; return va; } /* Allocate frag for rx buffer, and save the old buf */ static void mana_refill_rx_oob(struct device *dev, struct mana_rxq *rxq, - struct mana_recv_buf_oob *rxoob, void **old_buf, - bool *old_fp) + struct mana_recv_buf_oob *rxoob, u32 pktlen, + void **old_buf, bool *old_fp) { + struct page *pp_page; + u32 dma_sync_offset; bool from_pool; dma_addr_t da; void *va; - va = mana_get_rxfrag(rxq, dev, &da, &from_pool); + va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset); if (!va) return; - if (!rxoob->from_pool || rxq->frag_count == 1) + if (!rxoob->from_pool || rxq->frag_count == 1) { dma_unmap_single(dev, rxoob->sgl[0].address, rxq->datasize, DMA_FROM_DEVICE); + } else { + /* The page pool maps the whole page and only syncs for device + * automatically (PP_FLAG_DMA_SYNC_DEV). Sync the received bytes + * for the CPU before they are read: this is required if DMA + * is incoherent or bounce buffers are used. + */ + page_pool_dma_sync_for_cpu(rxq->page_pool, rxoob->pp_page, + rxoob->dma_sync_offset, pktlen); + } *old_buf = rxoob->buf_va; *old_fp = rxoob->from_pool; rxoob->buf_va = va; rxoob->sgl[0].address = da; rxoob->from_pool = from_pool; + rxoob->pp_page = pp_page; + rxoob->dma_sync_offset = dma_sync_offset; } static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, @@ -2258,7 +2278,8 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq, /* Reuse the RX buffer since rxbuf_oob is unchanged. */ } else { - mana_refill_rx_oob(dev, rxq, rxbuf_oob, &old_buf, &old_fp); + mana_refill_rx_oob(dev, rxq, rxbuf_oob, pktlen, + &old_buf, &old_fp); /* Unsuccessful refill will have old_buf == NULL. * In this case, mana_rx_skb() will drop the packet. @@ -2668,6 +2689,8 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, struct mana_rxq *rxq, struct device *dev) { struct mana_port_context *mpc = netdev_priv(rxq->ndev); + struct page *pp_page = NULL; + u32 dma_sync_offset = 0; bool from_pool = false; dma_addr_t da; void *va; @@ -2675,13 +2698,16 @@ static int mana_fill_rx_oob(struct mana_recv_buf_oob *rx_oob, u32 mem_key, if (mpc->rxbufs_pre) va = mana_get_rxbuf_pre(rxq, &da); else - va = mana_get_rxfrag(rxq, dev, &da, &from_pool); + va = mana_get_rxfrag(rxq, dev, &da, &from_pool, &pp_page, + &dma_sync_offset); if (!va) return -ENOMEM; rx_oob->buf_va = va; rx_oob->from_pool = from_pool; + rx_oob->pp_page = pp_page; + rx_oob->dma_sync_offset = dma_sync_offset; rx_oob->sgl[0].address = da; rx_oob->sgl[0].size = rxq->datasize; diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 13c87baf018e..04acb6791dbd 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -305,6 +305,14 @@ struct mana_recv_buf_oob { void *buf_va; bool from_pool; /* allocated from a page pool */ + /* head page of the page_pool fragment; valid only when + * from_pool && frag_count > 1. + */ + struct page *pp_page; + /* Fragment offset plus rxq->headroom, passed to + * page_pool_dma_sync_for_cpu(). + */ + u32 dma_sync_offset; /* SGL of the buffer going to be sent as part of the work request. */ u32 num_sge; -- cgit v1.2.3 From 27f575836cfebbf872dec020428742b10650a955 Mon Sep 17 00:00:00 2001 From: Théo Lebrun Date: Thu, 2 Jul 2026 17:37:02 +0200 Subject: net: macb: drop in-flight Tx SKBs on close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MACB driver has since forever leaked the outgoing SKBs that have not yet been marked as completed. They live in queue->tx_skb which gets freed without remorse nor checking. macb_free_consistent() gets called in a few codepaths, but only close will trigger the added expressions. In macb_open() and macb_alloc_consistent() failure cases, queues' tx_skb just got allocated and are empty. Fixes: 89e5785fc8a6 ("[PATCH] Atmel MACB ethernet driver") Cc: stable@vger.kernel.org Reviewed-by: Nicolai Buchwitz Signed-off-by: Théo Lebrun Link: https://patch.msgid.link/20260702-macb-drop-tx-v4-1-1c833eebdbc8@bootlin.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/cadence/macb_main.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index fd282a1700fb..d394f1f43b68 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2668,8 +2668,25 @@ static void macb_free_consistent(struct macb *bp) dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - kfree(queue->tx_skb); - queue->tx_skb = NULL; + if (queue->tx_skb) { + unsigned int dropped = 0, tail; + + for (tail = queue->tx_tail; tail != queue->tx_head; + tail++) { + if (macb_tx_skb(queue, tail)->skb) + dropped++; + macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0); + } + + queue->stats.tx_dropped += dropped; + bp->dev->stats.tx_dropped += dropped; + + kfree(queue->tx_skb); + queue->tx_skb = NULL; + } + + queue->tx_head = 0; + queue->tx_tail = 0; queue->tx_ring = NULL; queue->rx_ring = NULL; } -- cgit v1.2.3 From 97272a5704bf13369bba43951ad9fe8f806ccc7f Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 9 Jul 2026 14:57:06 +0800 Subject: 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), -- cgit v1.2.3 From d595255241e5fec0c94adeebf2565524398e37c5 Mon Sep 17 00:00:00 2001 From: Eckhart Mohr Date: Wed, 8 Jul 2026 15:21:06 +0200 Subject: 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), -- cgit v1.2.3 From c914307e1d41c2cb7bcdcbfde4cd2f214f6aa027 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 3 Jul 2026 22:14:23 +0800 Subject: net/mlx5: Fix L3 tunnel entropy refcount leak mlx5_tun_entropy_refcount_inc() counts both VXLAN and L2-to-L3 tunnel reformat entries as entropy-enabling users. The matching decrement path only handled VXLAN, leaving L2-to-L3 tunnel entries counted after release. Handle MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL in mlx5_tun_entropy_refcount_dec() as well so the enabling entry refcount remains balanced. Fixes: f828ca6a2fb6 ("net/mlx5e: Add support for hw encapsulation of MPLS over UDP") Signed-off-by: Li RongQing Reviewed-by: Simon Horman Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260703141423.1723-1-lirongqing@baidu.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c index 4571c56ec3c9..97f6097d4c70 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c @@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy, int reformat_type) { mutex_lock(&tun_entropy->lock); - if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN) + if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN || + reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL) tun_entropy->num_enabling_entries--; else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE && --tun_entropy->num_disabling_entries == 0) -- cgit v1.2.3 From b62869a81a7ce388d1fbb0fac5fa8300ea614d81 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 6 Jul 2026 08:50:17 +0300 Subject: ethtool: rss: Fix hfunc and input_xfrm parsing on big endian ETHTOOL_A_RSS_HFUNC and ETHTOOL_A_RSS_INPUT_XFRM are NLA_U32 attributes, but ethnl_rss_set() and ethnl_rss_create_doit() parse them with ethnl_update_u8(), which reads a single byte. On little endian this happens to read the least significant byte and works as long as the value fits in a byte. On big endian it reads the most significant byte, so the requested value is parsed incorrectly. The destination fields in struct ethtool_rxfh_param are u8, so the attribute can't be read directly with ethnl_update_u32(). Cap the hfunc policy at U8_MAX so an out of range value is rejected instead of being silently truncated into the u8 field, and add ethnl_update_u8_u32() to read the full u32 and narrow it into the u8 destination. Fixes: 82ae67cbc423 ("ethtool: rss: support setting hfunc via Netlink") Fixes: d3e2c7bab124 ("ethtool: rss: support setting input-xfrm via Netlink") Fixes: a166ab7816c5 ("ethtool: rss: support creating contexts via Netlink") Reviewed-by: Dragos Tatulea Reviewed-by: Nimrod Oren Signed-off-by: Gal Pressman Link: https://patch.msgid.link/20260706055017.3355806-1-gal@nvidia.com Signed-off-by: Paolo Abeni --- net/ethtool/netlink.h | 28 ++++++++++++++++++++++++++++ net/ethtool/rss.c | 14 ++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 4ca2eca2e94b..3e969a070f9f 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -114,6 +114,34 @@ static inline void ethnl_update_u8(u8 *dst, const struct nlattr *attr, *mod = true; } +/** + * ethnl_update_u8_u32() - update u8 value from an NLA_U32 attribute + * @dst: value to update + * @attr: netlink attribute with new value or null + * @mod: pointer to bool for modification tracking + * + * Some attributes are NLA_U32 on the wire but are stored in a u8. Read the + * full 32-bit value from NLA_U32 netlink attribute @attr and narrow it into + * the u8 pointed to by @dst; do nothing if @attr is null. + * Bool pointed to by @mod is set to true if this function changed the value + * of *dst, otherwise it is left as is. + */ +static inline void ethnl_update_u8_u32(u8 *dst, const struct nlattr *attr, + bool *mod) +{ + u32 val; + + if (!attr) + return; + val = nla_get_u32(attr); + DEBUG_NET_WARN_ON_ONCE(val > U8_MAX); + if (*dst == val) + return; + + *dst = val; + *mod = true; +} + /** * ethnl_update_bool32() - update u32 used as bool from NLA_U8 attribute * @dst: value to update diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index d8adc78e3775..d4a1a4724b67 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -570,7 +570,7 @@ static const struct nla_policy ethnl_rss_flows_policy[] = { const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = { [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, }, - [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX), [ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, }, [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_INPUT_XFRM] = @@ -851,7 +851,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info) indir_mod = !!tb[ETHTOOL_A_RSS_INDIR]; rxfh.hfunc = data.hfunc; - ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); + ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); if (rxfh.hfunc == data.hfunc) rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE; @@ -860,7 +860,8 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info) goto exit_free_indir; rxfh.input_xfrm = data.input_xfrm; - ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); + ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], + &mod); xfrm_sym = rxfh.input_xfrm || data.input_xfrm; if (rxfh.input_xfrm == data.input_xfrm) rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; @@ -934,7 +935,7 @@ const struct ethnl_request_ops ethnl_rss_request_ops = { const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = { [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1), - [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX), [ETHTOOL_A_RSS_INDIR] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), [ETHTOOL_A_RSS_INPUT_XFRM] = @@ -1048,14 +1049,15 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) goto exit_clean_data; indir_user_size = ret; - ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); + ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod); if (ret) goto exit_free_indir; rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; - ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); + ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], + &mod); ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size); if (!ctx) { -- cgit v1.2.3 From fabb881df322da25442f98d23f5fa371e3c78ec4 Mon Sep 17 00:00:00 2001 From: Harman Kalra Date: Thu, 2 Jul 2026 10:26:16 +0530 Subject: octeontx2-af: fix VF bringup affecting PF promiscuous state Mbox handling of nix_set_rx_mode for a VF with promiscuous and all_multi flags set to false causes deletion of the PF's promiscuous and allmulti MCAM rules. This occurs because the APIs that enable/disable these rules operate only on the PF, even when the mbox request is made via a VF interface. Guard both rvu_npc_enable_allmulti_entry() and rvu_npc_enable_promisc_entry() disable paths with an is_vf() check so that a VF bringing up or tearing down its interface cannot inadvertently clear the PF's MCAM rules. Fixes: 967db3529eca ("octeontx2-af: add support for multicast/promisc packet replication feature") Signed-off-by: Harman Kalra Signed-off-by: Nitin Shetty J Link: https://patch.msgid.link/20260702045616.3002773-2-nshettyj@marvell.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 0297c7ab0614..6a0ce2665031 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4580,7 +4580,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, rvu_npc_install_allmulti_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base); } else { - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_allmulti_entry(rvu, pcifunc, nixlf, false); } @@ -4590,7 +4590,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, pfvf->rx_chan_base, pfvf->rx_chan_cnt); else - if (!nix_rx_multicast) + if (!nix_rx_multicast && !is_vf(pcifunc)) rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false); return 0; -- cgit v1.2.3 From 34a4dd45cf210c04fee773b0dbc350aec285f03c Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Wed, 1 Jul 2026 18:24:34 -0700 Subject: 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; } } -- cgit v1.2.3 From af80e2bfde9312c76b60cf9274248dce0410b30d Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 2 Jul 2026 14:58:05 -0700 Subject: 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 @@ retry: 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++) -- cgit v1.2.3 From 91426ce50d14a49bde53b3ad1e48393556ba92cd Mon Sep 17 00:00:00 2001 From: Anas Khan Date: Thu, 2 Jul 2026 16:58:20 +0530 Subject: 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__) \ -- cgit v1.2.3 From 14abbed336a2d1bbd726c25d148d2ec0ff928073 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 15:34:22 +0800 Subject: 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; } } -- cgit v1.2.3 From f5ef65adf81da3dbce4e692e48c1754c0bb95da0 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Tue, 30 Jun 2026 19:22:21 +0000 Subject: 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 -- cgit v1.2.3 From 78237e3c0720fcc6eb9b87e90fd70f63eeca886f Mon Sep 17 00:00:00 2001 From: Dust Li Date: Tue, 7 Jul 2026 15:43:18 +0800 Subject: dibs: loopback: validate offset and size in move_data() The loopback move_data() performs a memcpy into the registered DMB without checking whether offset + size exceeds the DMB length. Unlike real ISM hardware, which enforces memory region bounds natively, the software loopback has no such protection. A peer-supplied out-of-bounds offset or oversized write would result in an OOB write past the allocated kernel buffer. Add an explicit bounds check before the memcpy to reject such requests with -EINVAL. Fixes: f7a22071dbf3 ("net/smc: implement DMB-related operations of loopback-ism") Cc: stable@vger.kernel.org Reported-by: Federico Kirschbaum Signed-off-by: Dust Li Reported-by: Baul Lee Link: https://patch.msgid.link/20260707074318.1448662-1-dust.li@linux.alibaba.com Signed-off-by: Paolo Abeni --- drivers/dibs/dibs_loopback.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index ec3b48cb0e87..0f2e09311152 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -254,6 +254,11 @@ static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok, read_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } + if ((u64)offset + size > rmb_node->len) { + read_unlock_bh(&ldev->dmb_ht_lock); + return -EINVAL; + } + memcpy((char *)rmb_node->cpu_addr + offset, data, size); sba_idx = rmb_node->sba_idx; read_unlock_bh(&ldev->dmb_ht_lock); -- cgit v1.2.3 From f5089008f90c0a7c5520dff3934e0af00adf322d Mon Sep 17 00:00:00 2001 From: Daehyeon Ko <4ncienth@gmail.com> Date: Fri, 3 Jul 2026 17:36:33 +0900 Subject: macsec: don't read an unset MAC header in macsec_encrypt() macsec_encrypt() reads the Ethernet header via eth_hdr(skb) (skb->head + skb->mac_header) to memmove() the 12 source/destination MAC bytes forward and make room for the SecTAG. On the AF_PACKET SOCK_RAW + PACKET_QDISC_BYPASS transmit path the skb reaches the macsec ndo_start_xmit() with the MAC header unset, so eth_hdr(skb) resolves to skb->head + (u16)~0 and the read is out of bounds: a 12-byte heap over-read that is also emitted on the wire as the frame's outer source/destination MAC. KASAN reports a slab-out-of-bounds read in macsec_start_xmit() on 6.0; on current mainline a CONFIG_DEBUG_NET build flags it as an unset mac header in skb_mac_header(). On the TX path the L2 header is at skb->data, so use skb_eth_hdr(), added by commit 96cc4b69581d ("macvlan: do not assume mac_header is set in macvlan_broadcast()") for exactly this purpose. Fixes: c09440f7dcb3 ("macsec: introduce IEEE 802.1AE driver") Cc: stable@vger.kernel.org Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260703083634.2035145-1-4ncienth@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/macsec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index fb009120a924..dd89282f0179 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -646,7 +646,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, } unprotected_len = skb->len; - eth = eth_hdr(skb); + eth = skb_eth_hdr(skb); sci_present = macsec_send_sci(secy); hh = skb_push(skb, macsec_extra_len(sci_present)); memmove(hh, eth, 2 * ETH_ALEN); -- cgit v1.2.3 From a4f27ad055392fa164f5649e89a3637b033c5fcc Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 20:35:20 +0800 Subject: 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 @@ replay_again: 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; } -- cgit v1.2.3 From fc8789bb57e625e5f32ac57ca2e7d3e7b7fda225 Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Thu, 9 Jul 2026 10:57:00 +0800 Subject: 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 @@ qmf_out: SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); qmf_out_open_fail: kfree(utf16_path); - kfree(pfile_info); return rc; } -- cgit v1.2.3 From 1f551e407bb49dce0bbd34ed6a499dad69e18020 Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Thu, 9 Jul 2026 10:57:01 +0800 Subject: 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 @@ replay_again: 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, -- cgit v1.2.3 From 8fce4cf4369c766a3293a05419500cbfde72e60d Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Thu, 9 Jul 2026 10:57:02 +0800 Subject: 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; -- cgit v1.2.3 From 9dd1964ac59d293c3684e71b4bbcd10e28f04bb4 Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Thu, 9 Jul 2026 10:57:03 +0800 Subject: 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 @@ replay_again: 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 */ } -- cgit v1.2.3 From 179e3ee9856d2b5f0ccf2f24e2334bbe76efefa9 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 6 Jul 2026 20:29:36 +0200 Subject: 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 @@ retry_iget5_locked: 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 @@ ffirst_retry: 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)) -- cgit v1.2.3 From 56bc6384314fb9ae98975fb2af8b143097ede3dc Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Thu, 9 Jul 2026 18:40:50 +0530 Subject: 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 | 63 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 19 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; + if (!err && IS_ALIGNED(rhs_offset, min_block_size)) + return 0; + 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; + } - 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); + if (size - filled > rhs_offset) + goto next; - /* 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); + 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; - } else if (err != -ENOSPC) { + 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); } } -- cgit v1.2.3 From bf1deecccf210d1dd84e85cd4a45070888583984 Mon Sep 17 00:00:00 2001 From: Steve Dunnagan Date: Wed, 1 Jul 2026 15:59:20 -0400 Subject: 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 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 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; } + step = clkd->r.rates[RATE_STEP]; + if (!step) + return -EINVAL; + ftmp = *rate - fmin; - ftmp += clkd->r.rates[RATE_STEP] - 1; /* to round up */ - ftmp = div64_ul(ftmp, clkd->r.rates[RATE_STEP]); + ftmp = DIV64_U64_ROUND_UP(ftmp, step); - *rate = ftmp * clkd->r.rates[RATE_STEP] + fmin; + *rate = ftmp * step + fmin; return 0; } -- cgit v1.2.3 From a4447c0693830d5ecadd6e755cb7fdc55d86aacc Mon Sep 17 00:00:00 2001 From: Pushpendra Singh Date: Wed, 8 Jul 2026 12:53:39 +0530 Subject: 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; } -- cgit v1.2.3 From c804aadc4ce3dcb34d6f2ecc9a159c148210805a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 3 Jul 2026 17:23:38 +0100 Subject: 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, }; -- cgit v1.2.3 From cf385cf6e713eba0720651174dac0b2d2f5bb8f8 Mon Sep 17 00:00:00 2001 From: Luigi Santivetti Date: Tue, 7 Jul 2026 16:17:16 +0100 Subject: 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; } -- cgit v1.2.3 From 3e3aa6da87d30a0064a17b836685cd43c90a3572 Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Thu, 9 Jul 2026 09:54:04 -0400 Subject: 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); -- cgit v1.2.3 From 0e120ee0822b7cc650bd7b29682a34e137cec10d Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 9 Jun 2026 09:25:28 +0000 Subject: wifi: ath11k: fix refcount leak in ath11k_ahb_fw_resources_init() of_get_child_by_name() returns a node pointer with refcount incremented, but the error path when ath11k_ahb_setup_msa_resources() fails does not release it. Add the missing of_node_put() to avoid leaking the reference. Cc: stable@vger.kernel.org Fixes: 095cb947490c ("wifi: ath11k: allow missing memory-regions") Signed-off-by: Wentao Liang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260609092528.220547-1-vulab@iscas.ac.cn Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/ahb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index f566d699d074..1e1dea485760 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -996,6 +996,7 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab) ret = ath11k_ahb_setup_msa_resources(ab); if (ret) { ath11k_err(ab, "failed to setup msa resources\n"); + of_node_put(node); return ret; } -- cgit v1.2.3 From 7393878255e492515858f751ba4c260f248fb108 Mon Sep 17 00:00:00 2001 From: Manikanta Pubbisetty Date: Tue, 23 Jun 2026 12:13:55 +0530 Subject: wifi: ath10k: fix skb leak on incomplete msdu during rx pop When ath10k_htt_rx_pop_paddr32_list() or ath10k_htt_rx_pop_paddr64_list() encounters an incomplete frame (RX_ATTENTION_FLAGS_MSDU_DONE not set), it returns -EIO without purging the skb list built up so far, leaking any skbs already queued in the list. Other early-exit paths within these same functions already call __skb_queue_purge() before returning an error. Add it before the -EIO return as well to be consistent and prevent the leak. Tested-on: WCN3990 hw1.0 WLAN.HL.3.2.2.c10-00754-QCAHLSWMTPL-1 Fixes: c545070e404b ("ath10k: implement rx reorder support") Fixes: 3b0b55b19d1d ("ath10k: Add support for 64 bit HTT in-order indication msg") Signed-off-by: Manikanta Pubbisetty Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260623064355.1876743-1-manikanta.pubbisetty@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath10k/htt_rx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index faac359aa9ac..b3f1b7186721 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -706,6 +706,7 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt, if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); + __skb_queue_purge(list); return -EIO; } } @@ -770,6 +771,7 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt, if (!(__le32_to_cpu(rxd_attention->flags) & RX_ATTENTION_FLAGS_MSDU_DONE)) { ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); + __skb_queue_purge(list); return -EIO; } } -- cgit v1.2.3 From 7f11e70629650ff6ea140984e5ce188b775b2683 Mon Sep 17 00:00:00 2001 From: Dmitry Morgun Date: Sat, 30 May 2026 11:42:52 +0000 Subject: wifi: ath11k: fix potential buffer underflow in ath11k_hal_rx_msdu_list_get() When the first entry in msdu_details has a zero buffer address, the code accesses msdu_details[i - 1] with i == 0, causing a buffer underflow. Fix similarly to ath12k_wifi7_hal_rx_msdu_list_get() by adding a separate check for i == 0 before the main condition to prevent the out-of-bounds access. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Dmitry Morgun Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260530114252.42615-1-d.morgun@ispras.ru Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 9e90d8e3f155..8e2abc7b8383 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4618,6 +4618,9 @@ static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar, msdu_details = &msdu_link->msdu_link[0]; for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) { + if (!i && FIELD_GET(BUFFER_ADDR_INFO0_ADDR, + msdu_details[i].buf_addr_info.info0) == 0) + break; if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR, msdu_details[i].buf_addr_info.info0) == 0) { msdu_desc_info = &msdu_details[i - 1].rx_msdu_info; -- cgit v1.2.3 From 0fe8010fc5b147607fc19ba010ba469afc95f35f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 23 Jun 2026 16:16:48 +0200 Subject: wifi: ath11k: Flush the posted write after writing to PCIE_SOC_GLOBAL_RESET ath11k_pci_soc_global_reset() tries to reset the device by writing to the PCIE_SOC_GLOBAL_RESET register. But it doesn't do a read-back to ensure that the write gets flushed to the device before the delay. This may lead to the delay on the host to be insufficient, if the posted write doesn't reach the device before the delay. So add a read-back after writing to the PCIE_SOC_GLOBAL_RESET register and before the delay. Compile tested only. Fixes: f3c603d412b3 ("ath11k: reset MHI during power down and power up") Reported-by: Alex Williamson Closes: https://lore.kernel.org/linux-pci/20260622160822.09350246@shazbot.org Signed-off-by: Manivannan Sadhasivam Reviewed-by: Baochen Qiang Reviewed-by: Raj Kumar Bhagat Link: https://patch.msgid.link/20260623141649.41087-1-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 35bb9e7a63a2..a163168f3617 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -199,6 +199,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -208,6 +210,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); -- cgit v1.2.3 From 55f3aa06951cac78b0206bde961c8cf11929a27a Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 23 Jun 2026 16:16:49 +0200 Subject: wifi: ath12k: Flush the posted write after writing to PCIE_SOC_GLOBAL_RESET ath12k_pci_soc_global_reset() tries to reset the device by writing to the PCIE_SOC_GLOBAL_RESET register. But it doesn't do a read-back to ensure that the write gets flushed to the device before the delay. This may lead to the delay on the host to be insufficient, if the posted write doesn't reach the device before the delay. So add a read-back after writing to the PCIE_SOC_GLOBAL_RESET register and before the delay. Compile tested only. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Reported-by: Alex Williamson Closes: https://lore.kernel.org/linux-pci/20260622160822.09350246@shazbot.org Signed-off-by: Manivannan Sadhasivam Reviewed-by: Baochen Qiang Reviewed-by: Raj Kumar Bhagat Tested-by: Raj Kumar Bhagat Link: https://patch.msgid.link/20260623141649.41087-2-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index d9a22d6afbb0..fee4129ea405 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -188,6 +188,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val |= PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); /* TODO: exact time to sleep is uncertain */ delay = 10; @@ -197,6 +199,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab) val &= ~PCIE_SOC_GLOBAL_RESET_V; ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + /* Flush the posted write to the device */ + ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); mdelay(delay); -- cgit v1.2.3 From 8008d6b59a659982b9f10916bb8712bf93bc7c46 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Jul 2026 22:26:51 -0400 Subject: lib/crypto: docs: Fix some sentence fragments Currently, the section about the library API for each algorithm begins with a noun phrase that was intended to serve as an elaboration on the title. It's better to use complete sentences. Suggested-by: Thomas Huth Reviewed-by: Thomas Huth Link: https://patch.msgid.link/20260709022651.44216-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/crypto/libcrypto-blockcipher.rst | 6 ++-- Documentation/crypto/libcrypto-hash.rst | 38 ++++++++++++++------------ Documentation/crypto/libcrypto-signature.rst | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Documentation/crypto/libcrypto-blockcipher.rst b/Documentation/crypto/libcrypto-blockcipher.rst index dd5ce2f8b515..fd85e27fab8d 100644 --- a/Documentation/crypto/libcrypto-blockcipher.rst +++ b/Documentation/crypto/libcrypto-blockcipher.rst @@ -6,14 +6,14 @@ Block ciphers AES --- -Support for the AES block cipher. +This API provides support for the AES block cipher. .. kernel-doc:: include/crypto/aes.h DES --- -Support for the DES block cipher. This algorithm is obsolete and is supported -only for backwards compatibility. +This API provides support for the DES block cipher. This algorithm is obsolete +and is supported only for backwards compatibility. .. kernel-doc:: include/crypto/des.h diff --git a/Documentation/crypto/libcrypto-hash.rst b/Documentation/crypto/libcrypto-hash.rst index 4248e6fdc952..fa4c54236af6 100644 --- a/Documentation/crypto/libcrypto-hash.rst +++ b/Documentation/crypto/libcrypto-hash.rst @@ -6,81 +6,83 @@ Hash functions, MACs, and XOFs AES-CMAC and AES-XCBC-MAC ------------------------- -Support for the AES-CMAC and AES-XCBC-MAC message authentication codes. +This API provides support for the AES-CMAC and AES-XCBC-MAC message +authentication codes. .. kernel-doc:: include/crypto/aes-cbc-macs.h BLAKE2b ------- -Support for the BLAKE2b cryptographic hash function. +This API provides support for the BLAKE2b cryptographic hash function. .. kernel-doc:: include/crypto/blake2b.h BLAKE2s ------- -Support for the BLAKE2s cryptographic hash function. +This API provides support for the BLAKE2s cryptographic hash function. .. kernel-doc:: include/crypto/blake2s.h GHASH and POLYVAL ----------------- -Support for the GHASH and POLYVAL universal hash functions. These algorithms -are used only as internal components of other algorithms. +This API provides support for the GHASH and POLYVAL universal hash functions. +These algorithms are used only as internal components of other algorithms. .. kernel-doc:: include/crypto/gf128hash.h MD5 --- -Support for the MD5 cryptographic hash function and HMAC-MD5. This algorithm is -obsolete and is supported only for backwards compatibility. +This API provides support for the MD5 cryptographic hash function and HMAC-MD5. +This algorithm is obsolete and is supported only for backwards compatibility. .. kernel-doc:: include/crypto/md5.h NH -- -Support for the NH universal hash function. This algorithm is used only as an -internal component of other algorithms. +This API provides support for the NH universal hash function. This algorithm is +used only as an internal component of other algorithms. .. kernel-doc:: include/crypto/nh.h Poly1305 -------- -Support for the Poly1305 universal hash function. This algorithm is used only -as an internal component of other algorithms. +This API provides support for the Poly1305 universal hash function. This +algorithm is used only as an internal component of other algorithms. .. kernel-doc:: include/crypto/poly1305.h SHA-1 ----- -Support for the SHA-1 cryptographic hash function and HMAC-SHA1. This algorithm -is obsolete and is supported only for backwards compatibility. +This API provides support for the SHA-1 cryptographic hash function and +HMAC-SHA1. This algorithm is obsolete and is supported only for backwards +compatibility. .. kernel-doc:: include/crypto/sha1.h SHA-2 ----- -Support for the SHA-2 family of cryptographic hash functions, including SHA-224, -SHA-256, SHA-384, and SHA-512. This also includes their corresponding HMACs: -HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512. +This API provides support for the SHA-2 family of cryptographic hash functions, +including SHA-224, SHA-256, SHA-384, and SHA-512. This also includes their +corresponding HMACs: HMAC-SHA224, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512. .. kernel-doc:: include/crypto/sha2.h SHA-3 ----- -The SHA-3 functions are documented in :ref:`sha3`. +The SHA-3 API is documented in :ref:`sha3`. SM3 --- -Support for the SM3 cryptographic hash function. +This API provides support for the SM3 cryptographic hash function. .. kernel-doc:: include/crypto/sm3.h diff --git a/Documentation/crypto/libcrypto-signature.rst b/Documentation/crypto/libcrypto-signature.rst index e80d59fa51b6..2a6dc793f0de 100644 --- a/Documentation/crypto/libcrypto-signature.rst +++ b/Documentation/crypto/libcrypto-signature.rst @@ -6,6 +6,6 @@ Digital signature algorithms ML-DSA ------ -Support for the ML-DSA digital signature algorithm. +This API provides support for the ML-DSA digital signature algorithm. .. kernel-doc:: include/crypto/mldsa.h -- cgit v1.2.3 From 9665e22579ee4324a14e035b5b29d3d1fdc929e2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Jul 2026 22:27:47 -0400 Subject: lib/crypto: docs: Improve introduction sentence Make it clear that lib/crypto/ is a kernel-internal library. It's easy for people to come across this page, especially the HTML version online, without that context. Reviewed-by: Thomas Huth Link: https://patch.msgid.link/20260709022747.44635-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- Documentation/crypto/libcrypto.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/crypto/libcrypto.rst b/Documentation/crypto/libcrypto.rst index a1557d45b0e5..0733e603d229 100644 --- a/Documentation/crypto/libcrypto.rst +++ b/Documentation/crypto/libcrypto.rst @@ -4,8 +4,9 @@ Crypto library ============== -``lib/crypto/`` provides faster and easier access to cryptographic algorithms -than the traditional crypto API. +The Linux kernel's crypto library (``lib/crypto/``) provides kernel-internal +users of cryptographic algorithms with faster and easier access to those +algorithms than the traditional kernel crypto API. Each cryptographic algorithm is supported via a set of dedicated functions. "Crypto agility", where needed, is left to calling code. -- cgit v1.2.3 From e073f1238ecaea366f53e98724c40b31856da56a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Jul 2026 22:29:54 -0400 Subject: crypto: aes - Fix conditions for selecting MAC dependencies Starting in commit 7137cbf2b5c9 ("crypto: aes - Add cmac, xcbc, and cbcmac algorithms using library"), the aes module (CRYPTO_AES) supports CBC based MACs using the corresponding library functions. To avoid including unneeded functionality, that support honors the existing CRYPTO_CMAC, CRYPTO_XCBC, and CRYPTO_CCM kconfig options. The dependencies are selected if at least one of those is enabled. However, the select statements don't correctly handle the case where CRYPTO_AES=y and (for example) CRYPTO_CMAC=m. In that case the dependencies get selected at level 'm', due to how the kconfig language works. That causes a linker error. Fix this by changing the selection conditions to use '!= n'. A similar issue also exists for CRYPTO_LIB_AES's conditional selection of CRYPTO_LIB_UTILS. The same '!= n' would work, but instead just make CRYPTO_LIB_AES always select CRYPTO_LIB_UTILS. CRYPTO_LIB_UTILS is lightweight, and it's needed by most AES modes and many other things. Fixes: 7137cbf2b5c9 ("crypto: aes - Add cmac, xcbc, and cbcmac algorithms using library") Fixes: 309a7e514da7 ("lib/crypto: aes: Add support for CBC-based MACs") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260709022954.45113-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- crypto/Kconfig | 4 ++-- lib/crypto/Kconfig | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index f1e372195273..b61401bd3ef6 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -358,8 +358,8 @@ config CRYPTO_AES tristate "AES (Advanced Encryption Standard)" select CRYPTO_ALGAPI select CRYPTO_LIB_AES - select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC || CRYPTO_XCBC || CRYPTO_CCM - select CRYPTO_HASH if CRYPTO_CMAC || CRYPTO_XCBC || CRYPTO_CCM + select CRYPTO_LIB_AES_CBC_MACS if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n + select CRYPTO_HASH if CRYPTO_CMAC != n || CRYPTO_XCBC != n || CRYPTO_CCM != n help AES cipher algorithms (Rijndael)(FIPS-197, ISO/IEC 18033-3) diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index 591c1c2a7fb3..83d4c95e079e 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -8,8 +8,7 @@ config CRYPTO_LIB_UTILS config CRYPTO_LIB_AES tristate - # Select dependencies of modes that are part of libaes. - select CRYPTO_LIB_UTILS if CRYPTO_LIB_AES_CBC_MACS + select CRYPTO_LIB_UTILS config CRYPTO_LIB_AES_ARCH bool -- cgit v1.2.3 From f86a7c96406d406a66f3d7653eca86aa576fcf6b Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 9 Jul 2026 13:12:24 +0100 Subject: 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 -- cgit v1.2.3 From 8ca0016a9b799d8366b32da7e729fcf56627516f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 9 Jul 2026 13:22:11 +0100 Subject: 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* -- cgit v1.2.3 From 7cbd0c4cebe4c9f678d15e6b9ba975e1155a107f Mon Sep 17 00:00:00 2001 From: Matt Bobrowski Date: Thu, 9 Jul 2026 02:53:16 +0000 Subject: bpf: Fix UAF in sock clone early bailouts Similar to recent commit 9b51a6155d14 ("bpf,fork: wipe ->bpf_storage before bailouts that access it"), sk_clone() performs an initial shallow copy of the socket field ->sk_bpf_storage via sock_copy() for the cloned socket newsk. If sk_clone() bails out early (e.g. if sk_filter_charge() fails) prior to calling bpf_sk_storage_clone(), newsk->sk_bpf_storage still points to the parent socket's BPF local storage. When newsk is subsequently freed via sk_free(), the deallocation path (__sk_destruct() -> bpf_sk_storage_free()) destroys the parent socket's BPF local storage, leading to a use-after-free (UAF) on the parent socket. Fix this by resetting newsk->sk_bpf_storage to NULL immediately after sock_copy() in sk_clone(), and remove the now redundant initialization from bpf_sk_storage_clone(). Fixes: 6ac99e8f23d4 ("bpf: Introduce bpf sk local storage") Fixes: f12dd75959b0 ("bpf: net: Set sk_bpf_storage back to NULL for cloned sk") Signed-off-by: Matt Bobrowski Signed-off-by: Daniel Borkmann Reviewed-by: Kuniyuki Iwashima Acked-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20260709025316.999913-1-mattbobrowski@google.com --- net/core/bpf_sk_storage.c | 2 -- net/core/sock.c | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index ecd659f79fd4..1d295a8769fa 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -158,8 +158,6 @@ int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk) struct bpf_local_storage_elem *selem; int ret = 0; - RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL); - rcu_read_lock_dont_migrate(); sk_storage = rcu_dereference(sk->sk_bpf_storage); diff --git a/net/core/sock.c b/net/core/sock.c index 8a59bfaa8096..498a57f34f5b 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2492,6 +2492,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, sock_copy(newsk, sk); newsk->sk_prot_creator = prot; +#ifdef CONFIG_BPF_SYSCALL + RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL); +#endif /* SANITY */ if (likely(newsk->sk_net_refcnt)) { -- cgit v1.2.3 From e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 Mon Sep 17 00:00:00 2001 From: Norbert Manthey Date: Thu, 9 Jul 2026 15:54:39 +0000 Subject: 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) { -- cgit v1.2.3 From 648790e0952789527ec68548edbedbc0fcff43b5 Mon Sep 17 00:00:00 2001 From: Woraphat Khiaodaeng Date: Thu, 9 Jul 2026 10:51:00 +0700 Subject: 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. -- cgit v1.2.3 From f3176c8ac4217c88fe1147ab084c47092921ffc4 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 9 Jul 2026 11:43:07 -0600 Subject: 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 | 6 +++--- 1 file changed, 3 insertions(+), 3 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) { + lockdep_assert_in_rcu_read_lock(); + if (ctx->flags & IORING_SETUP_TASKRUN_FLAG) { - struct io_rings *rings; + 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); } } -- cgit v1.2.3 From 27d80e0f8b8dff97503fc0061754b1d3800cb961 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Jul 2026 19:19:55 +0200 Subject: 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) -- cgit v1.2.3 From d2d5c129d07ea8eb91cd8a8633b5774116c4d171 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Jul 2026 19:25:39 +0200 Subject: 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; /* -- cgit v1.2.3 From db4e9defd2e8620abee04cfe5809c0bcd6ecf06a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 9 Jul 2026 11:08:13 -1000 Subject: 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 @@ err_unlock_and_disable: 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; } -- cgit v1.2.3 From 49b3378a750cf85112e656d003145d4b5d0da232 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 9 Jul 2026 11:08:22 -1000 Subject: 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: -- cgit v1.2.3 From e6979d05c6a6fe79980f08d63f039f0b27c30a1c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 9 Jul 2026 11:08:41 -1000 Subject: 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; -- cgit v1.2.3 From 4688cf884b3abcd12498e03b625d1916bf49a1e4 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 8 Jul 2026 19:49:26 +0900 Subject: rust: allow `clippy::unwrap_or_default` globally Starting with rustc 1.88, the `clippy::unwrap_or_default` lint triggers on `rust/kernel/soc.rs` if `CONFIG_CC_OPTIMIZE_FOR_SIZE=y`: warning: use of `unwrap_or` to construct default value --> ../rust/kernel/soc.rs:66:10 | 66 | .unwrap_or(core::ptr::null()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` This is a clippy bug [1]: the lint decides whether an expression is equivalent to `Default::default()` by inspecting the optimized MIR of `<*const T as Default>::default` exported by `core`, so its outcome depends on the optimization level `core` was built with. Moreover, its suggestion ignores our MSRV of 1.85 (`Default` for `*const T` is only stable since Rust 1.88), so we could not apply it anyway. Disable the lint globally rather than working around this single occurrence; it can be re-enabled conditionally using `rustc-min-version` once clippy is fixed. Link: https://github.com/rust-lang/rust-clippy/issues/17379 [1] Suggested-by: Miguel Ojeda Signed-off-by: Alexandre Courbot Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Link: https://patch.msgid.link/20260708-soc_unwrap_or-v2-1-007ed724cc7b@nvidia.com [ Moved to non-versioned group. - Miguel ] Signed-off-by: Miguel Ojeda --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index b9c5792c79e0..e54cc46340fd 100644 --- a/Makefile +++ b/Makefile @@ -475,6 +475,10 @@ KBUILD_USERLDFLAGS := $(USERLDFLAGS) # These flags apply to all Rust code in the tree, including the kernel and # host programs. +# +# `-Aclippy::unwrap_or_default`: the lint is buggy [1] and ignores our +# MSRV. It can trigger depending on the optimization level. +# [1] https://github.com/rust-lang/rust-clippy/issues/17379 export rust_common_flags := --edition=2021 \ -Zbinary_dep_depinfo=y \ -Astable_features \ @@ -503,6 +507,7 @@ export rust_common_flags := --edition=2021 \ -Aclippy::uninlined_format_args \ -Wclippy::unnecessary_safety_comment \ -Wclippy::unnecessary_safety_doc \ + -Aclippy::unwrap_or_default \ -Wrustdoc::missing_crate_level_docs \ -Wrustdoc::unescaped_backticks -- cgit v1.2.3 From 5d72720365f973e2c21fa99505b627521de66c25 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 9 Jul 2026 12:08:53 +0200 Subject: 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. */ -- cgit v1.2.3 From 3c58f641e813c3c71039f8fd4d4e2a3aab713288 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 18 Jun 2026 12:50:17 +0300 Subject: userfaultfd: prevent registration of special VMAs Vova Tokarev says: userfaultfd allows registration on shadow stack VMAs. With userfaultfd access, you can register on the shadow stack, discard a page ... and inject a page with chosen return addresses via UFFDIO_COPY. Update vma_can_userfault() to reject VM_SHADOW_STACK. While on it, also reject VM_SPECIAL so that if a driver would implement vm_uffd_ops, it wouldn't be possible to register special VMAs with userfaultfd. Since VM_SPECIAL includes VM_DONTEXPAND which is set but hugetlb, exclude hugetlb VMAs from the check for VM_SPECIAL. Link: https://lore.kernel.org/20260618095017.2553004-1-rppt@kernel.org Fixes: 54007f818206 ("mm: Introduce VM_SHADOW_STACK for shadow stack memory") Signed-off-by: Mike Rapoport (Microsoft) Reported-by: vova tokarev Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Cc: Al Viro Cc: Christian Brauner Cc: Jan Kara Cc: Linus Torvalds Cc: Mike Rapoport Cc: Oleg Nesterov Cc: Peter Xu Cc: Signed-off-by: Andrew Morton --- mm/userfaultfd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 246af12bf801..c3adedaaf7d5 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -2111,7 +2111,10 @@ static bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, { const struct vm_uffd_ops *ops = vma_uffd_ops(vma); - if (vma->vm_flags & VM_DROPPABLE) + if (vma->vm_flags & (VM_DROPPABLE | VM_SHADOW_STACK)) + return false; + + if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL)) return false; vm_flags &= __VM_UFFD_FLAGS; -- cgit v1.2.3 From 14afcf67dc2c3d2fce9f0b987a8fd4187777a841 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Mon, 29 Jun 2026 18:38:18 -0700 Subject: MAINTAINERS: s/SeongJae/SJ/ My legal and preferred first names are SeongJae and SJ, respectively. I was using the legal name for commits and tags, while using the preferred name for conversations. It sometimes confuses people including myself. Consistently use the preferred name. Together remove copyright notes on files. Those are only confusing for people who are not familiar with the law. Meanwhile, we can infer the information in a better way from git logs and public information. Link: https://lore.kernel.org/20260630013820.143366-1-sj@kernel.org Signed-off-by: SJ Park Acked-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- .mailmap | 1 + Documentation/ABI/testing/sysfs-kernel-mm-damon | 174 ++++++++++++------------ MAINTAINERS | 2 +- include/linux/damon.h | 2 - mm/damon/core.c | 2 - mm/damon/lru_sort.c | 2 - mm/damon/modules-common.c | 2 - mm/damon/modules-common.h | 2 - mm/damon/ops-common.c | 2 - mm/damon/ops-common.h | 2 - mm/damon/paddr.c | 2 - mm/damon/reclaim.c | 2 - mm/damon/sysfs-common.c | 2 - mm/damon/sysfs-common.h | 2 - mm/damon/sysfs-schemes.c | 2 - mm/damon/sysfs.c | 2 - mm/damon/tests/core-kunit.h | 4 - mm/damon/tests/sysfs-kunit.h | 2 - mm/damon/tests/vaddr-kunit.h | 4 - mm/damon/vaddr.c | 2 - 20 files changed, 89 insertions(+), 126 deletions(-) diff --git a/.mailmap b/.mailmap index 12f3acdebd72..5f7654b9f808 100644 --- a/.mailmap +++ b/.mailmap @@ -816,6 +816,7 @@ Simon Wunderlich Simon Wunderlich Simon Wunderlich Simon Wunderlich +SJ Park Sricharan Ramabadhran Srinivas Kandagatla Srinivas Kandagatla diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon index b73e6bc28ea5..4fdec63a47d4 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon @@ -1,26 +1,26 @@ what: /sys/kernel/mm/damon/ Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Interface for Data Access MONitoring (DAMON). Contains files for controlling DAMON. For more details on DAMON itself, please refer to Documentation/admin-guide/mm/damon/index.rst. What: /sys/kernel/mm/damon/admin/ Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Interface for privileged users of DAMON. Contains files for controlling DAMON that aimed to be used by privileged users. What: /sys/kernel/mm/damon/admin/kdamonds/nr_kdamonds Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number 'N' to this file creates the number of directories for controlling each DAMON worker thread (kdamond) named '0' to 'N-1' under the kdamonds/ directory. What: /sys/kernel/mm/damon/admin/kdamonds//state Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing 'on' or 'off' to this file makes the kdamond starts or stops, respectively. Reading the file returns the keywords based on the current status. Writing 'commit' to this file @@ -40,33 +40,33 @@ Description: Writing 'on' or 'off' to this file makes the kdamond starts or What: /sys/kernel/mm/damon/admin/kdamonds//pid Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the pid of the kdamond if it is running. What: /sys/kernel/mm/damon/admin/kdamonds//refresh_ms Date: Jul 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the time interval for automatic DAMON status file contents update. Writing '0' disables the update. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts/nr_contexts Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number 'N' to this file creates the number of directories for controlling each DAMON context named '0' to 'N-1' under the contexts/ directory. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//avail_operations Date: Apr 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the available monitoring operations sets on the currently running kernel. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//operations Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a keyword for a monitoring operations set ('vaddr' for virtual address spaces monitoring, 'fvaddr' for fixed virtual address ranges monitoring, and 'paddr' for the physical address @@ -79,42 +79,42 @@ Description: Writing a keyword for a monitoring operations set ('vaddr' for What: /sys/kernel/mm/damon/admin/kdamonds//contexts//addr_unit Date: Aug 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing an integer to this file sets the 'address unit' parameter of the given operations set of the context. Reading the file returns the last-written 'address unit' value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//pause Date: Mar 2026 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a boolean keyword to this file sets the 'pause' request parameter for the context. Reading the file returns the last-written 'pause' value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/sample_us Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the sampling interval of the DAMON context in microseconds as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/aggr_us Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the aggregation interval of the DAMON context in microseconds as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/update_us Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the update interval of the DAMON context in microseconds as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intrvals_goal/access_bp Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the monitoring intervals auto-tuning target DAMON-observed access events ratio within the given time interval (aggrs in same directory), in bp @@ -122,7 +122,7 @@ Description: Writing a value to this file sets the monitoring intervals What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intrvals_goal/aggrs Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the time interval to achieve the monitoring intervals auto-tuning target DAMON-observed access events ratio (access_bp in same directory) within. @@ -130,14 +130,14 @@ Description: Writing a value to this file sets the time interval to achieve What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intrvals_goal/min_sample_us Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the minimum value of auto-tuned sampling interval in microseconds. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intrvals_goal/max_sample_us Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the maximum value of auto-tuned sampling interval in microseconds. Reading this file returns the value. @@ -145,42 +145,42 @@ Description: Writing a value to this file sets the maximum value of What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/nr_regions/min WDate: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the minimum number of monitoring regions of the DAMON context as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/nr_regions/max Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the maximum number of monitoring regions of the DAMON context as the value. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//targets/nr_targets Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number 'N' to this file creates the number of directories for controlling each DAMON target of the context named '0' to 'N-1' under the contexts/ directory. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//targets//pid_target Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the pid of the target process if the context is for virtual address spaces monitoring, respectively. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//targets//obsolete_target Date: Oct 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the obsoleteness of the matching parameters commit destination target. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//targets//regions/nr_regions Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number 'N' to this file creates the number of directories for setting each DAMON target memory region of the context named '0' to 'N-1' under the regions/ directory. In @@ -190,181 +190,181 @@ Description: Writing a number 'N' to this file creates the number of What: /sys/kernel/mm/damon/admin/kdamonds//contexts//targets//regions//start Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the start address of the monitoring region. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//targets//regions//end Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the end address of the monitoring region. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes/nr_schemes Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number 'N' to this file creates the number of directories for controlling each DAMON-based operation scheme of the context named '0' to 'N-1' under the schemes/ directory. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//action Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the action of the scheme. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//target_nid Date: Jun 2024 -Contact: SeongJae Park +Contact: SJ Park Description: Action's target NUMA node id. Supported by only relevant actions. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//apply_interval_us Date: Sep 2023 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a value to this file sets the action apply interval of the scheme in microseconds. Reading this file returns the value. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//access_pattern/sz/min Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the minimum size of the scheme's target regions in bytes. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//access_pattern/sz/max Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the maximum size of the scheme's target regions in bytes. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//access_pattern/nr_accesses/min Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the manimum 'nr_accesses' of the scheme's target regions. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//access_pattern/nr_accesses/max Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the maximum 'nr_accesses' of the scheme's target regions. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//access_pattern/age/min Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the minimum 'age' of the scheme's target regions. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//access_pattern/age/max Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the maximum 'age' of the scheme's target regions. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/ms Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the time quota of the scheme in milliseconds. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/bytes Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the size quota of the scheme in bytes. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/effective_bytes Date: Feb 2024 -Contact: SeongJae Park +Contact: SJ Park Description: Reading from this file gets the effective size quota of the scheme in bytes, which adjusted for the time quota and goals. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/reset_interval_ms Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the quotas charge reset interval of the scheme in milliseconds. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/goals/nr_goals Date: Nov 2023 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number 'N' to this file creates the number of directories for setting automatic tuning of the scheme's aggressiveness named '0' to 'N-1' under the goals/ directory. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/goals//target_metric Date: Feb 2024 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the quota auto-tuning goal metric. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/goals//target_value Date: Nov 2023 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the target value of the goal metric. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/goals//current_value Date: Nov 2023 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the current value of the goal metric. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/goals//nid Date: Apr 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the nid parameter of the goal. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/goals//path Date: Oct 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the path parameter of the goal. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/goal_tuner Date: Mar 2026 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the goal-based effective quota auto-tuning algorithm to use. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/fail_charge_num Date: Mar 2026 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the action-failed memory quota charging ratio numerator. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/fail_charge_denom Date: Mar 2026 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the action-failed memory quota charging ratio denominator. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/weights/sz_permil Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the under-quota limit regions prioritization weight for 'size' in permil. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/weights/nr_accesses_permil Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the under-quota limit regions prioritization weight for 'nr_accesses' in permil. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//quotas/weights/age_permil Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the under-quota limit regions prioritization weight for 'age' in permil. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//watermarks/metric Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the metric of the watermarks for the scheme. The writable/readable keywords for this file are 'none' for disabling the watermarks @@ -373,44 +373,44 @@ Description: Writing to and reading from this file sets and gets the metric What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//watermarks/interval_us Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the metric check interval of the watermarks for the scheme in microseconds. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//watermarks/high Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the high watermark of the scheme in permil. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//watermarks/mid Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the mid watermark of the scheme in permil. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//watermarks/low Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the low watermark of the scheme in permil. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Directory for DAMON core layer-handled DAMOS filters. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters/nr_filters Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number 'N' to this file creates the number of directories for setting filters of the scheme named '0' to 'N-1' under the core_filters/ directory. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//type Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the type of the memory of the interest. 'anon' for anonymous pages, 'memcg' for specific memory cgroup, 'young' for young pages, @@ -419,62 +419,62 @@ Description: Writing to and reading from this file sets and gets the type of What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//memcg_path Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: If 'memcg' is written to the 'type' file, writing to and reading from this file sets and gets the path to the memory cgroup of the interest. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//addr_start Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: If 'addr' is written to the 'type' file, writing to or reading from this file sets or gets the start address of the address range for the filter. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//addr_end Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: If 'addr' is written to the 'type' file, writing to or reading from this file sets or gets the end address of the address range for the filter. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//min Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: If 'hugepage_size' is written to the 'type' file, writing to or reading from this file sets or gets the minimum size of the hugepage for the filter. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//max Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: If 'hugepage_size' is written to the 'type' file, writing to or reading from this file sets or gets the maximum size of the hugepage for the filter. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//damon_target_idx Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: If 'target' is written to the 'type' file, writing to or reading from this file sets or gets the index of the DAMON monitoring target of the interest. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//matching Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing 'Y' or 'N' to this file sets whether the filter is for the memory of the 'type', or all except the 'type'. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters//allow Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing 'Y' or 'N' to this file sets whether to allow or reject applying the scheme's action to the memory that satisfies the 'type' and the 'matching' of the directory. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//ops_filters Date: Feb 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Directory for DAMON operations set layer-handled DAMOS filters. Files under this directory works same to those of /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//core_filters @@ -482,7 +482,7 @@ Description: Directory for DAMON operations set layer-handled DAMOS filters. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//filters Date: Dec 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Directory for DAMOS filters. Files under this directory works same to those of /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//{core,ops}_filters @@ -491,14 +491,14 @@ Description: Directory for DAMOS filters. Files under this directory works What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//dests/nr_dests Date: Jul 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number 'N' to this file creates the number of directories for setting action destinations of the scheme named '0' to 'N-1' under the dests/ directory. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//dests//id Date: Jul 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the id of the DAMOS action destination. For DAMOS_MIGRATE_{HOT,COLD} actions, the destination node's node id can be written and @@ -506,98 +506,98 @@ Description: Writing to and reading from this file sets and gets the id of What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//dests//weight Date: Jul 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing to and reading from this file sets and gets the weight of the DAMOS action destination to select as the destination of each action among the destinations. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/nr_tried Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the number of regions that the action of the scheme has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/sz_tried Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the total size of regions that the action of the scheme has tried to be applied in bytes. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/nr_applied Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the number of regions that the action of the scheme has successfully applied. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/sz_applied Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the total size of regions that the action of the scheme has successfully applied in bytes. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/sz_ops_filter_passed Date: Dec 2024 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the total size of memory that passed DAMON operations layer-handled filters of the scheme in bytes. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/qt_exceeds Date: Mar 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the number of the exceed events of the scheme's quotas. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/nr_snapshots Date: Dec 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the total number of DAMON snapshots that the scheme has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//stats/max_nr_snapshots Date: Dec 2025 -Contact: SeongJae Park +Contact: SJ Park Description: Writing a number to this file sets the upper limit of nr_snapshots that deactivates the scheme when the limit is reached or exceeded. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//tried_regions/total_bytes Date: Jul 2023 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the total amount of memory that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//tried_regions//start Date: Oct 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the start address of a memory region that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//tried_regions//end Date: Oct 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the end address of a memory region that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//tried_regions//nr_accesses Date: Oct 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the 'nr_accesses' of a memory region that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//tried_regions//age Date: Oct 2022 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the 'age' of a memory region that corresponding DAMON-based Operation Scheme's action has tried to be applied. What: /sys/kernel/mm/damon/admin/kdamonds//contexts//schemes//tried_regions//sz_filter_passed Date: Dec 2024 -Contact: SeongJae Park +Contact: SJ Park Description: Reading this file returns the size of the memory in the region that passed DAMON operations layer-handled filters of the scheme in bytes. diff --git a/MAINTAINERS b/MAINTAINERS index f37a81950e25..a05d6741b249 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7124,7 +7124,7 @@ W: https://docs.dasharo.com/ F: drivers/platform/x86/dasharo-acpi.c DAMON -M: SeongJae Park +M: SJ Park L: damon@lists.linux.dev L: linux-mm@kvack.org S: Maintained diff --git a/include/linux/damon.h b/include/linux/damon.h index 02ac34537df9..cfbbf8ba28f6 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * DAMON api - * - * Author: SeongJae Park */ #ifndef _DAMON_H_ diff --git a/mm/damon/core.c b/mm/damon/core.c index 7e4b9affc5b0..d99f7a297fdd 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Data Access Monitor - * - * Author: SeongJae Park */ #define pr_fmt(fmt) "damon: " fmt diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 8298c6001fd0..32f41491b726 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON-based LRU-lists Sorting - * - * Author: SeongJae Park */ #define pr_fmt(fmt) "damon-lru-sort: " fmt diff --git a/mm/damon/modules-common.c b/mm/damon/modules-common.c index 86d58f8c4f63..f87fa46a95a0 100644 --- a/mm/damon/modules-common.c +++ b/mm/damon/modules-common.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Common Code for DAMON Modules - * - * Author: SeongJae Park */ #include diff --git a/mm/damon/modules-common.h b/mm/damon/modules-common.h index f103ad556368..6fd45490e45b 100644 --- a/mm/damon/modules-common.h +++ b/mm/damon/modules-common.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Common Code for DAMON Modules - * - * Author: SeongJae Park */ #include diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index d1842e2b00ef..6bdd1cfd3863 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Common Code for Data Access Monitoring - * - * Author: SeongJae Park */ #include diff --git a/mm/damon/ops-common.h b/mm/damon/ops-common.h index 5efa5b5970de..38d295488fa1 100644 --- a/mm/damon/ops-common.h +++ b/mm/damon/ops-common.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Common Code for Data Access Monitoring - * - * Author: SeongJae Park */ #include diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index d0598f5f2688..5c2da45f988c 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON Code for The Physical Address Space - * - * Author: SeongJae Park */ #define pr_fmt(fmt) "damon-pa: " fmt diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index ce4499cf4b8b..11b70d0a9a6f 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON-based page reclamation - * - * Author: SeongJae Park */ #define pr_fmt(fmt) "damon-reclaim: " fmt diff --git a/mm/damon/sysfs-common.c b/mm/damon/sysfs-common.c index bdc6ae2639e4..c59d7bf7a73a 100644 --- a/mm/damon/sysfs-common.c +++ b/mm/damon/sysfs-common.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Common Code for DAMON Sysfs Interface - * - * Author: SeongJae Park */ #include diff --git a/mm/damon/sysfs-common.h b/mm/damon/sysfs-common.h index 3079306966a9..733764716e8d 100644 --- a/mm/damon/sysfs-common.h +++ b/mm/damon/sysfs-common.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Common Code for DAMON Sysfs Interface - * - * Author: SeongJae Park */ #include diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 0134111c3c1f..3cbeccd436e4 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON sysfs Interface - * - * Copyright (c) 2022 SeongJae Park */ #include diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index 2e95e3bac774..a9e187158067 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON sysfs Interface - * - * Copyright (c) 2022 SeongJae Park */ #include diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h index 1cfb8c176b87..fcf7c7fadb5f 100644 --- a/mm/damon/tests/core-kunit.h +++ b/mm/damon/tests/core-kunit.h @@ -1,10 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Data Access Monitor Unit Tests - * - * Copyright 2019 Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Author: SeongJae Park */ #ifdef CONFIG_DAMON_KUNIT_TEST diff --git a/mm/damon/tests/sysfs-kunit.h b/mm/damon/tests/sysfs-kunit.h index f9ec5e795b34..138a4b8d14e7 100644 --- a/mm/damon/tests/sysfs-kunit.h +++ b/mm/damon/tests/sysfs-kunit.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Data Access Monitor Unit Tests - * - * Author: SeongJae Park */ #ifdef CONFIG_DAMON_SYSFS_KUNIT_TEST diff --git a/mm/damon/tests/vaddr-kunit.h b/mm/damon/tests/vaddr-kunit.h index 563fbc7e3f44..61f844336ffb 100644 --- a/mm/damon/tests/vaddr-kunit.h +++ b/mm/damon/tests/vaddr-kunit.h @@ -1,10 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Data Access Monitor Unit Tests - * - * Copyright 2019 Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Author: SeongJae Park */ #ifdef CONFIG_DAMON_VADDR_KUNIT_TEST diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index d27147603564..e73ec1ce016e 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * DAMON Code for Virtual Address Spaces - * - * Author: SeongJae Park */ #define pr_fmt(fmt) "damon-va: " fmt -- cgit v1.2.3 From f84ca9b1888d8fce7dfefe0e750fa971f8797486 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Tue, 30 Jun 2026 02:15:40 +0000 Subject: mm/page_vma_mapped: fix device-private PMD handling Commit 65edfda6f3f2 ("mm/rmap: extend rmap and migration support device-private entries") introduced the concept of device-private PMD entries, but did not correctly update the rmap walk code to account for them. As a result, when page_vma_mapped_walk() encounters device-private PMD entries, it takes no action other than to acquire the PMD lock and exit. However this is highly problematic for two reasons - firstly, device private entries possess a PFN so check_pmd() needs to be called to ensure an overlapping PFN range. Secondly, and more importantly, if PVMW_MIGRATION is set the caller assumes the returned entry is a migration entry, resulting in memory corruption when the caller tries to interpret the device private entry as such. In addition, commit 146287290023 ("mm/huge_memory: implement device-private THP splitting") allowed device private PMDs to be split like THP mappings, but again did not update this code path. As a result, we might race a PMD split prior to acquiring the PMD lock. This patch addresses all of these issues by invoking check_pmd(), ensuring PMVW_MIGRATION is not set and checks whether a split raced us we do for PMD THP and migration entries. Instead of checking for a subset of the cases after taking the pmd_lock(), put device-private along with pmd_trans_huge() and pmd_is_migration_entry(). Also remove thp_migration_supported() as it is already guarded by pmd_is_migration_entry(). [akpm@linux-foundation.org: fix Raspberry Pi 1 build, per David] Link: https://lore.kernel.org/20260630021540.17297-1-richard.weiyang@gmail.com Fixes: 65edfda6f3f2 ("mm/rmap: extend rmap and migration support device-private entries") Signed-off-by: Wei Yang Suggested-by: David Hildenbrand Reviewed-by: Lance Yang Acked-by: Balbir Singh Tested-by: Klara Modin Cc: SeongJae Park Cc: Zi Yan Cc: Lorenzo Stoakes Cc: q Signed-off-by: Andrew Morton --- mm/page_vma_mapped.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c index 2ccbabfb2cc1..bac2eb5de63d 100644 --- a/mm/page_vma_mapped.c +++ b/mm/page_vma_mapped.c @@ -243,21 +243,31 @@ restart: */ pmde = pmdp_get_lockless(pvmw->pmd); - if (pmd_trans_huge(pmde) || pmd_is_migration_entry(pmde)) { + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && + (pmd_trans_huge(pmde) || pmd_is_migration_entry(pmde) || + pmd_is_device_private_entry(pmde))) { pvmw->ptl = pmd_lock(mm, pvmw->pmd); pmde = *pvmw->pmd; - if (!pmd_present(pmde)) { + if (pmd_is_migration_entry(pmde)) { softleaf_t entry; - if (!thp_migration_supported() || - !(pvmw->flags & PVMW_MIGRATION)) + if (!(pvmw->flags & PVMW_MIGRATION)) return not_found(pvmw); entry = softleaf_from_pmd(pmde); + if (!check_pmd(softleaf_to_pfn(entry), pvmw)) + return not_found(pvmw); + return true; + } else if (pmd_is_device_private_entry(pmde)) { + softleaf_t entry; - if (!softleaf_is_migration(entry) || - !check_pmd(softleaf_to_pfn(entry), pvmw)) + if (pvmw->flags & PVMW_MIGRATION) + return not_found(pvmw); + entry = softleaf_from_pmd(pmde); + if (!check_pmd(softleaf_to_pfn(entry), pvmw)) return not_found(pvmw); return true; + } else if (!pmd_present(pmde)) { + return not_found(pvmw); } if (likely(pmd_trans_huge(pmde))) { if (pvmw->flags & PVMW_MIGRATION) @@ -266,17 +276,10 @@ restart: return not_found(pvmw); return true; } - /* THP pmd was split under us: handle on pte level */ + /* THP/device-private pmd was split under us: handle on pte level */ spin_unlock(pvmw->ptl); pvmw->ptl = NULL; } else if (!pmd_present(pmde)) { - const softleaf_t entry = softleaf_from_pmd(pmde); - - if (softleaf_is_device_private(entry)) { - pvmw->ptl = pmd_lock(mm, pvmw->pmd); - return true; - } - if ((pvmw->flags & PVMW_SYNC) && thp_vma_suitable_order(vma, pvmw->address, PMD_ORDER) && -- cgit v1.2.3 From e1cd30eceb6908fc13bebce41283b885d71ee8d6 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Wed, 1 Jul 2026 13:42:34 -0400 Subject: mm/huge_memory: set PG_has_hwpoisoned only after new folio head is established __split_folio_to_order() copies the hwpoison state onto each new sub-folio while splitting a folio to a non-zero order. It does so via if (handle_hwpoison && page_range_has_hwpoisoned(new_head, new_nr_pages)) folio_set_has_hwpoisoned(new_folio); *before* clear_compound_head(new_head)/prep_compound_page(new_head, ...) turns @new_head from a tail page into a proper folio head. PG_has_hwpoisoned is a FOLIO_SECOND_PAGE flag, so folio_set_has_hwpoisoned() resolves to folio_flags(folio, 1). With the new compound_info-based page-flags layout, folio_flags() asserts the page is not a tail: VM_BUG_ON_PGFLAGS(page->compound_info & 1, page); VM_BUG_ON_PGFLAGS(n > 0 && !test_bit(PG_head, &page->flags.f), page); At the current call site @new_head still has the tail marker (compound_info bit 0 set, PG_head clear), so on CONFIG_DEBUG_VM kernels this hits: kernel BUG at include/linux/page-flags.h:354 folio_flags+0x82 folio_set_has_hwpoisoned __split_folio_to_order __split_unmapped_folio __folio_split truncate_inode_partial_folio (shmem hole-punch / MADV_REMOVE) Reproduced by syzkaller: hwpoison-inject a few subpages of a large shmem folio, then MADV_REMOVE (fallocate punch hole) on the same range, which splits the partial folio to a non-zero order. memory_failure() tries to split the poisoned folio to order 0 first, but that split is best-effort; when it fails the folio is left large with PG_has_hwpoisoned set, the case fa5a06170036 added this hwpoison copying for. Move the folio_set_has_hwpoisoned() call to after clear_compound_head()/prep_compound_page(), where @new_folio is a real order-new_order head folio (handle_hwpoison implies new_order != 0, so a second page always exists). The flag still lands on the same struct page (page[1] of the new folio); only the ordering relative to compound-head setup changes, satisfying the FOLIO_SECOND_PAGE precondition. Link: https://lore.kernel.org/20260701174235.3173401-1-riel@surriel.com Fixes: fa5a06170036 ("mm/huge_memory: preserve PG_has_hwpoisoned if a folio is split to >0 order") Signed-off-by: Rik van Riel Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Tested-by: Lance Yang Reviewed-by: Lorenzo Stoakes Reviewed-by: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Lance Yang Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Yang Shi Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 2bccb0a53a0a..b5d1e9d4463d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3587,10 +3587,6 @@ static void __split_folio_to_order(struct folio *folio, int old_order, (1L << PG_dropbehind) | LRU_GEN_MASK | LRU_REFS_MASK)); - if (handle_hwpoison && - page_range_has_hwpoisoned(new_head, new_nr_pages)) - folio_set_has_hwpoisoned(new_folio); - new_folio->mapping = folio->mapping; new_folio->index = folio->index + i; @@ -3612,6 +3608,14 @@ static void __split_folio_to_order(struct folio *folio, int old_order, folio_set_large_rmappable(new_folio); } + /* + * PG_has_hwpoisoned is on the 2nd page, so set it after + * the compound head is prepped. + */ + if (handle_hwpoison && + page_range_has_hwpoisoned(new_head, new_nr_pages)) + folio_set_has_hwpoisoned(new_folio); + if (folio_test_young(folio)) folio_set_young(new_folio); if (folio_test_idle(folio)) -- cgit v1.2.3 From 955b67c3ddf9912a670ed80eae7769745b4f405e Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Mon, 25 May 2026 10:33:52 +0200 Subject: m68k: avoid -Wunused-but-set-parameter in clear_user_page() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loop in clear_user_pages() iterates over all pages and calls clear_user_page() for each of them. During the loop "vaddr" is modified. However on m68k clear_user() is a macro which does not use "vaddr". The compiler sees a variable which is modified but never used and emits a warning for that: include/linux/highmem.h: In function 'clear_user_pages': include/linux/highmem.h:234:63: warning: parameter 'vaddr' set but not used [-Wunused-but-set-parameter=] static inline void clear_user_pages(void *addr, unsigned long vaddr, Other architectures use an inline function for clear_user_page() which avoids the warning. This is not possible on m68k, as dlush_dcache_page() is another macro which is not yet defined where clear_user_page() is defined. Including cacheflush_mm.h will trigger recursive and lots of other issues. So hide the warning with a cast to (void) instead. While we are here, do the same for copy_user_page(). Link: https://lore.kernel.org/20260525-m68k-clear_user_page-v2-1-0c8981c6eca1@weissschuh.net Fixes: 62a9f5a85b98 ("mm: introduce clear_pages() and clear_user_pages()") Signed-off-by: Thomas Weißschuh Acked-by: Geert Uytterhoeven Cc: Andreas Schwab Cc: Ankur Arora Cc: David Hildenbrand Cc: Signed-off-by: Andrew Morton --- arch/m68k/include/asm/page_mm.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/m68k/include/asm/page_mm.h b/arch/m68k/include/asm/page_mm.h index ed782609ca41..0971a0651d49 100644 --- a/arch/m68k/include/asm/page_mm.h +++ b/arch/m68k/include/asm/page_mm.h @@ -55,10 +55,12 @@ static inline void clear_page(void *page) #define clear_user_page(addr, vaddr, page) \ do { clear_page(addr); \ flush_dcache_page(page); \ + (void)(vaddr); \ } while (0) #define copy_user_page(to, from, vaddr, page) \ do { copy_page(to, from); \ flush_dcache_page(page); \ + (void)(vaddr); \ } while (0) extern unsigned long m68k_memoffset; -- cgit v1.2.3 From 1292c0ecb1caefb8ca064a3639d5673991e8810c Mon Sep 17 00:00:00 2001 From: SJ Park Date: Mon, 29 Jun 2026 20:52:19 -0700 Subject: mm/damon/core: validate ranges in damon_set_regions() DAMON core logic assumes zero length regions don't exist. However, a few DAMON API callers including DAMON_SYSFS, DAMON_RECLAIM and DAMON_LRU_SORT allow users to set empty monitoring target regions. This could result in WARN_ONCE() on CONFIG_DAMON_DEBUG_SANITY enabled kernel, and divide-by-zero from damon_merge_two_regions(). For example, the WANR_ONCE() can be triggered like below. # grep DAMON_DEBUG_SANITY /boot/config-$(uname -r) # CONFIG_DAMON_DEBUG_SANITY=y # damo start # cd /sys/kernel/mm/damon/admin/kdamonds/0 # echo 0 > contexts/0/targets/0/regions/0/start # echo 0 > contexts/0/targets/0/regions/0/end # echo commit > state # dmesg [....] [ 73.705780] ------------[ cut here ]------------ [ 73.707552] start 0 >= end 0 [ 73.708452] WARNING: mm/damon/core.c:359 at damon_new_region+0x6e/0x80, CPU#1: kdamond.0/758 [...] All DAMON API callers eventually use damon_set_regions() to setup the regions. Add the validation logic in the function. Link: https://lore.kernel.org/20260630035221.146458-1-sj@kernel.org Fixes: 43b0536cb471 ("mm/damon: introduce DAMON-based Reclamation (DAMON_RECLAIM)") Signed-off-by: SJ Park Cc: Yang yingliang Cc: # 5.16.x Signed-off-by: Andrew Morton --- mm/damon/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/damon/core.c b/mm/damon/core.c index d99f7a297fdd..949d5309d54d 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -356,6 +356,12 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, unsigned int i; int err; + for (i = 0; i < nr_ranges; i++) { + if (ALIGN_DOWN(ranges[i].start, min_region_sz) >= + ALIGN(ranges[i].end, min_region_sz)) + return -EINVAL; + } + /* Remove regions which are not in the new ranges */ damon_for_each_region_safe(r, next, t) { for (i = 0; i < nr_ranges; i++) { -- cgit v1.2.3 From e1a1729419e3e440668dbc1ae95b0dc2004172fe Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 15 May 2026 22:44:46 +0200 Subject: fat: avoid stack overflow warning Building the fat kunit tests on with -fsanitize=alignment reveals some rather excessive stack usage: fs/fat/fat_test.c: In function 'fat_clus_to_blknr_test': fs/fat/fat_test.c:33:1: error: the frame size of 4736 bytes is larger than 1536 bytes [-Werror=frame-larger-than=] 33 | } | ^ fs/fat/fat_test.c: In function 'fat_get_blknr_offset_test': fs/fat/fat_test.c:52:1: error: the frame size of 4800 bytes is larger than 1536 bytes [-Werror=frame-larger-than=] The problem is clearly related to the on-stack copy of a local msdos_sb_info structure. Avoid this by making that copy 'static const' and changing the called functions to accept a constant input. Link: https://lore.kernel.org/20260515204456.2692208-1-arnd@kernel.org Fixes: 410002f8139c ("kunit: fat: test cluster and directory i_pos layout helpers") Signed-off-by: Arnd Bergmann Acked-by: OGAWA Hirofumi Cc: Christian Brauner Cc: Jan Kara Cc: Adi Nata Cc: David Laight Signed-off-by: Andrew Morton --- fs/fat/fat.h | 4 ++-- fs/fat/fat_test.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 99ed9228a677..2772675bd35a 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -249,13 +249,13 @@ static inline unsigned char fat_checksum(const __u8 *name) return s; } -static inline sector_t fat_clus_to_blknr(struct msdos_sb_info *sbi, int clus) +static inline sector_t fat_clus_to_blknr(const struct msdos_sb_info *sbi, int clus) { return ((sector_t)clus - FAT_START_ENT) * sbi->sec_per_clus + sbi->data_start; } -static inline void fat_get_blknr_offset(struct msdos_sb_info *sbi, +static inline void fat_get_blknr_offset(const struct msdos_sb_info *sbi, loff_t i_pos, sector_t *blknr, int *offset) { *blknr = i_pos >> sbi->dir_per_block_bits; diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c index 4eeed9dca549..9583ce66dca3 100644 --- a/fs/fat/fat_test.c +++ b/fs/fat/fat_test.c @@ -22,7 +22,7 @@ static void fat_checksum_test(struct kunit *test) static void fat_clus_to_blknr_test(struct kunit *test) { - struct msdos_sb_info sbi = { + static const struct msdos_sb_info sbi = { .sec_per_clus = 4, .data_start = 100, }; @@ -34,7 +34,7 @@ static void fat_clus_to_blknr_test(struct kunit *test) static void fat_get_blknr_offset_test(struct kunit *test) { - struct msdos_sb_info sbi = { + static const struct msdos_sb_info sbi = { .dir_per_block = 16, .dir_per_block_bits = 4, }; -- cgit v1.2.3 From f8ed699d493aafb8a989cc7d10a027ea40bd422f Mon Sep 17 00:00:00 2001 From: Lance Yang Date: Thu, 2 Jul 2026 22:02:57 +0800 Subject: MAINTAINERS: add Usama as a THP reviewer Usama has been active around THP, really enjoys working on THPs, and tries to review all the patches that come in. Let's invite him to the THP party as a reviewer to help with the ongoing THP review load. Link: https://lore.kernel.org/20260702140257.44780-1-lance.yang@linux.dev Signed-off-by: Lance Yang Acked-by: David Hildenbrand (Arm) Acked-by: Lorenzo Stoakes Acked-by: Zi Yan Acked-by: Barry Song Acked-by: SJ Park Acked-by: Baolin Wang Cc: Dev Jain Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Zi Yan Signed-off-by: Andrew Morton --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index a05d6741b249..852f7f0dd616 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17255,6 +17255,7 @@ R: Ryan Roberts R: Dev Jain R: Barry Song R: Lance Yang +R: Usama Arif L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org -- cgit v1.2.3 From 954157679ec34661c2e87e7eb796104a797c32db Mon Sep 17 00:00:00 2001 From: SJ Park Date: Fri, 3 Jul 2026 09:56:08 -0700 Subject: mm/damon/core: disallow overlapping input ranges for damon_set_regions() damon_set_regions() assumes the input ranges are sorted by the address and don't overlap each other. Hence the assumption was initially to be explicitly validated. But commit 97d482f4592f ("mm/damon/sysfs: reuse damon_set_regions() for regions setting") has mistakenly removed the validation. This can make DAMON behave in unexpected ways. At the best, the monitoring results snapshot will just look weird since there will be overlapping regions. DAMOS will also work weirdly, applying the same action multiple times for overlapping regions, and make DAMOS quota weird. More seriously, depending on the setup and regions updates sequence, negative size regions can be made. It will trigger WARN_ONCE() if the kernel is built with CONFIG_DAMON_DEBUG_SANITY=y. Depending on the monitoring results, the negative size region can further trigger division by zero in damon_merge_two_regions(). Note that some of the consequences including the WARN_ONCE() and the divide by zero depend on commits that were introduced after the root cause commit 97d482f4592f ("mm/damon/sysfs: reuse damon_set_regions() for regions setting"). Fix the problems by checking the assumption and returning an error if the input ranges don't meet the assumption. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260703165610.92894-1-sj@kernel.org Link: https://lore.kernel.org/20260630041806.151124-1-sj@kernel.org [1] Fixes: 97d482f4592f ("mm/damon/sysfs: reuse damon_set_regions() for regions setting") Signed-off-by: SJ Park Cc: # 5.19.x Signed-off-by: Andrew Morton --- mm/damon/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 949d5309d54d..cff932b3317d 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -354,12 +354,19 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, { struct damon_region *r, *next; unsigned int i; + unsigned long last_end; int err; for (i = 0; i < nr_ranges; i++) { - if (ALIGN_DOWN(ranges[i].start, min_region_sz) >= - ALIGN(ranges[i].end, min_region_sz)) + unsigned long start, end; + + start = ALIGN_DOWN(ranges[i].start, min_region_sz); + end = ALIGN(ranges[i].end, min_region_sz); + if (start >= end) + return -EINVAL; + if (i > 0 && last_end > start) return -EINVAL; + last_end = end; } /* Remove regions which are not in the new ranges */ -- cgit v1.2.3 From 79c37ae3733e93d9d8ea12ecb44f717e61439024 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 3 Jul 2026 09:17:24 -0700 Subject: mm/kmemleak: fix checksum computation for per-cpu objects The per-cpu object checksum folds each CPU's CRC together with XOR and seeds every CRC with 0. Both choices make update_checksum() miss content changes: - XOR is self-cancelling, so equal contents on two CPUs cancel out and simultaneous identical changes leave the checksum unchanged. - crc32(0, ...) over all-zero content is 0, so a freshly allocated, zeroed per-cpu area checksums to 0, matching the initial value, and the object is never seen to change. See discussions at [0]. When update_checksum() wrongly reports an actively modified object as unchanged, kmemleak stops greying it for an extra scan and can report a live per-cpu object as a leak. Fold the per-cpu CRC as a single rolling checksum across all CPUs and initialise the object checksum to ~0 so the first computed value always registers as a change, even for content that hashes to 0. reset_checksum() is seeded the same way. Link: https://lore.kernel.org/all/akfYImSNDh3OjIfR@gmail.com [0] Link: https://lore.kernel.org/20260703-kmemleak_checksum-v1-1-5e0ab7d6966f@debian.org Fixes: 6c99d4eb7c5e ("kmemleak: enable tracking for percpu pointers") Signed-off-by: Breno Leitao Co-developed-by: Catalin Marinas Signed-off-by: Catalin Marinas Reviewed-by: Pavel Tikhomirov Cc: Signed-off-by: Andrew Morton --- mm/kmemleak.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 7c7ba17ce7af..e196f53f9b46 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -687,7 +687,7 @@ static struct kmemleak_object *__alloc_object(gfp_t gfp) atomic_set(&object->use_count, 1); object->excess_ref = 0; object->count = 0; /* white color initially */ - object->checksum = 0; + object->checksum = ~0; object->del_state = 0; /* task information */ @@ -981,7 +981,7 @@ static void reset_checksum(unsigned long ptr) } raw_spin_lock_irqsave(&object->lock, flags); - object->checksum = 0; + object->checksum = ~0; raw_spin_unlock_irqrestore(&object->lock, flags); put_object(object); } @@ -1410,7 +1410,8 @@ static bool update_checksum(struct kmemleak_object *object) for_each_possible_cpu(cpu) { void *ptr = per_cpu_ptr((void __percpu *)object->pointer, cpu); - object->checksum ^= crc32(0, kasan_reset_tag((void *)ptr), object->size); + object->checksum = crc32(object->checksum, + kasan_reset_tag((void *)ptr), object->size); } } else { object->checksum = crc32(0, kasan_reset_tag((void *)object->pointer), object->size); -- cgit v1.2.3 From 47c81dadb704cdf07bb8f9f43b4fbe758a351e08 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 5 Jul 2026 02:25:13 -0400 Subject: mm: page_reporting: allow driver to set batch capacity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the moment, if a virtio balloon device has a page reporting vq but its size is < PAGE_REPORTING_CAPACITY (32), the balloon driver fails probe. But, there's no way for host to know this value, so it can easily create a smaller vq and suddenly adding the reporting capability to the device makes all of the driver fail. Not pretty. Add a capacity field to page_reporting_dev_info so drivers can control the maximum number of pages per report batch. In virtio-balloon, set the capacity to the reporting virtqueue size, letting page_reporting adapt to whatever the device provides. Capacity need not be a power of two. Code previously called out division by PAGE_REPORTING_CAPACITY as cheap since it was a power of 2, but no performance difference was observed with non-power-of-2 values. If capacity is 0 or exceeds PAGE_REPORTING_CAPACITY, it defaults to PAGE_REPORTING_CAPACITY. The 0 check and the clamping is done in page_reporting_register(), before the reporting work is scheduled, so we never get division by 0. Link: https://lore.kernel.org/444c24cf39f3f3620fc90ef4695bd6b0979f4c4b.1783232420.git.mst@redhat.com Fixes: b0c504f15471 ("virtio-balloon: add support for providing free page reports to host") Signed-off-by: Michael S. Tsirkin Assisted-by: Claude:claude-opus-4-6 Acked-by: David Hildenbrand (Arm) Reviewed-by: Gregory Price Acked-by: Zi Yan Reviewed-by: Pankaj Gupta Cc: Alexander Duyck Cc: Brendan Jackman Cc: Eugenio Pérez Cc: Jason Wang Cc: Johannes Weiner Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Xuan Zhuo Signed-off-by: Andrew Morton --- drivers/virtio/virtio_balloon.c | 5 +---- include/linux/page_reporting.h | 4 +++- mm/page_reporting.c | 24 ++++++++++++------------ 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 088b3a0e6ce6..581ac799d974 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -1017,10 +1017,6 @@ static int virtballoon_probe(struct virtio_device *vdev) unsigned int capacity; capacity = virtqueue_get_vring_size(vb->reporting_vq); - if (capacity < PAGE_REPORTING_CAPACITY) { - err = -ENOSPC; - goto out_unregister_oom; - } vb->pr_dev_info.order = PAGE_REPORTING_ORDER_UNSPECIFIED; @@ -1041,6 +1037,7 @@ static int virtballoon_probe(struct virtio_device *vdev) vb->pr_dev_info.order = 5; #endif + vb->pr_dev_info.capacity = capacity; err = page_reporting_register(&vb->pr_dev_info); if (err) goto out_unregister_oom; diff --git a/include/linux/page_reporting.h b/include/linux/page_reporting.h index 9d4ca5c218a0..272b1274efdc 100644 --- a/include/linux/page_reporting.h +++ b/include/linux/page_reporting.h @@ -5,7 +5,6 @@ #include #include -/* This value should always be a power of 2, see page_reporting_cycle() */ #define PAGE_REPORTING_CAPACITY 32 #define PAGE_REPORTING_ORDER_UNSPECIFIED -1 @@ -22,6 +21,9 @@ struct page_reporting_dev_info { /* Minimal order of page reporting */ unsigned int order; + + /* Max pages per report batch; 0 (default) means PAGE_REPORTING_CAPACITY */ + unsigned int capacity; }; /* Tear-down and bring-up for page reporting devices */ diff --git a/mm/page_reporting.c b/mm/page_reporting.c index 7418f2e500bb..942e84b6908a 100644 --- a/mm/page_reporting.c +++ b/mm/page_reporting.c @@ -173,11 +173,8 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone, * any pages that may have already been present from the previous * list processed. This should result in us reporting all pages on * an idle system in about 30 seconds. - * - * The division here should be cheap since PAGE_REPORTING_CAPACITY - * should always be a power of 2. */ - budget = DIV_ROUND_UP(area->nr_free, PAGE_REPORTING_CAPACITY * 16); + budget = DIV_ROUND_UP(area->nr_free, prdev->capacity * 16); /* loop through free list adding unreported pages to sg list */ list_for_each_entry_safe(page, next, list, lru) { @@ -222,10 +219,10 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone, spin_unlock_irq(&zone->lock); /* begin processing pages in local list */ - err = prdev->report(prdev, sgl, PAGE_REPORTING_CAPACITY); + err = prdev->report(prdev, sgl, prdev->capacity); /* reset offset since the full list was reported */ - *offset = PAGE_REPORTING_CAPACITY; + *offset = prdev->capacity; /* update budget to reflect call to report function */ budget--; @@ -234,7 +231,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone, spin_lock_irq(&zone->lock); /* flush reported pages from the sg list */ - page_reporting_drain(prdev, sgl, PAGE_REPORTING_CAPACITY, !err); + page_reporting_drain(prdev, sgl, prdev->capacity, !err); /* * Reset next to first entry, the old next isn't valid @@ -260,13 +257,13 @@ static int page_reporting_process_zone(struct page_reporting_dev_info *prdev, struct scatterlist *sgl, struct zone *zone) { - unsigned int order, mt, leftover, offset = PAGE_REPORTING_CAPACITY; + unsigned int order, mt, leftover, offset = prdev->capacity; unsigned long watermark; int err = 0; /* Generate minimum watermark to be able to guarantee progress */ watermark = low_wmark_pages(zone) + - (PAGE_REPORTING_CAPACITY << page_reporting_order); + (prdev->capacity << page_reporting_order); /* * Cancel request if insufficient free memory or if we failed @@ -290,7 +287,7 @@ page_reporting_process_zone(struct page_reporting_dev_info *prdev, } /* report the leftover pages before going idle */ - leftover = PAGE_REPORTING_CAPACITY - offset; + leftover = prdev->capacity - offset; if (leftover) { sgl = &sgl[offset]; err = prdev->report(prdev, sgl, leftover); @@ -322,11 +319,11 @@ static void page_reporting_process(struct work_struct *work) atomic_set(&prdev->state, state); /* allocate scatterlist to store pages being reported on */ - sgl = kmalloc_objs(*sgl, PAGE_REPORTING_CAPACITY); + sgl = kmalloc_objs(*sgl, prdev->capacity); if (!sgl) goto err_out; - sg_init_table(sgl, PAGE_REPORTING_CAPACITY); + sg_init_table(sgl, prdev->capacity); for_each_zone(zone) { err = page_reporting_process_zone(prdev, sgl, zone); @@ -377,6 +374,9 @@ int page_reporting_register(struct page_reporting_dev_info *prdev) page_reporting_order = pageblock_order; } + if (!prdev->capacity || prdev->capacity > PAGE_REPORTING_CAPACITY) + prdev->capacity = PAGE_REPORTING_CAPACITY; + /* initialize state and work structures */ atomic_set(&prdev->state, PAGE_REPORTING_IDLE); INIT_DELAYED_WORK(&prdev->work, &page_reporting_process); -- cgit v1.2.3 From e57d6e9e20b551e4910d7a6331a81775c3ad6693 Mon Sep 17 00:00:00 2001 From: Xie Yuanbin Date: Fri, 5 Jun 2026 16:12:13 +0800 Subject: mm/memory-failure: trace: change memory_failure_event to ras subsystem Commit 97f0b1345219 ("tracing: add trace event for memory-failure") introduced memory_failure_event in ras subsystem. commit 31807483d395 ("mm/memory-failure: remove the selection of RAS") changed memory_failure_event to memory_failure subsystem. This breaks the backward compatibility, some user programs rely on it. Change memory_failure_event to ras subsystem to keep backward compatibility. Link: https://lore.kernel.org/20260605081213.154660-1-xieyuanbin1@huawei.com Fixes: 31807483d395 ("mm/memory-failure: remove the selection of RAS") Signed-off-by: Xie Yuanbin Reported-by: Yi Lai Reported-by: Qiuxu Zhuo Closes: https://lore.kernel.org/linux-mm/CY8PR11MB7134346A3E4BB28ECA28D6E989132@CY8PR11MB7134.namprd11.prod.outlook.com Acked-by: David Hildenbrand (Arm) Reviewed-by: Qiuxu Zhuo Reviewed-by: Lance Yang Reviewed-by: Miaohe Lin Tested-by: Qiuxu Zhuo Reviewed-by: Lance Yang Cc: Steven Rostedt Cc: Borislav Petkov Cc: Signed-off-by: Andrew Morton --- include/trace/events/memory-failure.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/trace/events/memory-failure.h b/include/trace/events/memory-failure.h index aa57cc8f896b..7a8ee5d1a44e 100644 --- a/include/trace/events/memory-failure.h +++ b/include/trace/events/memory-failure.h @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ #undef TRACE_SYSTEM -#define TRACE_SYSTEM memory_failure +/* + * For historical versions, memory_failure_event is in ras subsystem, + * some user programs depend on it. + */ +#define TRACE_SYSTEM ras #define TRACE_INCLUDE_FILE memory-failure #if !defined(_TRACE_MEMORY_FAILURE_H) || defined(TRACE_HEADER_MULTI_READ) -- cgit v1.2.3 From 90dd1415a158b99ca16f5fe5862c07683e9ddcec Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 6 Jul 2026 20:48:09 +0200 Subject: 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; } -- cgit v1.2.3 From d2c46c9f7a9baf80a322eb1d4494a70e535b637c Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 6 Jul 2026 20:48:11 +0200 Subject: 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 @@ SetTimesRetry: if (rc == -EAGAIN) goto SetTimesRetry; - if (rc == -EOPNOTSUPP) - return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data, - nls_codepage, cifs_sb); - return rc; } -- cgit v1.2.3 From 6763a0aea6d658d69b9215ab9151d7bd4c1c314b Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 15 Jun 2026 14:47:37 +1000 Subject: 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++; } } } -- cgit v1.2.3 From 5948aaf64f81f217a25dcc2bf6c0779bca19566c Mon Sep 17 00:00:00 2001 From: Lee Jia Jie Date: Thu, 9 Jul 2026 21:56:19 +0800 Subject: 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. * -- cgit v1.2.3 From 33cb713db0161b54f04fe830e062c9e102c29a04 Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Wed, 1 Jul 2026 23:46:27 +0200 Subject: 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 -- cgit v1.2.3 From f4b30e0b1d488e7ffd8ea28d1365b9ba8e551edb Mon Sep 17 00:00:00 2001 From: Matthieu Buffet Date: Wed, 1 Jul 2026 23:46:28 +0200 Subject: 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; -- cgit v1.2.3 From 592a37889f97d60debf6a442684ba4d13435c817 Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Fri, 3 Jul 2026 16:17:09 +0200 Subject: 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; -- cgit v1.2.3 From 97c0e344e03818dbf3116e77d3d7fe81f1fbe795 Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Wed, 8 Jul 2026 13:06:33 +0200 Subject: 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); -- cgit v1.2.3 From d793186aa3bb833c878ae6826c87e62c843afaa3 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 9 Jul 2026 18:43:40 +0200 Subject: 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. */ -- cgit v1.2.3 From 5ab1dc6d110db6bee167a32fd94c53ea0e7ad6d2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 10 Jul 2026 10:16:42 +0200 Subject: 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 --- .../testing/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); -- cgit v1.2.3 From ec4215683e47424c9c4762fd3c60f552a3119142 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 6 Jul 2026 11:01:59 +0200 Subject: 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 */ } /* -- cgit v1.2.3 From fe179677b6dcb4b658586038a811f87265e97777 Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Mon, 15 Jun 2026 14:41:19 +0530 Subject: 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. -- cgit v1.2.3 From 10229a0702b51d0295b53190d34a7de9aa20602c Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 7 Jul 2026 12:14:14 +0000 Subject: 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 -- cgit v1.2.3 From 24d7e5e39b04c1ef8eee0688ca1527e879b22a40 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 14:31:25 +0200 Subject: 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) { -- cgit v1.2.3 From 7bb03b2b01b814a9fc14afbfc2cbb2cca5b34750 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:22:47 +0200 Subject: 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 @@ skip_spinlock: 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; -- cgit v1.2.3 From edf025f083854f80032b73a1aad69a3c90db236f Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:24:09 +0200 Subject: 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 @@ thorough_test: *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)); -- cgit v1.2.3 From 5a266764fadaff8b5c1fe37a186ebf9b09cb953e Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:25:39 +0200 Subject: 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 @@ skip_spinlock: 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); -- cgit v1.2.3 From 366665416f20527ff7cad548a32d1ddf23195740 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:29:11 +0200 Subject: 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); -- cgit v1.2.3 From 76c6f845dc0c614304a6e6ee619b552f97cf24b3 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:31:47 +0200 Subject: 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; } -- cgit v1.2.3 From 72e9ec2fe32b00994f41719cf77423fca67d48b2 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:33:16 +0200 Subject: 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; -- cgit v1.2.3 From e72b793ae440f6900fb17a4b8518c707b5cd3e17 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:35:06 +0200 Subject: 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); -- cgit v1.2.3 From 88dd117c92a142253fb7a17e791773902b3babc6 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:36:01 +0200 Subject: 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 -- cgit v1.2.3 From 8ec4d9c5a5cf4b61fc087f871465b1f79b393325 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 9 Jul 2026 21:37:38 +0200 Subject: 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; -- cgit v1.2.3 From f9eff167fefa6f222af87ca605ddd6b6494e390f Mon Sep 17 00:00:00 2001 From: "Uwe Kleine-König (The Capable Hub)" Date: Sun, 5 Jul 2026 10:50:00 +0200 Subject: 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 -- cgit v1.2.3 From e9ad4d5ca309cb517d3f7a85251c3c5328f40f1f Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 25 Jun 2026 22:48:33 -0400 Subject: 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; } -- cgit v1.2.3 From 061b627ba534230a18ec4d7251562af12325d06a Mon Sep 17 00:00:00 2001 From: Matthias Feser Date: Tue, 26 May 2026 07:35:09 +0000 Subject: 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: -- cgit v1.2.3 From b1b4efea05a56c0995e4702a86d6624b4fdff32f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 26 Jun 2026 11:49:37 +0200 Subject: 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[] = { -- cgit v1.2.3 From 302fbbb4fcbdeac2dc8c63a56c1c4e38c4781958 Mon Sep 17 00:00:00 2001 From: John Ogness Date: Tue, 7 Jul 2026 16:16:04 +0206 Subject: 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; -- cgit v1.2.3 From bd910a7660d280595ef94cb6d193951d855d330f Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 9 Jul 2026 22:28:37 -0400 Subject: 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; -- cgit v1.2.3 From 2a892294b83f541115c94b0bb637f39bef187657 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Fri, 10 Jul 2026 16:15:27 +0530 Subject: 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; } -- cgit v1.2.3 From f2f152e94a67bc746afaf05a1b2702c195553112 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Sat, 4 Jul 2026 10:14:21 -0700 Subject: 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 @@ succeeded: 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: -- cgit v1.2.3 From 4fa349156043dc119721d067329714179f501749 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sun, 5 Jul 2026 22:24:36 -0500 Subject: 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; } -- cgit v1.2.3 From 5d1a2240935ea47e2673d0ea17fdb058e4dc91dd Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Sat, 13 Jun 2026 18:27:15 +0800 Subject: 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); } -- cgit v1.2.3 From b06163ce52ec0561f202fbfb9b08090cb61f512e Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Tue, 7 Jul 2026 19:00:14 +0800 Subject: 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 @@ next: 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; } -- cgit v1.2.3 From 86f3ce81dd2b4b0aa2c3016c989a943e4b1b643d Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Wed, 8 Jul 2026 18:11:50 +0000 Subject: 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); -- cgit v1.2.3 From f62c41b4910e65da396ec9a8c40c1fe7fe82e449 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Wed, 8 Jul 2026 13:27:28 +0800 Subject: 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 @@ check_connections: /* 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 @@ check_connections: 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". -- cgit v1.2.3 From bd0bdfae1cf0e064b317cd62b3bc29326684cdc6 Mon Sep 17 00:00:00 2001 From: Zhengyang Chen Date: Mon, 22 Jun 2026 18:10:27 +0800 Subject: 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. -- cgit v1.2.3 From 90941d9c925d66a482c9121919ec3546a6988c16 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 9 Jul 2026 13:40:25 +0200 Subject: 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: -- cgit v1.2.3 From a2f57827bf7c695b8c72dc4511cae8e86582369d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 8 Jul 2026 16:21:30 +0200 Subject: 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; -- cgit v1.2.3 From b3fe4cbd583895987935a9bdad01c8f9d3a02310 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 8 Jul 2026 21:03:15 +0300 Subject: 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 -- cgit v1.2.3 From f468c48d488d0ea2df3422b3e1dfafae1611e853 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 10 Jul 2026 12:44:41 +0200 Subject: 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) { -- cgit v1.2.3 From 1cd23ca80784223fa2204e16203f754da4e821f8 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Fri, 3 Jul 2026 20:35:46 -0700 Subject: 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 -- cgit v1.2.3 From 181bb9c9eae4f69fe510a62a42c2932d0314a800 Mon Sep 17 00:00:00 2001 From: Connor Williamson Date: Mon, 15 Jun 2026 13:07:15 +0000 Subject: 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); -- cgit v1.2.3 From 58a37e7317b06665e21609a2f867a9962e9e2919 Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Fri, 10 Jul 2026 16:34:37 +0800 Subject: 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 --- tools/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)); } } -- cgit v1.2.3 From 6ee4140788234a6fabf59e6a50e38cdb936008cd Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Jun 2026 15:36:32 -0700 Subject: 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; -- cgit v1.2.3 From df371f2c62446c5fc5c5c1c91b47c219bb0d2100 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Jun 2026 15:36:33 -0700 Subject: 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 --- .../testing/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 @@ out: 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; } -- cgit v1.2.3 From 745df2052cf94b8e3348da59924d2eb0e24d4bb7 Mon Sep 17 00:00:00 2001 From: Gary Yang Date: Fri, 26 Jun 2026 17:20:15 +0800 Subject: 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/ -- cgit v1.2.3 From cfbebb55e5127dc162e73fa8956000055a78606c Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 10 Jul 2026 11:53:23 +0800 Subject: 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; } -- cgit v1.2.3 From 22a78be4123dce81d72c963a98b774b7d5e1f8e5 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 8 Jul 2026 15:32:39 -0400 Subject: 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 --- .../test.d/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 -- cgit v1.2.3 From c5c413534d40eb4c982f1794e62813f926aba747 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 8 Jul 2026 16:34:36 -0400 Subject: 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 --- tools/testing/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. -- cgit v1.2.3 From 5142c56651578abc346d6c17f3fb919b9ffbb317 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 8 Jul 2026 11:54:58 +0200 Subject: 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(); -- cgit v1.2.3 From 422f1d4f141eaa3a6e4199ceec86cc6b9bf26570 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:32:49 +0200 Subject: 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())) -- cgit v1.2.3 From 1917eb2db750ecbdf710f79a8042eaa545a063c7 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:35:49 +0200 Subject: 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 -- cgit v1.2.3 From 386df1a57b631c456d14f857cb0c0c2e11c16bef Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 10 Jul 2026 18:37:15 +0200 Subject: 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; } -- cgit v1.2.3 From 0e2f4ab68a89fad42e0f5a9ff4b740738e7aa1d6 Mon Sep 17 00:00:00 2001 From: Kuba Piecuch Date: Fri, 10 Jul 2026 14:43:41 +0000 Subject: 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); -- cgit v1.2.3 From 09b2ae290a241ce1f5f738fb65c35f449dcf663d Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Mon, 6 Jul 2026 09:13:39 -0400 Subject: platform/x86/intel/vsec: free ACPI discovery data on early errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_vsec_add_dev() may attach an ACPI discovery table copy to the intel_vsec_device before passing ownership to intel_vsec_add_aux(). The normal auxiliary-device release path frees that copy, but the earliest intel_vsec_add_aux() failures free only the outer structure directly. Route those direct frees through a common helper so acpi_disc is released consistently on the parent, xarray, and ID allocation failure paths. Fixes: 22fa2ebc11a1 ("platform/x86/intel/vsec: Plumb ACPI PMT discovery tables through vsec") Signed-off-by: Yousef Alhouseen Reviewed-by: David E. Box Link: https://patch.msgid.link/CAMuQ4bUtJtYNTguKoiXngROJw0QQQcrvW3=3_B0-hpMQOFqvCQ@mail.gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 3ae4557b32b4..5ab2215fdd7f 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -103,6 +103,12 @@ static void intel_vsec_remove_aux(void *data) auxiliary_device_uninit(data); } +static void intel_vsec_dev_free(struct intel_vsec_device *intel_vsec_dev) +{ + kfree(intel_vsec_dev->acpi_disc); + kfree(intel_vsec_dev); +} + static void intel_vsec_dev_release(struct device *dev) { struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); @@ -111,8 +117,7 @@ static void intel_vsec_dev_release(struct device *dev) ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); - kfree(intel_vsec_dev->acpi_disc); - kfree(intel_vsec_dev); + intel_vsec_dev_free(intel_vsec_dev); } static const struct vsec_feature_dependency * @@ -218,20 +223,22 @@ int intel_vsec_add_aux(struct device *parent, struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; int ret, id; - if (!parent) + if (!parent) { + intel_vsec_dev_free(intel_vsec_dev); return -EINVAL; + } ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, PMT_XA_LIMIT, GFP_KERNEL); if (ret < 0) { - kfree(intel_vsec_dev); + intel_vsec_dev_free(intel_vsec_dev); return ret; } id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); if (id < 0) { xa_erase(&auxdev_array, intel_vsec_dev->id); - kfree(intel_vsec_dev); + intel_vsec_dev_free(intel_vsec_dev); return id; } -- cgit v1.2.3 From 78bf392ba77dd8b2a25656e489449d2f91cfd1eb Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Fri, 10 Jul 2026 16:58:41 +0000 Subject: platform/x86: asus-wmi: temporarily revert to setting a charge limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A userspace regression has been observed leaving the battery charging threshold unconfigured, so while the fix is being shipped revert the change keeping the infrastructure in place to return to the preferred behaviour as soon as it's appropriate to do. Link: https://lore.kernel.org/all/5db117b7-aad1-437f-a3d4-ba7b29fc68b3@redhat.com/ Link: https://gitlab.freedesktop.org/upower/upower/-/work_items/347 Closes: https://lore.kernel.org/all/CABsFS_g+V_Owum6knLhenhM15EXJRrsF0FcLiw30WZxarsTpUA@mail.gmail.com/ Fixes: 186bf9031666 ("platform/x86: asus-wmi: do not enforce a battery charge threshold") Reported-by: Travers Biddle Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260710165841.59957-1-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 3c9ef826551d..e835779b6f5f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1618,6 +1618,8 @@ static DEVICE_ATTR_RW(charge_control_end_threshold); static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) { + int ret, rv; + /* The WMI method does not provide a way to specific a battery, so we * just assume it is the first battery. * Note: On some newer ASUS laptops (Zenbook UM431DA), the primary/first @@ -1635,12 +1637,30 @@ static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_batter /* The charge threshold is only reset when the system is power cycled, * and we can't read the current threshold, however the majority of - * platforms retains it, therefore signal the threshold as unknown - * until user explicitly sets it to a new value. + * platforms retains it. + * + * Setting a negative value would signal the threshold as unknown + * until user explicitly sets it to a new value, however to avoid + * regressing userspace, we initialize it to a value of 100. */ - charge_end_threshold = -1; + charge_end_threshold = 100; + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, charge_end_threshold, &rv); + if (ret) { + pr_err("Failed to reset battery charge threshold\n"); + goto asus_wmi_battery_add_err; + } + + if (rv != 1) { + pr_err("Error in battery charge threshold reset\n"); + ret = -EIO; + goto asus_wmi_battery_add_err; + } return 0; +asus_wmi_battery_add_err: + device_remove_file(&battery->dev, + &dev_attr_charge_control_end_threshold); + return ret; } static int asus_wmi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook) -- cgit v1.2.3 From ec082d0b978b5fb4c11205ccce63587ac94c74e1 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:15 +0100 Subject: 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); -- cgit v1.2.3 From d471d4f86e8689ad15309e49da8e5e4e9bb9ce87 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:16 +0100 Subject: 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); } -- cgit v1.2.3 From 601ddaceb861be7eb557278109966320a6f3478c Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 9 Jul 2026 17:00:17 +0100 Subject: 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); -- cgit v1.2.3 From 460511c11f0d67e62c6526b191b205eafc8033b2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 10 Jul 2026 15:26:04 -0700 Subject: arc: validate DT CPU map strings before parsing them arc_get_cpu_map() fetches the possible-cpus or present-cpus property from the flat DT and immediately passes the raw pointer to cpulist_parse(). That parser expects a NUL-terminated text buffer, but this path does not prove that the DT property is terminated within its declared bounds. Reject unterminated CPU-map properties before handing them to cpulist_parse(). Changes since v1: - fold the NUL-termination check into the initial lookup test, as suggested by Vineet Gupta Signed-off-by: Pengpeng Hou Signed-off-by: Vineet Gupta --- arch/arc/kernel/smp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index b2f2c59279a6..2d99dffed0ce 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -43,9 +44,10 @@ static int __init arc_get_cpu_map(const char *name, struct cpumask *cpumask) { unsigned long dt_root = of_get_flat_dt_root(); const char *buf; + int len; - buf = of_get_flat_dt_prop(dt_root, name, NULL); - if (!buf) + buf = of_get_flat_dt_prop(dt_root, name, &len); + if (!buf || !memchr(buf, '\0', len)) return -EINVAL; if (cpulist_parse(buf, cpumask)) -- cgit v1.2.3 From 76f38ad1b39cf0321f7ecb7ea37b46f61a0f3d75 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2026 15:26:11 -0700 Subject: ARC: configs: Drop redundant I2C_DESIGNWARE_PLATFORM I2C_DESIGNWARE_PLATFORM is default=y via I2C_DESIGNWARE_CORE, which is enabled. No impact on include/generated/autoconf.h. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Vineet Gupta --- arch/arc/configs/axs101_defconfig | 1 - arch/arc/configs/axs103_defconfig | 1 - arch/arc/configs/axs103_smp_defconfig | 1 - arch/arc/configs/tb10x_defconfig | 1 - 4 files changed, 4 deletions(-) diff --git a/arch/arc/configs/axs101_defconfig b/arch/arc/configs/axs101_defconfig index f930396d9dae..870e5291b7db 100644 --- a/arch/arc/configs/axs101_defconfig +++ b/arch/arc/configs/axs101_defconfig @@ -67,7 +67,6 @@ CONFIG_SERIAL_OF_PLATFORM=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_DESIGNWARE_CORE=y -CONFIG_I2C_DESIGNWARE_PLATFORM=y # CONFIG_HWMON is not set CONFIG_DRM=m CONFIG_DRM_I2C_ADV7511=m diff --git a/arch/arc/configs/axs103_defconfig b/arch/arc/configs/axs103_defconfig index 6b779dee5ea0..d45e4d335998 100644 --- a/arch/arc/configs/axs103_defconfig +++ b/arch/arc/configs/axs103_defconfig @@ -67,7 +67,6 @@ CONFIG_SERIAL_OF_PLATFORM=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_DESIGNWARE_CORE=y -CONFIG_I2C_DESIGNWARE_PLATFORM=y # CONFIG_HWMON is not set CONFIG_FB=y CONFIG_FRAMEBUFFER_CONSOLE=y diff --git a/arch/arc/configs/axs103_smp_defconfig b/arch/arc/configs/axs103_smp_defconfig index a89b50d5369d..f986c0205f13 100644 --- a/arch/arc/configs/axs103_smp_defconfig +++ b/arch/arc/configs/axs103_smp_defconfig @@ -67,7 +67,6 @@ CONFIG_SERIAL_OF_PLATFORM=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_DESIGNWARE_CORE=y -CONFIG_I2C_DESIGNWARE_PLATFORM=y # CONFIG_HWMON is not set CONFIG_DRM=m CONFIG_DRM_I2C_ADV7511=m diff --git a/arch/arc/configs/tb10x_defconfig b/arch/arc/configs/tb10x_defconfig index 865fbc19ef03..6e396a9ddb8b 100644 --- a/arch/arc/configs/tb10x_defconfig +++ b/arch/arc/configs/tb10x_defconfig @@ -61,7 +61,6 @@ CONFIG_SERIAL_8250_DW=y CONFIG_I2C=y # CONFIG_I2C_COMPAT is not set CONFIG_I2C_DESIGNWARE_CORE=y -CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_GPIO_SYSFS=y # CONFIG_HWMON is not set # CONFIG_USB_SUPPORT is not set -- cgit v1.2.3 From 1d0e25c1ddf2063c499264fb2ba0fa6a3e4f8a00 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Sat, 27 Jun 2026 12:57:25 +0200 Subject: udmabuf: Ensure to perform cache synchronisation in begin_cpu_udmabuf() The message of commit 504e2b4ab97a ("dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning") says: > The CPU sync at map/unmap time is also redundant for udmabuf: > begin_cpu_udmabuf() and end_cpu_udmabuf() already perform explicit > cache synchronization via dma_sync_sgtable_for_cpu/device() when CPU > access is requested through the dma-buf interface. This, however, does not apply to the first time begin_cpu_udmabuf() is called on an udmabuf, in which case the implementation previously relied on get_sg_table() to perform the cache synchronisation. Ensure to call dma_sync_sgtable_for_cpu() in that case as well. Fixes: 504e2b4ab97a ("dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning") Signed-off-by: Robert Mader Reviewed-by: Mikhail Gavrilov Signed-off-by: Vivek Kasireddy Link: https://patch.msgid.link/20260627105725.9083-1-robert.mader@collabora.com --- drivers/dma-buf/udmabuf.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index bced421c0d65..08f57bc1294d 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -224,21 +224,22 @@ static int begin_cpu_udmabuf(struct dma_buf *buf, { struct udmabuf *ubuf = buf->priv; struct device *dev = ubuf->device->this_device; - int ret = 0; if (!ubuf->sg) { ubuf->sg = get_sg_table(dev, buf, direction); if (IS_ERR(ubuf->sg)) { + int ret; + ret = PTR_ERR(ubuf->sg); ubuf->sg = NULL; + return ret; } else { ubuf->sg_dir = direction; } - } else { - dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction); } - return ret; + dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction); + return 0; } static int end_cpu_udmabuf(struct dma_buf *buf, -- cgit v1.2.3 From adea84ee6cdea611146c4251d3c1616f5a09feca Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 3 Jul 2026 23:01:12 -0700 Subject: 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; } -- cgit v1.2.3 From 47915e855fb38b42133e31ba917d99565f862154 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Fri, 10 Jul 2026 22:04:49 +0530 Subject: 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; -- cgit v1.2.3 From 7410d11460eb90d6c9281162ccc6a128534d897d Mon Sep 17 00:00:00 2001 From: James Raphael Tiovalen Date: Sun, 5 Jul 2026 19:36:29 +0800 Subject: 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); -- cgit v1.2.3 From 3f1f755366687d051174739fb99f7d560202f60b Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Mon, 6 Jul 2026 09:44:10 +0000 Subject: 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; + + 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; - a->nla_len = sfa->actions_len - st_offset; + 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; + + err = add_nested_action_end(*sfa, action_start); + if (err) + goto err_free; - add_nested_action_end(*sfa, action_start); - 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, 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; + + err = add_nested_action_end(*sfa, greater_acts_start); + if (err) + goto err_free; - add_nested_action_end(*sfa, nested_acts_start); - 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, 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) -- cgit v1.2.3 From 2808a3963988f00081594f1c4a2758733839eaac Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 26 Jun 2026 01:19:19 +0200 Subject: rust: zerocopy: update to v0.8.52 Update our vendored copy of `zerocopy` (and `zerocopy-derive`) to v0.8.52. Most SPDX identifiers have been added upstream at our request [1] (without parentheses -- supporting them is an issue on the kernel side, but it does already reduce our differences). The CSS one for `rustdoc` was added too [2], but will be picked up in a later version. For `zerocopy`, enable `--cfg no_fp_fmt_parse`, which was added at our request to avoid our local workaround [3]. This means one less difference, thus indicate so in our `README.md`. For `zerocopy-derive`, enable `--cfg zerocopy_unstable_linux`. This allows us to use `#[derive(zerocopy_derive::most_traits)]`, a new feature upstream added for us [4]. We noticed a minor doc render bug [5], which will be fixed for a future version too. The following script may be used to check for the remaining differences: for path in $(cd rust/zerocopy-derive/ && find . -type f ! -name README.md); do curl --silent --show-error --location \ https://github.com/google/zerocopy/raw/v0.8.52/zerocopy/zerocopy-derive/src/$path | git diff --no-index - rust/zerocopy-derive/$path && echo $path: OK done for path in $(cd rust/zerocopy/ && find . -type f ! -name README.md); do curl --silent --show-error --location \ https://github.com/google/zerocopy/raw/v0.8.52/zerocopy/$path | git diff --no-index - rust/zerocopy/$path && echo $path: OK done Cc: Joshua Liebow-Feeser Cc: Jack Wrenn Link: https://github.com/google/zerocopy/issues/3428 [1] Link: https://github.com/google/zerocopy/issues/3457 [2] Link: https://github.com/google/zerocopy/issues/3426 [3] Link: https://github.com/google/zerocopy/pull/3416 [4] Link: https://github.com/google/zerocopy/issues/3466 [5] Acked-by: Nicolas Schier Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20260625231919.692444-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- rust/Makefile | 16 +++++++-- rust/zerocopy-derive/README.md | 14 ++++---- rust/zerocopy-derive/derive/from_bytes.rs | 2 +- rust/zerocopy-derive/derive/into_bytes.rs | 2 +- rust/zerocopy-derive/derive/known_layout.rs | 3 +- rust/zerocopy-derive/derive/mod.rs | 20 ++++++----- rust/zerocopy-derive/derive/try_from_bytes.rs | 6 ++-- rust/zerocopy-derive/derive/unaligned.rs | 2 +- rust/zerocopy-derive/lib.rs | 36 +++++++++++++++++++- rust/zerocopy-derive/repr.rs | 2 +- rust/zerocopy-derive/util.rs | 34 ++++++++++++++----- rust/zerocopy/README.md | 15 ++++---- rust/zerocopy/src/byte_slice.rs | 2 +- rust/zerocopy/src/byteorder.rs | 49 ++++++++++++++++++++++----- rust/zerocopy/src/deprecated.rs | 2 +- rust/zerocopy/src/error.rs | 2 +- rust/zerocopy/src/impls.rs | 4 +-- rust/zerocopy/src/layout.rs | 2 +- rust/zerocopy/src/lib.rs | 13 ++++--- rust/zerocopy/src/macros.rs | 2 +- rust/zerocopy/src/pointer/inner.rs | 2 +- rust/zerocopy/src/pointer/invariant.rs | 2 +- rust/zerocopy/src/pointer/mod.rs | 2 +- rust/zerocopy/src/pointer/ptr.rs | 2 +- rust/zerocopy/src/pointer/transmute.rs | 2 +- rust/zerocopy/src/ref.rs | 2 +- rust/zerocopy/src/split_at.rs | 2 +- rust/zerocopy/src/util/macro_util.rs | 2 +- rust/zerocopy/src/util/macros.rs | 2 +- rust/zerocopy/src/util/mod.rs | 2 +- rust/zerocopy/src/wrappers.rs | 2 +- 31 files changed, 174 insertions(+), 76 deletions(-) diff --git a/rust/Makefile b/rust/Makefile index a870d1616c71..835cf10e1c0b 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -84,11 +84,15 @@ core-flags := \ --edition=$(core-edition) \ $(call cfgs-to-flags,$(core-cfgs)) +zerocopy-cfgs := \ + no_fp_fmt_parse + zerocopy-flags := \ - --cap-lints=allow + --cap-lints=allow \ + $(call cfgs-to-flags,$(zerocopy-cfgs)) zerocopy-envs := \ - CARGO_PKG_VERSION=0.8.50 + CARGO_PKG_VERSION=0.8.52 proc_macro2-cfgs := \ feature="proc-macro" \ @@ -128,11 +132,15 @@ syn-flags := \ --extern quote \ $(call cfgs-to-flags,$(syn-cfgs)) +zerocopy_derive-cfgs := \ + zerocopy_unstable_linux + zerocopy_derive-flags := \ --cap-lints=allow \ --extern proc_macro2 \ --extern quote \ - --extern syn + --extern syn \ + $(call cfgs-to-flags,$(zerocopy_derive-cfgs)) pin_init_internal-cfgs := \ kernel USE_RUSTC_FEATURES @@ -644,9 +652,11 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L rust-analyzer: $(Q)MAKEFLAGS= $(srctree)/scripts/generate_rust_analyzer.py \ --cfgs='core=$(core-cfgs)' $(core-edition) \ + --cfgs='zerocopy=$(zerocopy-cfgs)' \ --cfgs='proc_macro2=$(proc_macro2-cfgs)' \ --cfgs='quote=$(quote-cfgs)' \ --cfgs='syn=$(syn-cfgs)' \ + --cfgs='zerocopy_derive=$(zerocopy_derive-cfgs)' \ --cfgs='pin_init_internal=$(pin_init_internal-cfgs)' \ --cfgs='pin_init=$(pin_init-cfgs)' \ --envs='zerocopy=$(zerocopy-envs)' \ diff --git a/rust/zerocopy-derive/README.md b/rust/zerocopy-derive/README.md index 110f4a401778..2a3dcf1212af 100644 --- a/rust/zerocopy-derive/README.md +++ b/rust/zerocopy-derive/README.md @@ -1,14 +1,14 @@ # `zerocopy-derive` -These source files come from the Rust `zerocopy-derive` crate, version v0.8.50 -(released 2026-05-31), hosted in the +These source files come from the Rust `zerocopy-derive` crate, version v0.8.52 +(released 2026-06-09), hosted in the repository, licensed under "BSD-2-Clause OR Apache-2.0 OR MIT" and only -modified to add the SPDX license identifiers and to remove the generation of +modified to tweak the SPDX license identifiers and to remove the generation of non-ASCII identifiers. For copyright details, please see: - https://github.com/google/zerocopy/blob/v0.8.50/README.md?plain=1 - https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-BSD - https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-APACHE - https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-MIT + https://github.com/google/zerocopy/blob/v0.8.52/README.md?plain=1 + https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-BSD + https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-APACHE + https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-MIT diff --git a/rust/zerocopy-derive/derive/from_bytes.rs b/rust/zerocopy-derive/derive/from_bytes.rs index d693a63b7645..66d820f6ad4c 100644 --- a/rust/zerocopy-derive/derive/from_bytes.rs +++ b/rust/zerocopy-derive/derive/from_bytes.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// use proc_macro2::{Span, TokenStream}; use syn::{ parse_quote, Data, DataEnum, DataStruct, DataUnion, Error, Expr, ExprLit, ExprUnary, Lit, UnOp, diff --git a/rust/zerocopy-derive/derive/into_bytes.rs b/rust/zerocopy-derive/derive/into_bytes.rs index ad52a6b45d28..0103a78d087f 100644 --- a/rust/zerocopy-derive/derive/into_bytes.rs +++ b/rust/zerocopy-derive/derive/into_bytes.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{Data, DataEnum, DataStruct, DataUnion, Error, Type}; diff --git a/rust/zerocopy-derive/derive/known_layout.rs b/rust/zerocopy-derive/derive/known_layout.rs index fddffd167c82..d0c4cecfff15 100644 --- a/rust/zerocopy-derive/derive/known_layout.rs +++ b/rust/zerocopy-derive/derive/known_layout.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// use proc_macro2::TokenStream; use quote::quote; use syn::{parse_quote, Data, Error, Type}; @@ -87,7 +87,6 @@ fn derive_known_layout_for_repr_c_struct<'a>( }; let inner_extras = { - let leading_fields_tys = leading_fields_tys.clone(); let methods = make_methods(*trailing_field_ty); let (_, ty_generics, _) = ctx.ast.generics.split_for_impl(); diff --git a/rust/zerocopy-derive/derive/mod.rs b/rust/zerocopy-derive/derive/mod.rs index 665ba7da55a8..b3839fcf73c9 100644 --- a/rust/zerocopy-derive/derive/mod.rs +++ b/rust/zerocopy-derive/derive/mod.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// pub mod from_bytes; pub mod into_bytes; pub mod known_layout; @@ -15,8 +15,8 @@ use crate::{ util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, Trait}, }; -pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> TokenStream { - match &ctx.ast.data { +pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> Result { + Ok(match &ctx.ast.data { Data::Struct(strct) => { ImplBlockBuilder::new(ctx, strct, Trait::Immutable, FieldBounds::ALL_SELF).build() } @@ -26,7 +26,7 @@ pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> TokenStream { Data::Union(unn) => { ImplBlockBuilder::new(ctx, unn, Trait::Immutable, FieldBounds::ALL_SELF).build() } - } + }) } pub(crate) fn derive_hash(ctx: &Ctx, _top_level: Trait) -> Result { @@ -97,16 +97,20 @@ pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result {} Data::Enum(_) | Data::Union(_) => { - return Err(Error::new(Span::call_site(), "can only be applied to structs")); + return ctx + .error_or_skip(Error::new(Span::call_site(), "can only be applied to structs")); } }; if repr.get_packed().is_some() { - return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute")); + return ctx.error_or_skip(Error::new( + Span::call_site(), + "must not have #[repr(packed)] attribute", + )); } if !(repr.is_c() || repr.is_transparent()) { - return Err(Error::new( + return ctx.error_or_skip(Error::new( Span::call_site(), "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable", )); @@ -116,7 +120,7 @@ pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result TokenStream { data, Trait::ProjectField { variant_id: variant_id.clone(), - field: field.clone(), - field_id: field_id.clone(), + field, + field_id, invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)), }, FieldBounds::None, diff --git a/rust/zerocopy-derive/derive/unaligned.rs b/rust/zerocopy-derive/derive/unaligned.rs index d6dea0a11f1e..7c97d62e2dcb 100644 --- a/rust/zerocopy-derive/derive/unaligned.rs +++ b/rust/zerocopy-derive/derive/unaligned.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// use proc_macro2::{Span, TokenStream}; use syn::{Data, DataEnum, DataStruct, DataUnion, Error}; diff --git a/rust/zerocopy-derive/lib.rs b/rust/zerocopy-derive/lib.rs index c517ea7db1eb..88599e750894 100644 --- a/rust/zerocopy-derive/lib.rs +++ b/rust/zerocopy-derive/lib.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2019 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 @@ -129,6 +129,40 @@ derive!(ByteHash => derive_hash => crate::derive::derive_hash); derive!(ByteEq => derive_eq => crate::derive::derive_eq); derive!(SplitAt => derive_split_at => crate::derive::derive_split_at); +#[cfg_attr(zerocopy_unstable_linux, doc(hidden))] +#[proc_macro_derive(most_traits, attributes(zerocopy))] +pub fn most_traits(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = syn::parse_macro_input!(ts as DeriveInput); + let ctx = match Ctx::try_from_derive_input(ast) { + Ok(ctx) => ctx, + Err(e) => return e.into_compile_error().into(), + } + .skip_on_error(); + + // top-level traits for which to attempt a derive + let derives: [(fn(&Ctx, Trait) -> _, _); 6] = [ + (crate::derive::known_layout::derive, Trait::KnownLayout), + (crate::derive::derive_immutable, Trait::Immutable), + (crate::derive::from_bytes::derive_from_bytes, Trait::FromBytes), + (crate::derive::into_bytes::derive_into_bytes, Trait::IntoBytes), + (crate::derive::derive_split_at, Trait::SplitAt), + (crate::derive::unaligned::derive_unaligned, Trait::Unaligned), + ]; + + let mut tokens = proc_macro2::TokenStream::new(); + for (derive, t) in derives { + tokens.extend(derive(&ctx, t)) + } + + // We wrap in `const_block` as a backstop in case any derive fails + // to wrap its output in `const_block` (and thus fails to annotate) + // with the full set of `#[allow(...)]` attributes). + let ts = const_block([Some(tokens)]); + #[cfg(test)] + crate::util::testutil::check_hygiene(ts.clone()); + ts.into() +} + /// Deprecated: prefer [`FromZeros`] instead. #[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")] #[doc(hidden)] diff --git a/rust/zerocopy-derive/repr.rs b/rust/zerocopy-derive/repr.rs index 74fd376d9fda..1525e94302d1 100644 --- a/rust/zerocopy-derive/repr.rs +++ b/rust/zerocopy-derive/repr.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2019 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 diff --git a/rust/zerocopy-derive/util.rs b/rust/zerocopy-derive/util.rs index 5ba5228e2a44..5c5e9d3bdcb8 100644 --- a/rust/zerocopy-derive/util.rs +++ b/rust/zerocopy-derive/util.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2019 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 @@ -98,6 +98,11 @@ impl Ctx { } } + pub(crate) fn skip_on_error(mut self) -> Self { + self.skip_on_error = true; + self + } + pub(crate) fn core_path(&self) -> TokenStream { let zerocopy_crate = &self.zerocopy_crate; quote!(#zerocopy_crate::util::macro_util::core_reexport) @@ -106,20 +111,21 @@ impl Ctx { pub(crate) fn cfg_compile_error(&self) -> TokenStream { // By checking both during the compilation of the proc macro *and* in // the generated code, we ensure that `--cfg - // zerocopy_unstable_derive_on_error` need only be passed *either* when + // zerocopy_unstable_linux` need only be passed *either* when // compiling this crate *or* when compiling the user's crate. The former // is preferable, but in some situations (such as when cross-compiling // using `cargo build --target`), it doesn't get propagated to this // crate's build by default. - if cfg!(zerocopy_unstable_derive_on_error) { + if cfg!(zerocopy_unstable_linux) { quote!() } else if let Some(span) = self.on_error_span { let core = self.core_path(); - let error_message = "`on_error` is experimental; pass '--cfg zerocopy_unstable_derive_on_error' to enable"; + let error_message = + "`on_error` is experimental; pass '--cfg zerocopy_unstable_linux' to enable"; quote::quote_spanned! {span=> #[allow(unused_attributes, unexpected_cfgs)] const _: () = { - #[cfg(not(zerocopy_unstable_derive_on_error))] + #[cfg(not(zerocopy_unstable_linux))] #core::compile_error!(#error_message); }; } @@ -612,6 +618,20 @@ impl<'a> ImplBlockBuilder<'a> { } }; + let zerocopy_bounds = + field_type_bounds + .into_iter() + .chain(padding_check_bound) + .chain(self_bounds) + .map(|bound| { + if self.ctx.skip_on_error { + parse_quote!(for<'zc> #bound) + } else { + bound.clone() + } + }) + .collect::>(); + let bounds = self .ctx .ast @@ -621,9 +641,7 @@ impl<'a> ImplBlockBuilder<'a> { .map(|where_clause| where_clause.predicates.iter()) .into_iter() .flatten() - .chain(field_type_bounds.iter()) - .chain(padding_check_bound.iter()) - .chain(self_bounds.iter()); + .chain(zerocopy_bounds.iter()); // The parameters with trait bounds, but without type defaults. let mut params: Vec<_> = self diff --git a/rust/zerocopy/README.md b/rust/zerocopy/README.md index 99e6cad0e26c..712b0317df25 100644 --- a/rust/zerocopy/README.md +++ b/rust/zerocopy/README.md @@ -1,14 +1,13 @@ # `zerocopy` -These source files come from the Rust `zerocopy` crate, version v0.8.50 -(released 2026-05-31), hosted in the +These source files come from the Rust `zerocopy` crate, version v0.8.52 +(released 2026-06-09), hosted in the repository, licensed under "BSD-2-Clause OR Apache-2.0 OR MIT" and only -modified to add the SPDX license identifiers and to remove `Display` -for `f32` and `f64`. +modified to tweak the SPDX license identifiers. For copyright details, please see: - https://github.com/google/zerocopy/blob/v0.8.50/README.md?plain=1 - https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-BSD - https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-APACHE - https://github.com/google/zerocopy/blob/v0.8.50/LICENSE-MIT + https://github.com/google/zerocopy/blob/v0.8.52/README.md?plain=1 + https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-BSD + https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-APACHE + https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-MIT diff --git a/rust/zerocopy/src/byte_slice.rs b/rust/zerocopy/src/byte_slice.rs index a5ded4a18b39..b7f85098dbc4 100644 --- a/rust/zerocopy/src/byte_slice.rs +++ b/rust/zerocopy/src/byte_slice.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2024 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/byteorder.rs b/rust/zerocopy/src/byteorder.rs index 8f70048f1eb0..ecf95e38eebd 100644 --- a/rust/zerocopy/src/byteorder.rs +++ b/rust/zerocopy/src/byteorder.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2019 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 @@ -164,6 +164,42 @@ pub type BE = BigEndian; /// A type alias for [`LittleEndian`]. pub type LE = LittleEndian; +macro_rules! impl_dbg_trait { + ($name:ident, $native:ident) => { + impl Debug for $name { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // This results in a format like "U16(42)". + f.debug_tuple(stringify!($name)).field(&self.get()).finish() + } + } + }; +} + +macro_rules! impl_dbg_traits { + ($name:ident, $native:ident, "floating point number") => { + #[cfg(not(no_fp_fmt_parse))] + impl_dbg_trait!($name, $native); + + #[cfg(no_fp_fmt_parse)] + impl Debug for $name { + #[inline] + fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { + panic!("floating point support is turned off"); + } + } + }; + ($name:ident, $native:ident, "unsigned integer") => { + impl_dbg_traits!($name, $native, @all_types); + }; + ($name:ident, $native:ident, "signed integer") => { + impl_dbg_traits!($name, $native, @all_types); + }; + ($name:ident, $native:ident, @all_types) => { + impl_dbg_trait!($name, $native); + }; +} + macro_rules! impl_fmt_trait { ($name:ident, $native:ident, $trait:ident) => { impl $trait for $name { @@ -177,6 +213,8 @@ macro_rules! impl_fmt_trait { macro_rules! impl_fmt_traits { ($name:ident, $native:ident, "floating point number") => { + #[cfg(not(no_fp_fmt_parse))] + impl_fmt_trait!($name, $native, Display); }; ($name:ident, $native:ident, "unsigned integer") => { impl_fmt_traits!($name, $native, @all_types); @@ -687,16 +725,9 @@ example of how it can be used for parsing UDP packets. } } + impl_dbg_traits!($name, $native, $number_kind); impl_fmt_traits!($name, $native, $number_kind); impl_ops_traits!($name, $native, $number_kind); - - impl Debug for $name { - #[inline] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - // This results in a format like "U16(42)". - f.debug_tuple(stringify!($name)).field(&self.get()).finish() - } - } }; } diff --git a/rust/zerocopy/src/deprecated.rs b/rust/zerocopy/src/deprecated.rs index 24bafbf9adeb..59ddd35c77c6 100644 --- a/rust/zerocopy/src/deprecated.rs +++ b/rust/zerocopy/src/deprecated.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2024 The Fuchsia Authors // // Licensed under the 2-Clause BSD License README.md +// (cd .. && cargo -q run --manifest-path tools/Cargo.toml -p generate-readme) > README.md //! ***Fast, safe, compile error. Pick two.*** @@ -174,7 +174,7 @@ //! //! [Miri]: https://github.com/rust-lang/miri //! [Kani]: https://github.com/model-checking/kani -//! [soundness policy]: https://github.com/google/zerocopy/blob/main/POLICIES.md#soundness +//! [soundness policy]: https://github.com/google/zerocopy/blob/main/zerocopy/POLICIES.md#soundness //! //! # Relationship to Project Safe Transmute //! @@ -203,7 +203,7 @@ //! //! See our [MSRV policy]. //! -//! [MSRV policy]: https://github.com/google/zerocopy/blob/main/POLICIES.md#msrv +//! [MSRV policy]: https://github.com/google/zerocopy/blob/main/zerocopy/POLICIES.md#msrv //! //! # Changelog //! @@ -435,6 +435,9 @@ const _: () = { WARNING }; +#[doc(hidden)] +#[cfg(all(any(feature = "derive", test), zerocopy_unstable_linux))] +pub use zerocopy_derive::most_traits; /// Implements [`KnownLayout`]. /// /// This derive analyzes various aspects of a type's layout that are needed for @@ -2832,7 +2835,7 @@ pub unsafe trait TryFromBytes { /// ``` /// /// [`try_mut_from_bytes`]: TryFromBytes::try_mut_from_bytes - /// + /// #[doc = codegen_header!("h5", "try_mut_from_bytes_with_elems")] /// /// See [`TryFromBytes::try_ref_from_bytes_with_elems`](#method.try_ref_from_bytes_with_elems.codegen). diff --git a/rust/zerocopy/src/macros.rs b/rust/zerocopy/src/macros.rs index b801d86a8fa6..ec67c03a44fc 100644 --- a/rust/zerocopy/src/macros.rs +++ b/rust/zerocopy/src/macros.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2024 The Fuchsia Authors // // Licensed under the 2-Clause BSD License , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/pointer/invariant.rs b/rust/zerocopy/src/pointer/invariant.rs index 1802d23563db..7ff0d43dad5e 100644 --- a/rust/zerocopy/src/pointer/invariant.rs +++ b/rust/zerocopy/src/pointer/invariant.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2024 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/pointer/mod.rs b/rust/zerocopy/src/pointer/mod.rs index 3461f7f5ca80..d6eacc52febe 100644 --- a/rust/zerocopy/src/pointer/mod.rs +++ b/rust/zerocopy/src/pointer/mod.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2023 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/pointer/ptr.rs b/rust/zerocopy/src/pointer/ptr.rs index b7c4ea56d2b2..7213f6f4a04e 100644 --- a/rust/zerocopy/src/pointer/ptr.rs +++ b/rust/zerocopy/src/pointer/ptr.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2023 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/pointer/transmute.rs b/rust/zerocopy/src/pointer/transmute.rs index a534984b70d3..ef9836698203 100644 --- a/rust/zerocopy/src/pointer/transmute.rs +++ b/rust/zerocopy/src/pointer/transmute.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2025 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/ref.rs b/rust/zerocopy/src/ref.rs index 860066d75196..e49f2a887ffa 100644 --- a/rust/zerocopy/src/ref.rs +++ b/rust/zerocopy/src/ref.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2024 The Fuchsia Authors // // Licensed under the 2-Clause BSD License , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/util/macros.rs b/rust/zerocopy/src/util/macros.rs index 43e4fd64ee15..7e63e3a54fc4 100644 --- a/rust/zerocopy/src/util/macros.rs +++ b/rust/zerocopy/src/util/macros.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2023 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/util/mod.rs b/rust/zerocopy/src/util/mod.rs index d6d4c6c2fcd9..f8affbbd336c 100644 --- a/rust/zerocopy/src/util/mod.rs +++ b/rust/zerocopy/src/util/mod.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2023 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 diff --git a/rust/zerocopy/src/wrappers.rs b/rust/zerocopy/src/wrappers.rs index 266aec25fa58..1a8cf2b41d55 100644 --- a/rust/zerocopy/src/wrappers.rs +++ b/rust/zerocopy/src/wrappers.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT - +// // Copyright 2023 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 -- cgit v1.2.3 From 425e10586cf12b86af076d1746bb87b0bb3f18a1 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Thu, 9 Jul 2026 23:13:11 +0200 Subject: rust: zerocopy: update to v0.8.54 Update our vendored copy of `zerocopy` (and `zerocopy-derive`) to v0.8.54. It is a very small delta from v0.8.52, and most importantly it resolves the unexpected lack of inlining [1] which triggered a modpost error under `CONFIG_CC_OPTIMIZE_FOR_SIZE=y` reported by Alexandre using Gary's suggestion [2]: ERROR: modpost: "_RNvMNtCs5wX7wwEUCR9_8zerocopy6layoutNtB2_8SizeInfo24try_to_nonzero_elem_size" [drivers/gpu/nova-core.ko] undefined! ERROR: modpost: "_RNvNtCs5wX7wwEUCR9_8zerocopy4util18padding_needed_for" [drivers/gpu/nova-core.ko] undefined! It also resolves `most_traits` being unexpectedly documented [3] that I reported and adds a missing SPDX license identifier [4] that I requested to match the kernel version. The following script may be used to check for the remaining differences: for path in $(cd rust/zerocopy-derive/ && find . -type f ! -name README.md); do curl --silent --show-error --location \ https://github.com/google/zerocopy/raw/v0.8.54/zerocopy/zerocopy-derive/src/$path | git diff --no-index - rust/zerocopy-derive/$path && echo $path: OK done for path in $(cd rust/zerocopy/ && find . -type f ! -name README.md); do curl --silent --show-error --location \ https://github.com/google/zerocopy/raw/v0.8.54/zerocopy/$path | git diff --no-index - rust/zerocopy/$path && echo $path: OK done Cc: Joshua Liebow-Feeser Cc: Jack Wrenn Reported-by: Alexandre Courbot Closes: https://lore.kernel.org/rust-for-linux/20260708-zerocopy-export-v1-1-2bfc355853c6@nvidia.com/ [1] Suggested-by: Gary Guo Link: https://lore.kernel.org/rust-for-linux/DJT6235B3DOV.222XR5O6VHG4M@garyguo.net/ [2] Link: https://github.com/google/zerocopy/issues/3466 [3] Link: https://github.com/google/zerocopy/issues/3457 [4] Reviewed-by: Alice Ryhl Tested-by: Alexandre Courbot Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260709211311.142544-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- rust/Makefile | 2 +- rust/zerocopy-derive/README.md | 12 ++++++------ rust/zerocopy-derive/lib.rs | 2 +- rust/zerocopy/README.md | 12 ++++++------ rust/zerocopy/rustdoc/style.css | 1 - rust/zerocopy/src/byteorder.rs | 1 + rust/zerocopy/src/layout.rs | 2 ++ rust/zerocopy/src/lib.rs | 1 - rust/zerocopy/src/util/mod.rs | 6 ++++++ 9 files changed, 23 insertions(+), 16 deletions(-) diff --git a/rust/Makefile b/rust/Makefile index 835cf10e1c0b..627ed79dc6f5 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -92,7 +92,7 @@ zerocopy-flags := \ $(call cfgs-to-flags,$(zerocopy-cfgs)) zerocopy-envs := \ - CARGO_PKG_VERSION=0.8.52 + CARGO_PKG_VERSION=0.8.54 proc_macro2-cfgs := \ feature="proc-macro" \ diff --git a/rust/zerocopy-derive/README.md b/rust/zerocopy-derive/README.md index 2a3dcf1212af..d62c79804342 100644 --- a/rust/zerocopy-derive/README.md +++ b/rust/zerocopy-derive/README.md @@ -1,14 +1,14 @@ # `zerocopy-derive` -These source files come from the Rust `zerocopy-derive` crate, version v0.8.52 -(released 2026-06-09), hosted in the +These source files come from the Rust `zerocopy-derive` crate, version v0.8.54 +(released 2026-07-08), hosted in the repository, licensed under "BSD-2-Clause OR Apache-2.0 OR MIT" and only modified to tweak the SPDX license identifiers and to remove the generation of non-ASCII identifiers. For copyright details, please see: - https://github.com/google/zerocopy/blob/v0.8.52/README.md?plain=1 - https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-BSD - https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-APACHE - https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-MIT + https://github.com/google/zerocopy/blob/v0.8.54/README.md?plain=1 + https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-BSD + https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-APACHE + https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-MIT diff --git a/rust/zerocopy-derive/lib.rs b/rust/zerocopy-derive/lib.rs index 88599e750894..d387de368367 100644 --- a/rust/zerocopy-derive/lib.rs +++ b/rust/zerocopy-derive/lib.rs @@ -129,7 +129,7 @@ derive!(ByteHash => derive_hash => crate::derive::derive_hash); derive!(ByteEq => derive_eq => crate::derive::derive_eq); derive!(SplitAt => derive_split_at => crate::derive::derive_split_at); -#[cfg_attr(zerocopy_unstable_linux, doc(hidden))] +#[cfg_attr(not(zerocopy_unstable_linux), doc(hidden))] #[proc_macro_derive(most_traits, attributes(zerocopy))] pub fn most_traits(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse_macro_input!(ts as DeriveInput); diff --git a/rust/zerocopy/README.md b/rust/zerocopy/README.md index 712b0317df25..3d11a6502cf0 100644 --- a/rust/zerocopy/README.md +++ b/rust/zerocopy/README.md @@ -1,13 +1,13 @@ # `zerocopy` -These source files come from the Rust `zerocopy` crate, version v0.8.52 -(released 2026-06-09), hosted in the +These source files come from the Rust `zerocopy` crate, version v0.8.54 +(released 2026-07-08), hosted in the repository, licensed under "BSD-2-Clause OR Apache-2.0 OR MIT" and only modified to tweak the SPDX license identifiers. For copyright details, please see: - https://github.com/google/zerocopy/blob/v0.8.52/README.md?plain=1 - https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-BSD - https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-APACHE - https://github.com/google/zerocopy/blob/v0.8.52/LICENSE-MIT + https://github.com/google/zerocopy/blob/v0.8.54/README.md?plain=1 + https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-BSD + https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-APACHE + https://github.com/google/zerocopy/blob/v0.8.54/LICENSE-MIT diff --git a/rust/zerocopy/rustdoc/style.css b/rust/zerocopy/rustdoc/style.css index 414348964af2..d2d55ad2e689 100644 --- a/rust/zerocopy/rustdoc/style.css +++ b/rust/zerocopy/rustdoc/style.css @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT */ - /* Copyright 2026 The Fuchsia Authors diff --git a/rust/zerocopy/src/byteorder.rs b/rust/zerocopy/src/byteorder.rs index ecf95e38eebd..c761d5728320 100644 --- a/rust/zerocopy/src/byteorder.rs +++ b/rust/zerocopy/src/byteorder.rs @@ -100,6 +100,7 @@ mod private { #[allow(missing_copy_implementations, missing_debug_implementations)] #[doc(hidden)] +#[derive(PartialEq)] pub enum Order { BigEndian, LittleEndian, diff --git a/rust/zerocopy/src/layout.rs b/rust/zerocopy/src/layout.rs index 942d35db8957..b1fa0cd436db 100644 --- a/rust/zerocopy/src/layout.rs +++ b/rust/zerocopy/src/layout.rs @@ -71,6 +71,8 @@ impl SizeInfo { /// Attempts to create a `SizeInfo` from `Self` in which `elem_size` is a /// `NonZeroUsize`. If `elem_size` is 0, returns `None`. #[allow(unused)] + #[cfg_attr(not(zerocopy_inline_always), inline)] + #[cfg_attr(zerocopy_inline_always, inline(always))] const fn try_to_nonzero_elem_size(&self) -> Option> { Some(match *self { SizeInfo::Sized { size } => SizeInfo::Sized { size }, diff --git a/rust/zerocopy/src/lib.rs b/rust/zerocopy/src/lib.rs index 700fece1b728..572f0563fe2f 100644 --- a/rust/zerocopy/src/lib.rs +++ b/rust/zerocopy/src/lib.rs @@ -435,7 +435,6 @@ const _: () = { WARNING }; -#[doc(hidden)] #[cfg(all(any(feature = "derive", test), zerocopy_unstable_linux))] pub use zerocopy_derive::most_traits; /// Implements [`KnownLayout`]. diff --git a/rust/zerocopy/src/util/mod.rs b/rust/zerocopy/src/util/mod.rs index f8affbbd336c..02fd4ed62741 100644 --- a/rust/zerocopy/src/util/mod.rs +++ b/rust/zerocopy/src/util/mod.rs @@ -150,6 +150,8 @@ pub(crate) fn validate_aligned_to(t: T) -> Result<(), Alignment // Ensures that we add the minimum required padding. kani::ensures(|&p| p < align.get()), )] +#[cfg_attr(not(zerocopy_inline_always), inline)] +#[cfg_attr(zerocopy_inline_always, inline(always))] pub(crate) const fn padding_needed_for(len: usize, align: NonZeroUsize) -> usize { #[cfg(kani)] #[kani::proof_for_contract(padding_needed_for)] @@ -251,6 +253,8 @@ pub(crate) const fn round_down_to_next_multiple_of_alignment( n & mask } +#[cfg_attr(not(zerocopy_inline_always), inline)] +#[cfg_attr(zerocopy_inline_always, inline(always))] pub(crate) const fn max(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize { if a.get() < b.get() { b @@ -259,6 +263,8 @@ pub(crate) const fn max(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize { } } +#[cfg_attr(not(zerocopy_inline_always), inline)] +#[cfg_attr(zerocopy_inline_always, inline(always))] pub(crate) const fn min(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize { if a.get() > b.get() { b -- cgit v1.2.3 From 5a81c35c3b18cd59ded56171a6a9f643b92a6759 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 10 Jul 2026 19:32:52 +0200 Subject: objtool/rust: add one more `noreturn` Rust function for Rust 1.99.0 Starting with Rust 1.99.0 (expected 2026-10-01), under `CONFIG_RUST_DEBUG_ASSERTIONS=y`, `objtool` may report: rust/kernel.o: warning: objtool: _R..._6kernel12module_param9set_paramaEB4_() falls through to next function _R..._6kernel12module_param9set_paramhEB4_() (and many others) due to calls to the `noreturn` symbol [1]: core::panicking::panic_null_reference_constructed Thus add the mangled one to the list so that `objtool` knows it is actually `noreturn`. See commit 56d680dd23c3 ("objtool/rust: list `noreturn` Rust functions") for more details. Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Cc: Josh Poimboeuf Cc: Peter Zijlstra Cc: Petr Pavlu Link: https://github.com/rust-lang/rust/pull/158796 [1] Reported-by: Alice Ryhl Closes: https://lore.kernel.org/rust-for-linux/alEBInX9gD1M5NAr@google.com/ Reviewed-by: Alice Ryhl Tested-by: Alice Ryhl Link: https://patch.msgid.link/20260710173252.191781-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- tools/objtool/check.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 10b18cf9c360..f03dd59e7fca 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -206,6 +206,7 @@ static bool is_rust_noreturn(const struct symbol *func) str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt") || str_ends_with(func->name, "_4core9panicking19assert_failed_inner") || str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference") || + str_ends_with(func->name, "_4core9panicking32panic_null_reference_constructed") || str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference") || str_ends_with(func->name, "_7___rustc17rust_begin_unwind") || strstr(func->name, "_4core9panicking13assert_failed") || -- cgit v1.2.3 From 4b22d0801fadfcae2e106e6ba32e49439c7c7ebf Mon Sep 17 00:00:00 2001 From: Genjian Zhang Date: Sat, 11 Jul 2026 18:05:26 +0800 Subject: 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); -- cgit v1.2.3 From 1aa4e2ed7caafbbbedff89fb226a982413469baf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 23 Jun 2026 09:30:51 +0200 Subject: dt-bindings: clock: Replace bouncing emails Replace permanently bouncing email addresses (550 5.1.1 Recipient address rejected) of Adam Skladowski, Chanho Park, Anusha Rao and Sireesh Kodali. There are no new messages from them via other email addresses, so drop them permanently. Add Alim Akhtar to Samsung ExynosAutov9 SoC clocks, because he looks at other Samsung clock hardware and drivers. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260623073050.36262-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml | 2 -- Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml | 1 - Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml | 1 - Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml | 2 +- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml index ced3118c8580..bbc38b7a018a 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8953.yaml @@ -7,8 +7,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm Global Clock & Reset Controller on MSM8937, MSM8940, MSM8953 and SDM439 maintainers: - - Adam Skladowski - - Sireesh Kodali - Barnabas Czeman description: | diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml index 27ae9938febc..03fe290476be 100644 --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml @@ -8,7 +8,6 @@ title: Qualcomm Global Clock & Reset Controller on IPQ9574 maintainers: - Bjorn Andersson - - Anusha Rao description: | Qualcomm global clock control module provides the clocks, resets and power diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml index 7ff4ff3587ca..a35bbc7b1924 100644 --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-nsscc.yaml @@ -8,7 +8,6 @@ title: Qualcomm Networking Sub System Clock & Reset Controller on IPQ9574 and IP maintainers: - Bjorn Andersson - - Anusha Rao description: | Qualcomm networking sub system clock control module provides the clocks, diff --git a/Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml index 32f39e543b36..21e307d59775 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynosautov9-clock.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Samsung Exynos Auto v9 SoC clock controller maintainers: - - Chanho Park + - Alim Akhtar - Chanwoo Choi - Krzysztof Kozlowski - Sylwester Nawrocki -- cgit v1.2.3 From 1ecb29b084616ca423ca45ec6a6365da53411516 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Tue, 9 Jun 2026 20:32:47 -0700 Subject: x86/cpu: Remove Makefile rule for removed UMC CPU support Support for UMC CPUs was removed in 7d328c5de43a ("x86/cpu: Remove CPU_SUP_UMC_32 support"), but a Makefile rule for the support code remained. Remove it. Fixes: 7d328c5de43a ("x86/cpu: Remove CPU_SUP_UMC_32 support") Signed-off-by: Ethan Nelson-Moore Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Ahmed S. Darwish Link: https://patch.msgid.link/20260610033252.164571-1-enelsonmoore@gmail.com --- arch/x86/kernel/cpu/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index d2e8a849f180..5b1070ec85d9 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -46,7 +46,6 @@ obj-$(CONFIG_CPU_SUP_HYGON) += hygon.o obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o obj-$(CONFIG_CPU_SUP_CENTAUR) += centaur.o obj-$(CONFIG_CPU_SUP_TRANSMETA_32) += transmeta.o -obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o obj-$(CONFIG_CPU_SUP_ZHAOXIN) += zhaoxin.o obj-$(CONFIG_CPU_SUP_VORTEX_32) += vortex.o -- cgit v1.2.3 From d130041a7b96f79cd4c7079a6c2431a6db4c9619 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 21 Jun 2026 19:00:10 +0200 Subject: x86/boot: Reject too long acpi_rsdp= values cmdline_find_option() returns the full length of the parsed acpi_rsdp= value. get_cmdline_acpi_rsdp() then silently truncates values which do not fit in the val[] buffer. Prevent boot_kstrtoul() from parsing a truncated value and then the kernel from silently using the wrong RSDP address, see discussion in Link:. Issue a warning so that the user is aware that s/he supplied a malformed value and can get feedback instead of silent crashes. [ bp: Make commit message more precise. ] Fixes: 3c98e71b42a7 ("x86/boot: Add "acpi_rsdp=" early parsing") Signed-off-by: Thorsten Blum Signed-off-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/20260617130417.36651-4-thorsten.blum@linux.dev --- arch/x86/boot/compressed/acpi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c index f196b1d1ddf8..aed27604c11f 100644 --- a/arch/x86/boot/compressed/acpi.c +++ b/arch/x86/boot/compressed/acpi.c @@ -184,10 +184,15 @@ static unsigned long get_cmdline_acpi_rsdp(void) char val[MAX_ADDR_LEN] = { }; int ret; - ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN); + ret = cmdline_find_option("acpi_rsdp", val, sizeof(val)); if (ret < 0) return 0; + if (ret >= sizeof(val)) { + warn("acpi_rsdp= value too long; ignoring"); + return 0; + } + if (boot_kstrtoul(val, 16, &addr)) return 0; #endif -- cgit v1.2.3 From 9202ee546b0cd71004eed7598546efe4660097da Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Thu, 9 Jul 2026 17:24:07 +0200 Subject: 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); } -- cgit v1.2.3 From f20d61c22bcaf172d6790b6500e3838e532e71c8 Mon Sep 17 00:00:00 2001 From: Jaewon Yang Date: Mon, 13 Jul 2026 02:11:47 +0900 Subject: 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) -- cgit v1.2.3 From a13c140cc289c0b7b3770bce5b3ad42ab35074aa Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 12 Jul 2026 14:16:39 -0700 Subject: 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* -- cgit v1.2.3 From d04a179085c262c9ed577d0a4cbc6482ff1fd9a3 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 11 Jun 2026 13:42:26 -0500 Subject: scsi: target: Bound PR-OUT TransportID parsing to the received buffer core_scsi3_decode_spec_i_port() and core_scsi3_emulate_register_and_move() hand the raw PERSISTENT RESERVE OUT parameter buffer to target_parse_pr_out_transport_id() without telling it how many bytes are valid. For an iSCSI TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() locates the ",i,0x" ISID separator with an unbounded strstr() (and on the error path prints the name with a further unbounded "%s"). An initiator can submit a TransportID whose iSCSI name contains neither a ",i,0x" substring nor a NUL terminator, filling the parameter list to its end, so the scan runs off the end of the buffer. When the parameter list spans more than one page the buffer is a multi-page vmap (transport_kmap_data_sg()), so the over-read walks into the trailing vmalloc guard page and oopses (KASAN: vmalloc-out-of-bounds in strstr). It is reachable by any fabric that delivers a PR OUT to a device exported through an iSCSI TPG, including a guest via vhost-scsi. Pass the number of received bytes down to the parser and validate the iSCSI TransportID's own self-described length (ADDITIONAL LENGTH + 4) once, up front: reject it if it is below the spc4r17 minimum or larger than the received buffer, then bound the separator search, the ISID walk and the name copy by that length. This is the length check the callers already perform after the parse (core_scsi3_decode_spec_i_port() compares tid_len against tpdl, core_scsi3_emulate_register_and_move() validates it against data_length), moved ahead of the scan. Also drop the unbounded "%s" of the unterminated name. Add per-format explicit name-length checks before copying into i_str, rather than silently truncating with min_t: for FORMAT CODE 00b reject if the descriptor body (tid_len - 4 bytes) cannot fit in i_str[TRANSPORT_IQN_LEN]; for FORMAT CODE 01b reject if the name portion (from &buf[4] up to the separator) cannot fit. Both checks make the bounds intent explicit at each format branch. While here, also reject a FORMAT CODE 01b TransportID whose ",i,0x" separator sits at the very end of the descriptor: that leaves an empty ISID and points the returned port nexus pointer at buf + tid_len, one past the descriptor, which the registration code (__core_scsi3_locate_pr_reg(), __core_scsi3_alloc_registration()) then dereferences as the ISID string -- the same over-read of the parameter buffer for a malformed descriptor. Fixes: c66ac9db8d4a ("[SCSI] target: Add LIO target core v4.0.0-rc6") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: John Garry Reviewed-by: David Disseldorp Link: https://patch.msgid.link/20260611-b4-disp-9f20739e-v6-1-f6630e2aae44@proton.me Signed-off-by: Martin K. Petersen --- drivers/target/target_core_fabric_lib.c | 89 +++++++++++++++++++++++++-------- drivers/target/target_core_internal.h | 3 +- drivers/target/target_core_pr.c | 4 +- 3 files changed, 73 insertions(+), 23 deletions(-) diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 87c5d26a5089..2853b95b2c59 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -290,13 +290,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str) static bool iscsi_parse_pr_out_transport_id( struct se_portal_group *se_tpg, char *buf, + u32 buf_len, u32 *out_tid_len, char **port_nexus_ptr, char *i_str) { char *p; + u32 tid_len; int i; - u8 format_code = (buf[0] & 0xc0); + u8 format_code; + + /* + * The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL + * LENGTH) must be present before any of it can be parsed. + */ + if (buf_len < 4) + return false; + + format_code = buf[0] & 0xc0; /* * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6: * @@ -316,15 +327,17 @@ static bool iscsi_parse_pr_out_transport_id( return false; } /* - * If the caller wants the TransportID Length, we set that value for the - * entire iSCSI Tarnsport ID now. + * Reconstruct the self-described TransportID length from the ADDITIONAL + * LENGTH field plus the 4-byte header. Reject it if it is below the + * spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least + * 20) or if it runs past the bytes actually received, so that every + * access below stays inside the TransportID. */ - if (out_tid_len) { - /* The shift works thanks to integer promotion rules */ - *out_tid_len = get_unaligned_be16(&buf[2]); - /* Add four bytes for iSCSI Transport ID header */ - *out_tid_len += 4; - } + tid_len = get_unaligned_be16(&buf[2]) + 4; + if (tid_len < 24 || tid_len > buf_len) + return false; + if (out_tid_len) + *out_tid_len = tid_len; /* * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator @@ -332,16 +345,32 @@ static bool iscsi_parse_pr_out_transport_id( * format. */ if (format_code == 0x40) { - p = strstr(&buf[4], ",i,0x"); + p = strnstr(&buf[4], ",i,0x", tid_len - 4); if (!p) { - pr_err("Unable to locate \",i,0x\" separator" - " for Initiator port identifier: %s\n", - &buf[4]); + pr_err("Unable to locate \",i,0x\" separator in iSCSI TransportID\n"); + return false; + } + /* + * The iSCSI name runs from &buf[4] up to the separator; reject it + * if it cannot fit in i_str[TRANSPORT_IQN_LEN]. + */ + if (p - &buf[4] >= TRANSPORT_IQN_LEN) { + pr_err("iSCSI Initiator port name too long in TransportID\n"); return false; } *p = '\0'; /* Terminate iSCSI Name */ p += 5; /* Skip over ",i,0x" separator */ + /* + * The ISID must follow the separator. A ",i,0x" sitting at the + * very end of the TransportID leaves no ISID and would point the + * port nexus at buf + tid_len, i.e. past the descriptor, which + * the registration code then reads as the ISID string. + */ + if (p >= buf + tid_len) { + pr_err("Missing ISID in iSCSI Initiator port TransportID\n"); + return false; + } *port_nexus_ptr = p; /* * Go ahead and do the lower case conversion of the received @@ -349,7 +378,7 @@ static bool iscsi_parse_pr_out_transport_id( * for comparison against the running iSCSI session's ISID from * iscsi_target.c:lio_sess_get_initiator_sid() */ - for (i = 0; i < 12; i++) { + for (i = 0; i < 12 && p < buf + tid_len; i++) { /* * The first ISCSI INITIATOR SESSION ID field byte * containing an ASCII null character terminates the @@ -367,10 +396,22 @@ static bool iscsi_parse_pr_out_transport_id( *p = tolower(*p); p++; } - } else + strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN); + } else { *port_nexus_ptr = NULL; - - strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN); + /* + * FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The + * declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN]. + * (For 01b the same tid_len bound would be over-restrictive: the + * descriptor also carries the separator and ISID, so a legal + * <=223-byte name gives tid_len up to 244.) + */ + if (tid_len - 4 >= TRANSPORT_IQN_LEN) { + pr_err("iSCSI Initiator port name too long in TransportID\n"); + return false; + } + strscpy(i_str, &buf[4], tid_len - 4); + } return true; } @@ -420,8 +461,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, } bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, - char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str) + char *buf, u32 buf_len, u32 *out_tid_len, + char **port_nexus_ptr, char *i_str) { + /* + * The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI + * format is variable and bounds itself against buf_len below. + */ + if (tpg->proto_id != SCSI_PROTOCOL_ISCSI && buf_len < 24) + return false; + switch (tpg->proto_id) { case SCSI_PROTOCOL_SAS: /* @@ -440,8 +489,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, sbp_parse_pr_out_transport_id(buf, i_str); break; case SCSI_PROTOCOL_ISCSI: - return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len, - port_nexus_ptr, i_str); + return iscsi_parse_pr_out_transport_id(tpg, buf, buf_len, + out_tid_len, port_nexus_ptr, i_str); default: pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id); return false; diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 763e6d26e187..f0886ea29034 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -104,7 +104,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, struct t10_pr_registration *pr_reg, int *format_code, unsigned char *buf); bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, - char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str); + char *buf, u32 buf_len, u32 *out_tid_len, + char **port_nexus_ptr, char *i_str); /* target_core_hba.c */ struct se_hba *core_alloc_hba(const char *, u32, u32); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 11790f2c5d80..0b19997c2edd 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1573,7 +1573,7 @@ core_scsi3_decode_spec_i_port( iport_ptr = NULL; tid_found = target_parse_pr_out_transport_id(tmp_tpg, - ptr, &tid_len, &iport_ptr, i_str); + ptr, tpdl, &tid_len, &iport_ptr, i_str); if (!tid_found) continue; /* @@ -3285,7 +3285,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } tid_found = target_parse_pr_out_transport_id(dest_se_tpg, - &buf[24], &tmp_tid_len, &iport_ptr, initiator_str); + &buf[24], tid_len, &tmp_tid_len, &iport_ptr, initiator_str); if (!tid_found) { pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate" " initiator_str from Transport ID\n"); -- cgit v1.2.3 From fda6a1f3c3d7047b5ce5654487649c2daa738bfc Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Wed, 10 Jun 2026 04:22:48 +0000 Subject: scsi: target: core: Fix iSCSI ISID use-after-free in REGISTER AND MOVE core_scsi3_emulate_pro_register_and_move() maps the PERSISTENT RESERVE OUT parameter list with transport_kmap_data_sg() and parses the destination TransportID with target_parse_pr_out_transport_id(). For an iSCSI TransportID (FORMAT CODE 01b), iscsi_parse_pr_out_transport_id() returns the ISID in iport_ptr as a raw pointer into that mapped buffer. The function then unmaps the buffer with transport_kunmap_data_sg() before dereferencing iport_ptr in strcmp(), __core_scsi3_locate_pr_reg() and core_scsi3_alloc_registration(). When the parameter list spans more than one page (PARAMETER LIST LENGTH > 4096), transport_kmap_data_sg() uses vmap() and transport_kunmap_data_sg() does vunmap(), so the kernel virtual address backing iport_ptr is torn down and every subsequent dereference is a use-after-free read of the unmapped region. Keep the parameter list mapped until iport_ptr is no longer needed: drop the early transport_kunmap_data_sg() and unmap once on the success path, right before returning. The error paths already unmap through the existing "if (buf) transport_kunmap_data_sg(cmd)" at the out: label, which now runs on every post-map error exit because buf is no longer cleared early. Only reads of the mapping happen while spinlocks are held; the map and unmap calls remain outside any lock. The sibling caller core_scsi3_decode_spec_i_port() already uses the buffer before unmapping it and is left unchanged. Fixes: 4949314c7283 ("target: Allow control CDBs with data > 1 page") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Reviewed-by: John Garry Reviewed-by: David Disseldorp Link: https://patch.msgid.link/20260610042245.35473-1-hexlabsecurity@proton.me Signed-off-by: Martin K. Petersen --- drivers/target/target_core_pr.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 0b19997c2edd..1a77b4bb62b0 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -3293,9 +3293,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } - transport_kunmap_data_sg(cmd); - buf = NULL; - pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s" " %s\n", dest_tf_ops->fabric_name, (iport_ptr != NULL) ? "port" : "device", initiator_str, (iport_ptr != NULL) ? @@ -3532,6 +3529,11 @@ after_iport_check: core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl); core_scsi3_put_pr_reg(dest_pr_reg); + /* + * iport_ptr aliases the PR-OUT parameter list mapped above, so the + * buffer is unmapped only here on success (and at out: on error). + */ + transport_kunmap_data_sg(cmd); return 0; out: if (buf) -- cgit v1.2.3 From dccf3b1798b70f94e958b3d00b83010399e6fb05 Mon Sep 17 00:00:00 2001 From: David Jeffery Date: Mon, 15 Jun 2026 13:46:30 -0400 Subject: scsi: core: wake eh reliably when using scsi_schedule_eh Drivers which use the scsi_schedule_eh function to run the error handler currently risk the error handler thread never waking once all commands are timed out or inactive. There is no enforced memory order between setting the host into error recovery state and counting busy commands. This can result in a race with scsi_dec_host_busy where neither CPU sees both conditions of all commands inactive and the host error state to request waking the error handler. To fix this, run the scsi_schedule_eh's scsi_eh_wakeup from a new work item which will use rcu to ensure scsi_schedule_eh's call to scsi_host_busy will occur after the error state is globally visible and will be seen by any current scsi_dec_host_busy callers. Fixes: 6eb045e092ef ("scsi: core: avoid host-wide host_busy counter for scsi_mq") Signed-off-by: David Jeffery Link: https://patch.msgid.link/20260615174630.11492-1-djeffery@redhat.com Signed-off-by: Martin K. Petersen --- drivers/scsi/hosts.c | 2 ++ drivers/scsi/scsi_error.c | 22 +++++++++++++++++++++- drivers/scsi/scsi_priv.h | 1 + include/scsi/scsi_host.h | 3 +++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index e047747d4ecf..46cc8e3c79a2 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -357,6 +357,7 @@ static void scsi_host_dev_release(struct device *dev) /* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */ rcu_barrier(); + cancel_work_sync(&shost->eh_work); if (shost->tmf_work_q) destroy_workqueue(shost->tmf_work_q); if (shost->ehandler) @@ -422,6 +423,7 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); + INIT_WORK(&shost->eh_work, scsi_rcu_eh_wakeup); index = ida_alloc(&host_index_ida, GFP_KERNEL); if (index < 0) { diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 147127fb4db9..453a2232452d 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -73,6 +73,26 @@ void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy) } } +void scsi_rcu_eh_wakeup(struct work_struct *work) +{ + struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work); + unsigned long flags; + unsigned int busy; + + /* + * Ensure any running scsi_dec_host_busy has completed its rcu section + * so changes to host state and host_eh_scheduled are visible to all + * future calls of scsi_dec_host_busy + */ + synchronize_rcu(); + + busy = scsi_host_busy(shost); + + spin_lock_irqsave(shost->host_lock, flags); + scsi_eh_wakeup(shost, busy); + spin_unlock_irqrestore(shost->host_lock, flags); +} + /** * scsi_schedule_eh - schedule EH for SCSI host * @shost: SCSI host to invoke error handling on. @@ -88,7 +108,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost) if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 || scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) { shost->host_eh_scheduled++; - scsi_eh_wakeup(shost, scsi_host_busy(shost)); + queue_work(shost->tmf_work_q, &shost->eh_work); } spin_unlock_irqrestore(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 37e5601be2b8..3dbf2ca59536 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -91,6 +91,7 @@ extern enum blk_eh_timer_return scsi_timeout(struct request *req); extern int scsi_error_handler(void *host); extern enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *cmd); extern void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy); +extern void scsi_rcu_eh_wakeup(struct work_struct *work); extern void scsi_eh_scmd_add(struct scsi_cmnd *); void scsi_eh_ready_devs(struct Scsi_Host *shost, struct list_head *work_q, diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 7e2011830ba4..f6b286fa59f2 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -750,6 +750,9 @@ struct Scsi_Host { */ struct device *dma_dev; + /* Used for an rcu-synchronizing eh wakeup */ + struct work_struct eh_work; + /* Delay for runtime autosuspend */ int rpm_autosuspend_delay; -- cgit v1.2.3 From 9cb2d5291dbfe7bed565ead3337047dee9ed1064 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Mon, 22 Jun 2026 15:58:44 +0800 Subject: scsi: elx: efct: Fix I/O leak on unsupported additional CDB efct_dispatch_fcp_cmd() allocates an efct_io before dispatching an unsolicited FCP command. If the command has an unsupported additional CDB, the function returns -EIO before handing the IO to the SCSI layer. Free the allocated IO before returning from this error path. Fixes: f45ae6aac0a0 ("scsi: elx: efct: Unsolicited FC frame processing routines") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Daniel Wagner Link: https://patch.msgid.link/20260622075844.832871-1-haoxiang_li2024@163.com Signed-off-by: Martin K. Petersen --- drivers/scsi/elx/efct/efct_unsol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/elx/efct/efct_unsol.c b/drivers/scsi/elx/efct/efct_unsol.c index e6addab66a60..6a871a59c909 100644 --- a/drivers/scsi/elx/efct/efct_unsol.c +++ b/drivers/scsi/elx/efct/efct_unsol.c @@ -385,6 +385,7 @@ efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq) if (cmnd->fc_flags & FCP_CFL_LEN_MASK) { efc_log_err(efct, "Additional CDB not supported\n"); + efct_scsi_io_free(io); return -EIO; } /* -- cgit v1.2.3 From 2c007acf7b31c39c08ce4959451ad00b19be4c1f Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 13:30:37 +0800 Subject: scsi: elx: efct: Fix refcount leak in efct_hw_io_abort() When efct_hw_reqtag_alloc() fails in efct_hw_io_abort(), the error path returns -ENOSPC without releasing the reference obtained via kref_get_unless_zero() earlier in the function. All other error paths correctly drop the reference. This causes a permanent reference leak on the io_to_abort object. Additionally, the abort_in_progress flag is left set to true on this path, which means future abort attempts for the same I/O will immediately return -EINPROGRESS even though the abort was never submitted, effectively blocking recovery. Fix this by adding the missing kref_put() call and reset abort_in_progress to false, matching the cleanup done in the efct_hw_wq_write() failure path below. Cc: stable@vger.kernel.org Fixes: 63de51327a64 ("scsi: elx: efct: Hardware I/O and SGL initialization") Signed-off-by: WenTao Liang Reviewed-by: Daniel Wagner Link: https://patch.msgid.link/20260611053037.63756-1-vulab@iscas.ac.cn Signed-off-by: Martin K. Petersen --- drivers/scsi/elx/efct/efct_hw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c index 1838032f6486..b79c6a7ea791 100644 --- a/drivers/scsi/elx/efct/efct_hw.c +++ b/drivers/scsi/elx/efct/efct_hw.c @@ -1997,6 +1997,8 @@ efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort, wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort); if (!wqcb) { efc_log_err(hw->os, "can't allocate request tag\n"); + io_to_abort->abort_in_progress = false; + kref_put(&io_to_abort->ref, io_to_abort->release); return -ENOSPC; } -- cgit v1.2.3 From e166bafc483e927150cb9b5f286c9191ea0df84e Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 23 Jun 2026 00:00:28 +0800 Subject: scsi: hpsa: Fix DMA mapping leak on IOACCEL2 reset path If phys_disk->in_reset is set, the function returns directly without undoing the resources acquired for the command. Add the missing error cleanup by unmapping the IOACCEL2 SG chain block when needed, unmapping the SCSI command, and dropping the outstanding IOACCEL command count before returning. Fixes: c5dfd106414f ("scsi: hpsa: correct device resets") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Acked-by: Don Brace Link: https://patch.msgid.link/20260622160028.1240496-1-haoxiang_li2024@163.com Signed-off-by: Martin K. Petersen --- drivers/scsi/hpsa.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index a1b116cd4723..8edad1830abe 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -5017,6 +5017,10 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, if (phys_disk->in_reset) { cmd->result = DID_RESET << 16; + atomic_dec(&phys_disk->ioaccel_cmds_out); + scsi_dma_unmap(cmd); + if (use_sg > h->ioaccel_maxsg) + hpsa_unmap_ioaccel2_sg_chain_block(h, cp); return -1; } -- cgit v1.2.3 From 50246d57233ad3b3c5dab99001a84d93e3e3d8f0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:47:52 +0200 Subject: usb: udc: pxa: fix error handling As Dan Carpenter points out, my recent change makes subtle changes to the error handling that were not intended. Move the warning print up so it does not get skipped in case of an error, but handle -EPROBE_DEFER properly now. Change the devm_gpiod_get() to the _optional variant, which is in line with the intended behavior and the DT binding, though this did not work previously. Reported-by: Dan Carpenter Link: https://lore.kernel.org/linux-usb/ag6-xhfFjb5NpXQz@stanley.mountain/ Fixes: 25bd55f46032 ("usb: udc: pxa: remove unused platform_data") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260526104810.3906090-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pxa27x_udc.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 640f81988c04..df5cca43afbe 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2374,9 +2374,10 @@ static int pxa_udc_probe(struct platform_device *pdev) struct pxa_udc *udc = &memory; int retval = 0; - udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); + udc->gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_ASIS); if (IS_ERR(udc->gpiod)) - return PTR_ERR(udc->gpiod); + return dev_err_probe(&pdev->dev, PTR_ERR(udc->gpiod), + "Couldn't find or request D+ gpio\n"); udc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->regs)) @@ -2395,11 +2396,6 @@ static int pxa_udc_probe(struct platform_device *pdev) udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); } - if (IS_ERR(udc->gpiod)) { - dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n", - PTR_ERR(udc->gpiod)); - return PTR_ERR(udc->gpiod); - } if (udc->gpiod) gpiod_direction_output(udc->gpiod, 0); -- cgit v1.2.3 From 79e2d75725c85607f8a9d87ae9cace62a19f767d Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Sat, 27 Jun 2026 18:41:53 +0800 Subject: usb: gadget: f_tcm: synchronize delayed set_alt with teardown The f_tcm set_alt() path defers endpoint setup to a work item and completes the delayed status response from process context. The delayed work uses f_tcm private state and may complete the setup request after disconnect or function teardown has already moved on. Cancel and drain the delayed set_alt work when the function is unbound or freed. For disable paths, which are reached under the composite device lock, use a small state machine and a non-sleeping cancellation path instead of cancel_work_sync(). If the work is already running, mark it cancelled and let the worker own the cleanup; otherwise tcm_disable() can cancel the queued work and clean up immediately. Also serialize the final delayed-status completion with the cancellation check while holding the composite device lock. This prevents a disconnect from clearing delayed_status while the worker is about to complete the control request. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in tcm_delayed_set_alt+0x6c/0xef0 Call Trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? tcm_delayed_set_alt+0x6c/0xef0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? tcm_delayed_set_alt+0x6c/0xef0 kasan_report+0xe0/0x110 ? tcm_delayed_set_alt+0x6c/0xef0 tcm_delayed_set_alt+0x6c/0xef0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? process_one_work+0x4cb/0xb90 ? rcu_is_watching+0x20/0x50 ? tcm_delayed_set_alt+0x9/0xef0 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_tcm_delayed_set_alt+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 Allocated by task 544: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0x8f/0xa0 tcm_alloc+0x68/0x180 usb_get_function+0x36/0x60 config_usb_cfg_link+0x125/0x1b0 configfs_symlink+0x322/0x890 vfs_symlink+0xc2/0x270 filename_symlinkat+0x295/0x2f0 __x64_sys_symlinkat+0x62/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 661: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x43/0x70 kfree+0x2f9/0x530 config_usb_cfg_unlink+0x173/0x1e0 configfs_unlink+0x1fa/0x340 vfs_unlink+0x15c/0x510 filename_unlinkat+0x2ba/0x450 __x64_sys_unlinkat+0x63/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: c52661d60f63 ("usb-gadget: Initial merge of target module for UASP + BOT") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang Link: https://patch.msgid.link/20260627104153.3822495-1-zzzccc427@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_tcm.c | 192 ++++++++++++++++++++++++++++++------ drivers/usb/gadget/function/tcm.h | 13 +++ 2 files changed, 177 insertions(+), 28 deletions(-) diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 34d9f49e9987..b3fa5a17fd2d 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -2363,31 +2363,158 @@ ep_fail: return -ENOTSUPP; } -struct guas_setup_wq { - struct work_struct work; - struct f_uas *fu; - unsigned int alt; -}; +static void tcm_cleanup_old_alt(struct f_uas *fu) +{ + if (fu->flags & USBG_IS_UAS) + uasp_cleanup_old_alt(fu); + else if (fu->flags & USBG_IS_BOT) + bot_cleanup_old_alt(fu); + fu->flags = 0; +} + +static void tcm_delayed_set_alt_done(struct f_uas *fu) +{ + unsigned long flags; + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; + fu->delayed_set_alt_cancel = false; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); +} + +static bool tcm_delayed_set_alt_cancelled(struct f_uas *fu) +{ + bool cancelled; + unsigned long flags; + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + cancelled = fu->delayed_set_alt_cancel; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + + return cancelled; +} + +static bool tcm_complete_delayed_status(struct f_uas *fu) +{ + struct usb_composite_dev *cdev = fu->function.config->cdev; + struct usb_request *req = cdev->req; + unsigned long cdev_flags; + bool cancelled; + int ret; + + spin_lock_irqsave(&cdev->lock, cdev_flags); + spin_lock(&fu->delayed_set_alt_lock); + cancelled = fu->delayed_set_alt_cancel; + if (!cancelled) { + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; + fu->delayed_set_alt_cancel = false; + } + spin_unlock(&fu->delayed_set_alt_lock); + + if (cancelled) { + spin_unlock_irqrestore(&cdev->lock, cdev_flags); + return false; + } + + if (cdev->delayed_status == 0) { + WARN(cdev, "%s: Unexpected call\n", __func__); + } else if (--cdev->delayed_status == 0) { + req->length = 0; + req->context = cdev; + ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (ret == 0) { + cdev->setup_pending = true; + } else { + req->status = 0; + req->complete(cdev->gadget->ep0, req); + } + } + + spin_unlock_irqrestore(&cdev->lock, cdev_flags); + + return true; +} + +static bool tcm_cancel_delayed_set_alt(struct f_uas *fu) +{ + bool cleanup = false; + bool cancel = false; + unsigned long flags; + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + switch (fu->delayed_set_alt_state) { + case USBG_DELAYED_SET_ALT_IDLE: + cleanup = true; + break; + case USBG_DELAYED_SET_ALT_QUEUED: + case USBG_DELAYED_SET_ALT_RUNNING: + fu->delayed_set_alt_cancel = true; + cancel = true; + break; + } + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + + if (cancel && cancel_work(&fu->delayed_set_alt)) { + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + if (fu->delayed_set_alt_state == USBG_DELAYED_SET_ALT_QUEUED) { + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; + fu->delayed_set_alt_cancel = false; + cleanup = true; + } + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + } + + return cleanup; +} + +static void tcm_cancel_delayed_set_alt_sync(struct f_uas *fu) +{ + unsigned long flags; + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) + fu->delayed_set_alt_cancel = true; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + + cancel_work_sync(&fu->delayed_set_alt); + + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; + fu->delayed_set_alt_cancel = false; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); +} static void tcm_delayed_set_alt(struct work_struct *wq) { - struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq, - work); - struct f_uas *fu = work->fu; - int alt = work->alt; + struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt); + unsigned long flags; + unsigned int alt; - kfree(work); + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_QUEUED) { + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + return; + } + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_RUNNING; + alt = fu->delayed_alt; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); - if (fu->flags & USBG_IS_BOT) - bot_cleanup_old_alt(fu); - if (fu->flags & USBG_IS_UAS) - uasp_cleanup_old_alt(fu); + tcm_cleanup_old_alt(fu); + + if (tcm_delayed_set_alt_cancelled(fu)) + goto out_done; if (alt == USB_G_ALT_INT_BBB) bot_set_alt(fu); else if (alt == USB_G_ALT_INT_UAS) uasp_set_alt(fu); - usb_composite_setup_continue(fu->function.config->cdev); + + if (tcm_complete_delayed_status(fu)) + return; + + tcm_cleanup_old_alt(fu); +out_done: + tcm_delayed_set_alt_done(fu); } static int tcm_get_alt(struct usb_function *f, unsigned intf) @@ -2413,15 +2540,20 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return -EOPNOTSUPP; if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) { - struct guas_setup_wq *work; + unsigned long flags; - work = kmalloc_obj(*work, GFP_ATOMIC); - if (!work) - return -ENOMEM; - INIT_WORK(&work->work, tcm_delayed_set_alt); - work->fu = fu; - work->alt = alt; - schedule_work(&work->work); + spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); + if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) { + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, + flags); + return -EBUSY; + } + fu->delayed_alt = alt; + fu->delayed_set_alt_cancel = false; + fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_QUEUED; + spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + + schedule_work(&fu->delayed_set_alt); return USB_GADGET_DELAYED_STATUS; } return -EOPNOTSUPP; @@ -2431,11 +2563,8 @@ static void tcm_disable(struct usb_function *f) { struct f_uas *fu = to_f_uas(f); - if (fu->flags & USBG_IS_UAS) - uasp_cleanup_old_alt(fu); - else if (fu->flags & USBG_IS_BOT) - bot_cleanup_old_alt(fu); - fu->flags = 0; + if (tcm_cancel_delayed_set_alt(fu)) + tcm_cleanup_old_alt(fu); } static int tcm_setup(struct usb_function *f, @@ -2583,11 +2712,16 @@ static void tcm_free(struct usb_function *f) { struct f_uas *tcm = to_f_uas(f); + tcm_cancel_delayed_set_alt_sync(tcm); kfree(tcm); } static void tcm_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_uas *fu = to_f_uas(f); + + tcm_cancel_delayed_set_alt_sync(fu); + tcm_cleanup_old_alt(fu); usb_free_all_descriptors(f); } @@ -2620,6 +2754,8 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi) fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; fu->tpg = tpg_instances[i].tpg; + INIT_WORK(&fu->delayed_set_alt, tcm_delayed_set_alt); + spin_lock_init(&fu->delayed_set_alt_lock); hash_init(fu->stream_hash); mutex_unlock(&tpg_instances_lock); diff --git a/drivers/usb/gadget/function/tcm.h b/drivers/usb/gadget/function/tcm.h index 009974d81d66..e1d5a9391612 100644 --- a/drivers/usb/gadget/function/tcm.h +++ b/drivers/usb/gadget/function/tcm.h @@ -3,6 +3,7 @@ #define __TARGET_USB_GADGET_H__ #include +#include /* #include */ #include #include @@ -29,6 +30,12 @@ enum { #define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS +enum { + USBG_DELAYED_SET_ALT_IDLE = 0, + USBG_DELAYED_SET_ALT_QUEUED, + USBG_DELAYED_SET_ALT_RUNNING, +}; + struct tcm_usbg_nexus { struct se_session *tvn_se_sess; }; @@ -132,6 +139,12 @@ struct f_uas { #define USBG_BOT_CMD_PEND (1 << 4) #define USBG_BOT_WEDGED (1 << 5) + struct work_struct delayed_set_alt; + spinlock_t delayed_set_alt_lock; /* protects delayed_set_alt_* */ + unsigned int delayed_alt; + unsigned int delayed_set_alt_state; + bool delayed_set_alt_cancel; + struct usbg_cdb cmd[USBG_NUM_CMDS]; struct usb_ep *ep_in; struct usb_ep *ep_out; -- cgit v1.2.3 From b70dc75e85ba968b7b76eebfe5d63000080b875b Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Tue, 30 Jun 2026 00:50:04 +0500 Subject: usb: gadget: uvc: clamp SEND_RESPONSE length to the response buffer uvc_send_response() builds the UVC control response from a user-supplied struct uvc_request_data: req->length = min_t(unsigned int, uvc->event_length, data->length); ... memcpy(req->buf, data->data, req->length); req->length is clamped to uvc->event_length, which is taken from the host control request wLength (up to UVC_MAX_REQUEST_SIZE, 64), and to data->length, which comes from the UVCIOC_SEND_RESPONSE ioctl and is only checked for being negative. The source buffer data->data is only 60 bytes, so a response with uvc->event_length and data->length both greater than 60 makes memcpy() read past the end of data->data. Clamp req->length to sizeof(data->data) as well. Fixes: a5eaaa1f33e7 ("usb: gadget: uvc: use capped length value") Cc: stable Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260629195004.148405-1-meatuni001@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc_v4l2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 514e5930b9ca..dfa0521a243a 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -200,6 +200,8 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) return usb_ep_set_halt(cdev->gadget->ep0); req->length = min_t(unsigned int, uvc->event_length, data->length); + if (req->length > sizeof(data->data)) + req->length = sizeof(data->data); req->zero = data->length < uvc->event_length; memcpy(req->buf, data->data, req->length); -- cgit v1.2.3 From 1fc50f1ecde39feb4fccdaf4bc71aa6c0eb25c49 Mon Sep 17 00:00:00 2001 From: Xincheng Zhang Date: Tue, 30 Jun 2026 09:26:48 +0800 Subject: usb: xhci-pci: Limit VIA VL805 DMA addressing to 36 bits The VIA VL805/806 xHCI controller advertises AC64, but fails to handle DMA addresses at or above 0x1000000000. On systems with large amounts of RAM, this can cause USB device failures when the controller is given DMA addresses beyond its usable address width. Do not use XHCI_NO_64BIT_SUPPORT for this controller. That quirk clears the cached AC64 capability and limits DMA to 32 bits, causing unnecessary bouncing for addresses between 4GiB and 64GiB and hiding the controller's real AC64 capability from code that may need to distinguish register access width from usable DMA address width. Track the usable DMA address width separately from the AC64 capability. Initialize the generic xhci->dma_mask_bits field to 64 and let PCI quirks reduce it for controllers with narrower DMA support. Set VIA VL805/806 to 36 bits so the DMA API only hands it addresses in the range it can handle while keeping HCCPARAMS1.AC64 visible. Cc: stable Signed-off-by: Xincheng Zhang Link: https://patch.msgid.link/20260630-xhci-via-dma-fix-v3-1-690dcb8cf75a@ultrarisc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 1 + drivers/usb/host/xhci.c | 15 ++++++++++----- drivers/usb/host/xhci.h | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 039c26b241d0..6b3fcba44b08 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -448,6 +448,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) { xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_TRB_OVERFETCH; + xhci->dma_mask_bits = 36; } if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f44ccee5fa07..091c82ca8ee2 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5466,6 +5466,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + xhci->dma_mask_bits = 64; xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS); xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); /* xhci-plat or xhci-pci might have set max_interrupters already */ @@ -5515,12 +5516,16 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->quirks & XHCI_NO_64BIT_SUPPORT) xhci->hcc_params &= ~BIT(0); - /* Set dma_mask and coherent_dma_mask to 64-bits, - * if xHC supports 64-bit addressing */ + /* + * Set dma_mask and coherent_dma_mask to 64-bits if xHC supports + * 64-bit addressing, unless a controller-specific quirk callback + * limits the usable address width. + */ if ((xhci->hcc_params & HCC_64BIT_ADDR) && - !dma_set_mask(dev, DMA_BIT_MASK(64))) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + !dma_set_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits))) { + xhci_dbg(xhci, "Enabling %u-bit DMA addresses.\n", + xhci->dma_mask_bits); + dma_set_coherent_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits)); } else { /* * This is to avoid error in cases where a 32-bit USB diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index d02046a573e4..2d3941b5e1e3 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1526,6 +1526,7 @@ struct xhci_hcd { /* imod_interval in ns (I * 250ns) */ u32 imod_interval; u32 page_size; + unsigned int dma_mask_bits; /* MSI-X/MSI vectors */ int nvecs; /* optional clocks */ -- cgit v1.2.3 From 6b874d00c466e73c6448a89856407fe46b2f50e4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 2 Jul 2026 16:15:33 +0200 Subject: USB: gadget: fsl-udc: fix device name leak on probe failure The gadget device name is set by UDC core when registering the gadget and must not be set before to avoid leaking the name in intermediate error paths (e.g. on dma pool creation failure). Fixes: eab35c4e6d95 ("usb: gadget: fsl_udc_core: let udc-core manage gadget->dev") Cc: stable Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260702141536.90887-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_udc_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 600ce8cc0fef..8c5b2f42ff44 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2474,7 +2474,6 @@ static int fsl_udc_probe(struct platform_device *pdev) udc_controller->gadget.name = driver_name; /* Setup gadget.dev and register with kernel */ - dev_set_name(&udc_controller->gadget.dev, "gadget"); udc_controller->gadget.dev.of_node = pdev->dev.of_node; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) -- cgit v1.2.3 From 29a142d3e8b35ebc9e0bcc78f4bc26c9b6a9ac0b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 2 Jul 2026 16:15:34 +0200 Subject: USB: gadget: snps-udc: fix device name leak on probe failure The gadget device name is set by UDC core when registering the gadget and must not be set before to avoid leaking the name in intermediate error paths (e.g. when detecting an older chip revision). Fixes: 12ad0fcaf2fb ("usb: gadget: amd5536udc: let udc-core manage gadget->dev") Cc: stable Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260702141536.90887-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/snps_udc_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index 0e0db68e0b27..d506f9d92bca 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -3133,7 +3133,6 @@ int udc_probe(struct udc *dev) /* device struct setup */ dev->gadget.ops = &udc_ops; - dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.name = name; dev->gadget.max_speed = USB_SPEED_HIGH; -- cgit v1.2.3 From c4dd150fceab281496acb3a643ae712aacb74864 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 2 Jul 2026 16:15:35 +0200 Subject: USB: gadget: fsl-udc: fix dev_printk() device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A change replacing custom printk() macros with dev_printk() incorrectly used the gadget struct device instead of the controller struct device (including for messages printed before the gadget device name has been initialised). Switch to using the controller platform device with dev_printk() so that the controller device and driver names are included in log messages as expected. Fixes: 6025f20f16c2 ("usb: gadget: fsl-udc: Replace custom log wrappers by dev_{err,warn,dbg,vdbg}") Cc: stable Cc: Uwe Kleine-König Signed-off-by: Johan Hovold Acked-by: Uwe Kleine-König Link: https://patch.msgid.link/20260702141536.90887-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_udc_core.c | 92 ++++++++++++++++------------------- drivers/usb/gadget/udc/fsl_usb2_udc.h | 1 + 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 8c5b2f42ff44..c4761933ec86 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -184,7 +184,7 @@ __acquires(ep->udc->lock) usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep)); if (status && (status != -ESHUTDOWN)) - dev_vdbg(&udc->gadget.dev, "complete %s req %p stat %d len %u/%u\n", + dev_vdbg(udc->dev, "complete %s req %p stat %d len %u/%u\n", ep->ep.name, &req->req, status, req->req.actual, req->req.length); @@ -286,7 +286,7 @@ static int dr_controller_setup(struct fsl_udc *udc) timeout = jiffies + FSL_UDC_RESET_TIMEOUT; while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) { if (time_after(jiffies, timeout)) { - dev_err(&udc->gadget.dev, "udc reset timeout!\n"); + dev_err(udc->dev, "udc reset timeout!\n"); return -ETIMEDOUT; } cpu_relax(); @@ -309,7 +309,7 @@ static int dr_controller_setup(struct fsl_udc *udc) tmp &= USB_EP_LIST_ADDRESS_MASK; fsl_writel(tmp, &dr_regs->endpointlistaddr); - dev_vdbg(&udc->gadget.dev, + dev_vdbg(udc->dev, "vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x\n", udc->ep_qh, (int)tmp, fsl_readl(&dr_regs->endpointlistaddr)); @@ -500,7 +500,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; break; default: - dev_vdbg(&udc->gadget.dev, "error ep type is %d\n", ep_type); + dev_vdbg(udc->dev, "error ep type is %d\n", ep_type); return; } if (zlt) @@ -613,7 +613,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, spin_unlock_irqrestore(&udc->lock, flags); retval = 0; - dev_vdbg(&udc->gadget.dev, "enabled %s (ep%d%s) maxpacket %d\n", + dev_vdbg(udc->dev, "enabled %s (ep%d%s) maxpacket %d\n", ep->ep.name, ep->ep.desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", max); @@ -634,13 +634,8 @@ static int fsl_ep_disable(struct usb_ep *_ep) int ep_num; ep = container_of(_ep, struct fsl_ep, ep); - if (!_ep || !ep->ep.desc) { - /* - * dev_vdbg(&udc->gadget.dev, "%s not enabled\n", - * _ep ? ep->ep.name : NULL); - */ + if (!_ep || !ep->ep.desc) return -EINVAL; - } /* disable ep on controller */ ep_num = ep_index(ep); @@ -664,7 +659,7 @@ static int fsl_ep_disable(struct usb_ep *_ep) ep->stopped = 1; spin_unlock_irqrestore(&udc->lock, flags); - dev_vdbg(&udc->gadget.dev, "disabled %s OK\n", _ep->name); + dev_vdbg(udc->dev, "disabled %s OK\n", _ep->name); return 0; } @@ -724,9 +719,6 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) { u32 temp, bitmask, tmp_stat; - /* dev_vdbg(&udc->gadget.dev, "QH addr Register 0x%8x\n", dr_regs->endpointlistaddr); - dev_vdbg(&udc->gadget.dev, "ep_qh[%d] addr is 0x%8x\n", i, (u32)&(ep->udc->ep_qh[i])); */ - bitmask = ep_is_in(ep) ? (1 << (ep_index(ep) + 16)) : (1 << (ep_index(ep))); @@ -813,7 +805,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, *is_last = 0; if ((*is_last) == 0) - dev_vdbg(&udc_controller->gadget.dev, "multi-dtd request!\n"); + dev_vdbg(udc_controller->dev, "multi-dtd request!\n"); /* Fill in the transfer size; set active bit */ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); @@ -825,7 +817,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, mb(); - dev_vdbg(&udc_controller->gadget.dev, "length = %d address= 0x%x\n", *length, (int)*dma); + dev_vdbg(udc_controller->dev, "length = %d address= 0x%x\n", *length, (int)*dma); return dtd; } @@ -876,11 +868,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* catch various bogus parameters */ if (!_req || !req->req.complete || !req->req.buf || !list_empty(&req->queue)) { - dev_vdbg(&udc->gadget.dev, "%s, bad params\n", __func__); + dev_vdbg(udc->dev, "%s, bad params\n", __func__); return -EINVAL; } if (unlikely(!ep->ep.desc)) { - dev_vdbg(&udc->gadget.dev, "%s, bad ep\n", __func__); + dev_vdbg(udc->dev, "%s, bad ep\n", __func__); return -EINVAL; } if (usb_endpoint_xfer_isoc(ep->ep.desc)) { @@ -1040,7 +1032,7 @@ static int fsl_ep_set_halt(struct usb_ep *_ep, int value) udc->ep0_dir = 0; } out: - dev_vdbg(&udc->gadget.dev, "%s %s halt stat %d\n", ep->ep.name, + dev_vdbg(udc->dev, "%s %s halt stat %d\n", ep->ep.name, value ? "set" : "clear", status); return status; @@ -1109,7 +1101,7 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep) /* Wait until flush complete */ while (fsl_readl(&dr_regs->endptflush)) { if (time_after(jiffies, timeout)) { - dev_err(&udc_controller->gadget.dev, + dev_err(udc_controller->dev, "ep flush timeout\n"); return; } @@ -1182,7 +1174,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active) udc = container_of(gadget, struct fsl_udc, gadget); spin_lock_irqsave(&udc->lock, flags); - dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active)); + dev_vdbg(udc->dev, "VBUS %s\n", str_on_off(is_active)); udc->vbus_active = (is_active != 0); if (can_pullup(udc)) fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), @@ -1548,7 +1540,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, udc->ep0_state = WAIT_FOR_SETUP; break; case WAIT_FOR_SETUP: - dev_err(&udc->gadget.dev, "Unexpected ep0 packets\n"); + dev_err(udc->dev, "Unexpected ep0 packets\n"); break; default: ep0stall(udc); @@ -1617,7 +1609,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, errors = hc32_to_cpu(curr_td->size_ioc_sts); if (errors & DTD_ERROR_MASK) { if (errors & DTD_STATUS_HALTED) { - dev_err(&udc->gadget.dev, "dTD error %08x QH=%d\n", errors, pipe); + dev_err(udc->dev, "dTD error %08x QH=%d\n", errors, pipe); /* Clear the errors and Halt condition */ tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts); tmp &= ~errors; @@ -1628,26 +1620,26 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, break; } if (errors & DTD_STATUS_DATA_BUFF_ERR) { - dev_vdbg(&udc->gadget.dev, "Transfer overflow\n"); + dev_vdbg(udc->dev, "Transfer overflow\n"); status = -EPROTO; break; } else if (errors & DTD_STATUS_TRANSACTION_ERR) { - dev_vdbg(&udc->gadget.dev, "ISO error\n"); + dev_vdbg(udc->dev, "ISO error\n"); status = -EILSEQ; break; } else - dev_err(&udc->gadget.dev, + dev_err(udc->dev, "Unknown error has occurred (0x%x)!\n", errors); } else if (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_STATUS_ACTIVE) { - dev_vdbg(&udc->gadget.dev, "Request not complete\n"); + dev_vdbg(udc->dev, "Request not complete\n"); status = REQ_UNCOMPLETE; return status; } else if (remaining_length) { if (direction) { - dev_vdbg(&udc->gadget.dev, + dev_vdbg(udc->dev, "Transmit dTD remaining length not zero\n"); status = -EPROTO; break; @@ -1655,8 +1647,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, break; } } else { - dev_vdbg(&udc->gadget.dev, - "dTD transmitted successful\n"); + dev_vdbg(udc->dev, "dTD transmitted successful\n"); } if (j != curr_req->dtd_count - 1) @@ -1699,7 +1690,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) /* If the ep is configured */ if (!curr_ep->ep.name) { - dev_warn(&udc->gadget.dev, "Invalid EP?\n"); + dev_warn(udc->dev, "Invalid EP?\n"); continue; } @@ -1708,7 +1699,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) queue) { status = process_ep_req(udc, i, curr_req); - dev_vdbg(&udc->gadget.dev, + dev_vdbg(udc->dev, "status of process_ep_req= %d, ep = %d\n", status, ep_num); if (status == REQ_UNCOMPLETE) @@ -1829,7 +1820,7 @@ static void reset_irq(struct fsl_udc *udc) while (fsl_readl(&dr_regs->endpointprime)) { /* Wait until all endptprime bits cleared */ if (time_after(jiffies, timeout)) { - dev_err(&udc->gadget.dev, "Timeout for reset\n"); + dev_err(udc->dev, "Timeout for reset\n"); break; } cpu_relax(); @@ -1839,7 +1830,7 @@ static void reset_irq(struct fsl_udc *udc) fsl_writel(0xffffffff, &dr_regs->endptflush); if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { - dev_vdbg(&udc->gadget.dev, "Bus reset\n"); + dev_vdbg(udc->dev, "Bus reset\n"); /* Bus is reseting */ udc->bus_reset = 1; /* Reset all the queues, include XD, dTD, EP queue @@ -1847,7 +1838,7 @@ static void reset_irq(struct fsl_udc *udc) reset_queues(udc, true); udc->usb_state = USB_STATE_DEFAULT; } else { - dev_vdbg(&udc->gadget.dev, "Controller reset\n"); + dev_vdbg(udc->dev, "Controller reset\n"); /* initialize usb hw reg except for regs for EP, not * touch usbintr reg */ dr_controller_setup(udc); @@ -1881,7 +1872,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Clear notification bits */ fsl_writel(irq_src, &dr_regs->usbsts); - /* dev_vdbg(&udc->gadget.dev, "irq_src [0x%8x]", irq_src); */ + /* dev_vdbg(udc->dev, "irq_src [0x%8x]", irq_src); */ /* Need to resume? */ if (udc->usb_state == USB_STATE_SUSPENDED) @@ -1890,7 +1881,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* USB Interrupt */ if (irq_src & USB_STS_INT) { - dev_vdbg(&udc->gadget.dev, "Packet int\n"); + dev_vdbg(udc->dev, "Packet int\n"); /* Setup package, we only support ep0 as control ep */ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) { tripwire_handler(udc, 0, @@ -1919,7 +1910,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Reset Received */ if (irq_src & USB_STS_RESET) { - dev_vdbg(&udc->gadget.dev, "reset int\n"); + dev_vdbg(udc->dev, "reset int\n"); reset_irq(udc); status = IRQ_HANDLED; } @@ -1931,7 +1922,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) } if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { - dev_vdbg(&udc->gadget.dev, "Error IRQ %x\n", irq_src); + dev_vdbg(udc->dev, "Error IRQ %x\n", irq_src); } spin_unlock_irqrestore(&udc->lock, flags); @@ -1967,7 +1958,7 @@ static int fsl_udc_start(struct usb_gadget *g, udc_controller->transceiver->otg, &udc_controller->gadget); if (retval < 0) { - dev_err(&udc_controller->gadget.dev, "can't bind to transceiver\n"); + dev_err(udc_controller->dev, "can't bind to transceiver\n"); udc_controller->driver = NULL; return retval; } @@ -2252,7 +2243,7 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->eps = kzalloc_objs(struct fsl_ep, udc->max_ep); if (!udc->eps) { - dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n"); + dev_err(udc->dev, "kmalloc udc endpoint status failed\n"); goto eps_alloc_failed; } @@ -2267,7 +2258,7 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->ep_qh = dma_alloc_coherent(&pdev->dev, size, &udc->ep_qh_dma, GFP_KERNEL); if (!udc->ep_qh) { - dev_err(&udc->gadget.dev, "malloc QHs for udc failed\n"); + dev_err(udc->dev, "malloc QHs for udc failed\n"); goto ep_queue_alloc_failed; } @@ -2278,14 +2269,14 @@ static int struct_udc_setup(struct fsl_udc *udc, udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL), struct fsl_req, req); if (!udc->status_req) { - dev_err(&udc->gadget.dev, "kzalloc for udc status request failed\n"); + dev_err(udc->dev, "kzalloc for udc status request failed\n"); goto udc_status_alloc_failed; } /* allocate a small amount of memory to get valid address */ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); if (!udc->status_req->req.buf) { - dev_err(&udc->gadget.dev, "kzalloc for udc request buffer failed\n"); + dev_err(udc->dev, "kzalloc for udc request buffer failed\n"); goto udc_req_buf_alloc_failed; } @@ -2373,6 +2364,7 @@ static int fsl_udc_probe(struct platform_device *pdev) if (udc_controller == NULL) return -ENOMEM; + udc_controller->dev = &pdev->dev; pdata = dev_get_platdata(&pdev->dev); udc_controller->pdata = pdata; spin_lock_init(&udc_controller->lock); @@ -2382,7 +2374,7 @@ static int fsl_udc_probe(struct platform_device *pdev) if (pdata->operating_mode == FSL_USB2_DR_OTG) { udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(udc_controller->transceiver)) { - dev_err(&udc_controller->gadget.dev, "Can't find OTG driver!\n"); + dev_err(&pdev->dev, "Can't find OTG driver!\n"); ret = -ENODEV; goto err_kfree; } @@ -2398,7 +2390,7 @@ static int fsl_udc_probe(struct platform_device *pdev) if (pdata->operating_mode == FSL_USB2_DR_DEVICE) { if (!request_mem_region(res->start, resource_size(res), driver_name)) { - dev_err(&udc_controller->gadget.dev, "request mem region for %s failed\n", pdev->name); + dev_err(&pdev->dev, "failed to request mem region\n"); ret = -EBUSY; goto err_kfree; } @@ -2429,7 +2421,7 @@ static int fsl_udc_probe(struct platform_device *pdev) /* Read Device Controller Capability Parameters register */ dccparams = fsl_readl(&dr_regs->dccparams); if (!(dccparams & DCCPARAMS_DC)) { - dev_err(&udc_controller->gadget.dev, "This SOC doesn't support device role\n"); + dev_err(&pdev->dev, "This SOC doesn't support device role\n"); ret = -ENODEV; goto err_exit; } @@ -2447,14 +2439,14 @@ static int fsl_udc_probe(struct platform_device *pdev) ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED, driver_name, udc_controller); if (ret != 0) { - dev_err(&udc_controller->gadget.dev, "cannot request irq %d err %d\n", + dev_err(&pdev->dev, "cannot request irq %d err %d\n", udc_controller->irq, ret); goto err_exit; } /* Initialize the udc structure including QH member and other member */ if (struct_udc_setup(udc_controller, pdev)) { - dev_err(&udc_controller->gadget.dev, "Can't initialize udc data structure\n"); + dev_err(&pdev->dev, "Can't initialize udc data structure\n"); ret = -ENOMEM; goto err_free_irq; } diff --git a/drivers/usb/gadget/udc/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h index cc1756f3e89d..53922bc58ca0 100644 --- a/drivers/usb/gadget/udc/fsl_usb2_udc.h +++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h @@ -470,6 +470,7 @@ struct fsl_ep { #define EP_DIR_OUT 0 struct fsl_udc { + struct device *dev; struct usb_gadget gadget; struct usb_gadget_driver *driver; struct fsl_usb2_platform_data *pdata; -- cgit v1.2.3 From f05847a216a74e1f2387169a336edbc198960016 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 2 Jul 2026 16:15:36 +0200 Subject: USB: gadget: fsl-udc: drop misleading unbind sanity check The UDC pointer is set on successful probe and will never be NULL when the driver is later unbound so drop the misleading sanity check (and confused error message). Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260702141536.90887-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_udc_core.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index c4761933ec86..20392409afb1 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2539,12 +2539,6 @@ static void fsl_udc_remove(struct platform_device *pdev) DECLARE_COMPLETION_ONSTACK(done); - if (!udc_controller) { - dev_err(&pdev->dev, - "Driver still in use but removing anyhow\n"); - return; - } - udc_controller->done = &done; usb_del_gadget_udc(&udc_controller->gadget); -- cgit v1.2.3 From 1febec7e47cdcd01f43fb0211094e3010474666e Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Fri, 3 Jul 2026 08:37:24 +0000 Subject: usb: gadget: f_ncm: validate datagram bounds in ncm_unwrap_ntb() When unpacking host-supplied NTBs, ncm_unwrap_ntb() checks datagram length against frame_max but does not verify that the datagram fits within the declared block length. Additionally, when decoding multiple NTBs from a single socket buffer, subsequent block lengths are not checked against the actual remaining buffer data. With these checks missing, a malicious USB host can specify datagram offsets and lengths that point beyond the block, or supply secondary NTB headers declaring lengths larger than the buffer. skb_put_data() then copies adjacent kernel memory from skb_shared_info into the network skb. Fix this by verifying that sufficient buffer space remains for the NTB header before parsing, handling zero-length block declarations, ensuring that block lengths never exceed the remaining buffer space, and verifying that each datagram payload stays strictly within the block boundary. Fixes: 427694cfaafa ("usb: gadget: ncm: Handle decoding of multiple NTB's in unwrap call") Fixes: 2b74b0a04d3e ("USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()") Cc: stable Assisted-by: Jetski:Gemini-2.5-Pro Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260703083725.1903850-1-sonalipradhan@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ncm.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index c5bf8a448d64..64eabda2f546 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1189,6 +1189,10 @@ static int ncm_unwrap_ntb(struct gether *port, frame_max = ncm_opts->max_segment_size; parse_ntb: + if (to_process < (int)opts->nth_size) { + INFO(port->func.config->cdev, "Packet too small for headers\n"); + goto err; + } tmp = (__le16 *)ntb_ptr; /* dwSignature */ @@ -1209,8 +1213,12 @@ parse_ntb: tmp++; /* skip wSequence */ block_len = get_ncm(&tmp, opts->block_length); + if (block_len == 0) + block_len = to_process; + /* (d)wBlockLength */ - if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) { + if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max) || + (block_len > to_process)) { INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len); goto err; } @@ -1273,7 +1281,7 @@ parse_ntb: index = index2; /* wDatagramIndex[0] */ if ((index < opts->nth_size) || - (index > block_len - opts->dpe_size)) { + (index > block_len)) { INFO(port->func.config->cdev, "Bad index: %#X\n", index); goto err; @@ -1285,7 +1293,8 @@ parse_ntb: * ethernet hdr + crc or larger than max frame size */ if ((dg_len < 14 + crc_len) || - (dg_len > frame_max)) { + (dg_len > frame_max) || + (dg_len > block_len - index)) { INFO(port->func.config->cdev, "Bad dgram length: %#X\n", dg_len); goto err; @@ -1310,7 +1319,7 @@ parse_ntb: dg_len2 = get_ncm(&tmp, opts->dgram_item_len); /* wDatagramIndex[1] */ - if (index2 > block_len - opts->dpe_size) { + if (index2 > block_len) { INFO(port->func.config->cdev, "Bad index: %#X\n", index2); goto err; -- cgit v1.2.3 From 7aa7d4bf9d3fa9a6a47b640ad103ab433b7ff261 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Tue, 7 Jul 2026 14:17:36 +0000 Subject: usb: typec: ucsi: Fix race condition and ordering in port unregistration A synchronization issue exists during port unregistration where pending partner work items can race against workqueue destruction, leading to use-after-free conditions: cros_ec_ucsi cros_ec_ucsi.3.auto: error -ETIMEDOUT: PPM init failed BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:__queue_work+0x83/0x4a0 Call Trace: __cfi_delayed_work_timer_fn+0x10/0x10 run_timer_softirq+0x3b6/0xbd0 sched_clock_cpu+0xc/0x110 irq_exit_rcu+0x18d/0x330 fred_sysvec_apic_timer_interrupt+0x5e/0x80 Fix this by ensuring strict ordering and proper serialization during teardown: 1. Move ucsi_unregister_partner() to the beginning of the teardown sequence and protect it under the connector mutex lock. 2. Ensure all pending partner tasks are explicitly flushed and finished before the workqueue is destroyed. 3. Switch from mod_delayed_work() to a cancel_delayed_work() and queue_delayed_work() sequence. This guarantees that items currently marked as pending won't be scheduled an additional time, preventing a double release of resources which leads to the following crash: Oops: general protection fault, probably for non-canonical address 0xdead000000000122: 0000 [#1] SMP NOPTI Workqueue: cros_ec_ucsi.3.auto-con2 ucsi_poll_worker RIP: 0010:ucsi_poll_worker+0x65/0x1e0 Call Trace: process_scheduled_works+0x218/0x6d0 worker_thread+0x188/0x3f0 __cfi_worker_thread+0x10/0x10 kthread+0x226/0x2a0 To ensure these rules are applied identically across both the normal teardown and the ucsi_init() error paths, consolidate the cleanup logic into a new helper, ucsi_unregister_port(). Cc: stable Fixes: b9aa02ca39a4 ("usb: typec: ucsi: Add polling mechanism for partner tasks like alt mode checking") Fixes: b13abcb7ddd8 ("usb: typec: ucsi: Fix NULL pointer access") Fixes: fac4b8633fd6 ("usb: ucsi: Ensure connector delayed work items are flushed") Signed-off-by: Andrei Kuchynski Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260707141736.1635698-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 82 ++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 6a6723e8fb12..f56897e23df8 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1845,6 +1845,42 @@ out_unlock: return ret; } +static void ucsi_unregister_port(struct ucsi_connector *con) +{ + struct ucsi_work *uwork; + + if (con->wq) { + mutex_lock(&con->lock); + ucsi_unregister_partner(con); + /* + * queue delayed items immediately so they can execute + * and free themselves before the wq is destroyed + */ + list_for_each_entry(uwork, &con->partner_tasks, node) { + if (cancel_delayed_work(&uwork->work)) + queue_delayed_work(con->wq, &uwork->work, 0); + } + mutex_unlock(&con->lock); + + destroy_workqueue(con->wq); + con->wq = NULL; + } else { + ucsi_unregister_partner(con); + } + + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); + ucsi_unregister_port_psy(con); + + usb_power_delivery_unregister_capabilities(con->port_sink_caps); + con->port_sink_caps = NULL; + usb_power_delivery_unregister_capabilities(con->port_source_caps); + con->port_source_caps = NULL; + usb_power_delivery_unregister(con->pd); + con->pd = NULL; + typec_unregister_port(con->port); + con->port = NULL; +} + static u64 ucsi_get_supported_notifications(struct ucsi *ucsi) { u16 features = ucsi->cap.features; @@ -1971,22 +2007,8 @@ err_unregister: for (i = 0; i < ucsi->cap.num_connectors; i++) lockdep_unregister_key(&connector[i].lock_key); - for (con = connector; con->port; con++) { - if (con->wq) - destroy_workqueue(con->wq); - ucsi_unregister_partner(con); - ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); - ucsi_unregister_port_psy(con); - - usb_power_delivery_unregister_capabilities(con->port_sink_caps); - con->port_sink_caps = NULL; - usb_power_delivery_unregister_capabilities(con->port_source_caps); - con->port_source_caps = NULL; - usb_power_delivery_unregister(con->pd); - con->pd = NULL; - typec_unregister_port(con->port); - con->port = NULL; - } + for (con = connector; con->port; con++) + ucsi_unregister_port(con); kfree(connector); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); @@ -2214,33 +2236,7 @@ void ucsi_unregister(struct ucsi *ucsi) for (i = 0; i < ucsi->cap.num_connectors; i++) { cancel_work_sync(&ucsi->connector[i].work); - - if (ucsi->connector[i].wq) { - struct ucsi_work *uwork; - - mutex_lock(&ucsi->connector[i].lock); - /* - * queue delayed items immediately so they can execute - * and free themselves before the wq is destroyed - */ - list_for_each_entry(uwork, &ucsi->connector[i].partner_tasks, node) - mod_delayed_work(ucsi->connector[i].wq, &uwork->work, 0); - mutex_unlock(&ucsi->connector[i].lock); - destroy_workqueue(ucsi->connector[i].wq); - } - - ucsi_unregister_partner(&ucsi->connector[i]); - ucsi_unregister_altmodes(&ucsi->connector[i], - UCSI_RECIPIENT_CON); - ucsi_unregister_port_psy(&ucsi->connector[i]); - - usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps); - ucsi->connector[i].port_sink_caps = NULL; - usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps); - ucsi->connector[i].port_source_caps = NULL; - usb_power_delivery_unregister(ucsi->connector[i].pd); - ucsi->connector[i].pd = NULL; - typec_unregister_port(ucsi->connector[i].port); + ucsi_unregister_port(&ucsi->connector[i]); lockdep_unregister_key(&ucsi->connector[i].lock_key); } -- cgit v1.2.3 From 0583f2fbf8f86ae3a0ce054f96783dd83e65d9bb Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 9 Jul 2026 02:09:04 +0000 Subject: usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown The Broadcom BDC UDC driver registers its IRQ handler with devm_request_irq() in bdc_udc_init(), so the IRQ is released by devm only after bdc_remove() returns. devm releases resources in reverse LIFO order, but bdc_remove() runs bdc_udc_exit() and bdc_hw_exit() -> bdc_mem_free() manually before returning: bdc_udc_exit() tears down individual endpoint objects via bdc_free_ep(), while bdc_hw_exit() -> bdc_mem_free() frees and NULLs the DMA-coherent status-report ring (bdc->srr.sr_bds) and kfree()s bdc->bdc_ep_array. Both happen while the IRQ handler (bdc_udc_interrupt, requested with IRQF_SHARED) remains deliverable in the window up to the post-remove devm free_irq(). On receipt of a shared interrupt in that window, bdc_udc_interrupt() dereferences bdc->srr.sr_bds[bdc->srr.dqp_index] (NULL or freed DMA) and dispatches sr_handler callbacks that index into bdc_ep_array, causing a NULL-deref or use-after-free. The same window affects the delayed_work bdc->func_wake_notify, which is armed from the IRQ handler via bdc_sr_uspc() -> handle_link_state_change() -> schedule_delayed_work() and may self-rearm from its own callback bdc_func_wake_timer(). No cancel exists anywhere in the driver, so a queued work item that fires after bdc_remove() returns and the bdc structure is devm-freed dereferences freed memory. Replace devm_request_irq() with request_irq() and add an explicit free_irq(bdc->irq, bdc) in bdc_remove(). Clear BDC_GIE before free_irq() to stop the device from asserting interrupts, then free_irq() drains any in-flight handler, then cancel_delayed_work_sync() drains the func_wake_notify delayed work. This ordering ensures the IRQ handler and delayed work cannot interfere with the subsequent endpoint and DMA teardown in bdc_udc_exit() and bdc_hw_exit(). Wire the matching free_irq() into the bdc_udc_init() error path so the IRQ is released on probe failure, and route the bdc_init_ep() failure through err0 instead of returning directly. This issue was found by an in-house static analysis tool. Fixes: efed421a94e6 ("usb: gadget: Add UDC driver for Broadcom USB3.0 device controller IP BDC") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260709020904.502611-1-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/bdc/bdc_core.c | 20 ++++++++++++++++++++ drivers/usb/gadget/udc/bdc/bdc_udc.c | 7 ++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 438201dc96ca..a8dbaef54aba 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -585,9 +585,29 @@ disable_clk: static void bdc_remove(struct platform_device *pdev) { struct bdc *bdc; + unsigned long flags; + u32 temp; bdc = platform_get_drvdata(pdev); dev_dbg(bdc->dev, "%s ()\n", __func__); + /* + * Disable the device interrupt source before freeing the IRQ: + * clear BDC_GIE so the controller stops asserting interrupts, + * then free_irq drains any in-flight handler. + */ + spin_lock_irqsave(&bdc->lock, flags); + temp = bdc_readl(bdc->regs, BDC_BDCSC); + temp &= ~BDC_GIE; + bdc_writel(bdc->regs, BDC_BDCSC, temp); + spin_unlock_irqrestore(&bdc->lock, flags); + free_irq(bdc->irq, bdc); + /* + * Drain func_wake_notify after free_irq: the IRQ handler arms this + * delayed_work via bdc_sr_uspc -> handle_link_state_change -> + * schedule_delayed_work (self-rearmed in bdc_func_wake_timer), so + * the IRQ must be released first to prevent re-arm after cancel. + */ + cancel_delayed_work_sync(&bdc->func_wake_notify); bdc_udc_exit(bdc); bdc_hw_exit(bdc); bdc_phy_exit(bdc); diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index 23826fd7a8e6..7a12219edac6 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -530,8 +530,8 @@ int bdc_udc_init(struct bdc *bdc) bdc->gadget.name = BRCM_BDC_NAME; - ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt, - IRQF_SHARED, BRCM_BDC_NAME, bdc); + ret = request_irq(bdc->irq, bdc_udc_interrupt, IRQF_SHARED, + BRCM_BDC_NAME, bdc); if (ret) { dev_err(bdc->dev, "failed to request irq #%d %d\n", @@ -542,7 +542,7 @@ int bdc_udc_init(struct bdc *bdc) ret = bdc_init_ep(bdc); if (ret) { dev_err(bdc->dev, "bdc init ep fail: %d\n", ret); - return ret; + goto err0; } ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget); @@ -571,6 +571,7 @@ int bdc_udc_init(struct bdc *bdc) err1: usb_del_gadget_udc(&bdc->gadget); err0: + free_irq(bdc->irq, bdc); bdc_free_ep(bdc); return ret; -- cgit v1.2.3 From 5650c18d93a1db7e27cb5a40b394747eb4686d5b Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 9 Jul 2026 15:07:17 +0000 Subject: usb: gadget: f_midi: cancel pending IN work before freeing the midi object The f_midi driver embeds a work item (midi->work) whose handler, f_midi_in_work(), dereferences the enclosing struct f_midi through container_of(). This work is armed from two sites: f_midi_complete(), on a normal IN-endpoint completion, and f_midi_in_trigger(), on an ALSA rawmidi output-stream start. Neither f_midi_disable() nor f_midi_unbind() cancels midi->work. f_midi_disable() only disables the endpoints and drains the in_req_fifo; it does not synchronize the work item, and the sound card is released asynchronously to the final free of the midi object. The midi object is reference-counted (midi->free_ref) and is freed in f_midi_free() only once both the usb_function reference and the rawmidi private_data reference have been dropped. In f_midi_unbind(), f_midi_disable() runs before the sound card is released, so while the USB endpoints are already disabled the rawmidi device is still usable by an open substream. A concurrent userspace write on such a substream can reach f_midi_in_trigger() and queue midi->work again after f_midi_disable() has returned. A work item armed this way may still be pending when the last reference drops and f_midi_free() proceeds to kfree(midi), letting f_midi_in_work() dereference the struct after it has been freed, a use-after-free. For this reason cancelling midi->work in f_midi_disable() would not be sufficient: the ALSA trigger path can rearm the work after disable() returns. Cancelling at the refcount-zero free site is the boundary after which neither arming source can survive, because by then both references that keep the midi object alive have been dropped: the USB endpoints are already disabled and the rawmidi device has been released. Fix this by calling cancel_work_sync(&midi->work) in the refcount-zero block of f_midi_free(), before the embedded work_struct is freed along with the rest of the structure. opts->lock is a sleeping mutex, so calling cancel_work_sync() under it is permitted, and the handler takes midi->transmit_lock rather than opts->lock, so no self-deadlock can occur while it waits for a running instance of the work to finish. This issue was found by an in-house static analysis tool. Fixes: 8653d71ce3763 ("usb/gadget: f_midi: Replace tasklet with work") Cc: stable Assisted-by: Codex:gpt-5.5 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260709150717.399083-1-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_midi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 4d9e4bd700d8..fba8cf787d6c 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1309,6 +1309,7 @@ static void f_midi_free(struct usb_function *f) opts = container_of(f->fi, struct f_midi_opts, func_inst); mutex_lock(&opts->lock); if (!--midi->free_ref) { + cancel_work_sync(&midi->work); kfree(midi->id); kfifo_free(&midi->in_req_fifo); kfree(midi); -- cgit v1.2.3 From c2e819be6a5c7f34344926b4bd7e3dfca58cf48a Mon Sep 17 00:00:00 2001 From: Melbin K Mathew Date: Thu, 9 Jul 2026 21:56:22 +0100 Subject: usb: gadget: printer: fix infinite loop in printer_read() printer_read() uses the same variable for the requested copy size and the number of bytes actually copied to user space. copy_to_user() returns the number of bytes not copied, so when it fails to copy anything, the computed copied length becomes zero. In that case len, buf, current_rx_bytes and current_rx_buf are left unchanged. If RX data is available and the user buffer remains unwritable, the read loop can repeat indefinitely. Track the copied length separately and return -EFAULT, or the number of bytes already copied, if an iteration makes no progress. Fixes: b185f01a9ab7 ("usb: gadget: printer: factor out f_printer") Cc: stable Reviewed-by: Peter Chen Signed-off-by: Melbin K Mathew Link: https://patch.msgid.link/20260709205622.55700-1-mlbnkm1@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_printer.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 837f753d0cae..1857d786110b 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -431,7 +431,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) { struct printer_dev *dev = fd->private_data; unsigned long flags; - size_t size; + size_t size, not_copied, copied; size_t bytes_copied; struct usb_request *req; /* This is a pointer to the current USB rx request. */ @@ -524,10 +524,12 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) else size = len; - size -= copy_to_user(buf, current_rx_buf, size); - bytes_copied += size; - len -= size; - buf += size; + not_copied = copy_to_user(buf, current_rx_buf, size); + copied = size - not_copied; + + bytes_copied += copied; + len -= copied; + buf += copied; spin_lock_irqsave(&dev->lock, flags); @@ -542,6 +544,17 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) if (dev->interface < 0) goto out_disabled; + if (!copied) { + dev->current_rx_req = current_rx_req; + dev->current_rx_bytes = current_rx_bytes; + dev->current_rx_buf = current_rx_buf; + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return bytes_copied ? bytes_copied : -EFAULT; + } + + size = copied; + /* If we not returning all the data left in this RX request * buffer then adjust the amount of data left in the buffer. * Othewise if we are done with this RX request buffer then -- cgit v1.2.3 From 4bbc16a353a98023e5ddfca7c1fc0e49971cf4d0 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 14:37:26 -0700 Subject: ata: sata_dwc_460ex: enable SATA interrupts only after IRQ handler is registered sata_dwc_enable_interrupts() is called before platform_get_irq() and ata_host_activate(), leaving the SATA controller's interrupt mask enabled without a registered handler. If a later step fails (irq request, phy init, etc.) or if the controller asserts an interrupt during probe, the irq line may fire with no handler, causing a spurious interrupt storm. Move sata_dwc_enable_interrupts() after ata_host_activate() so that interrupts are only unmasked once the handler is registered and the core is fully initialized. Fixes: 62936009f35a ("[libata] Add 460EX on-chip SATA driver, sata_dwc_460ex") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_dwc_460ex.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 4fc22ce4bd9a..e10eeb8ca030 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -1169,9 +1169,6 @@ static int sata_dwc_probe(struct platform_device *ofdev) /* Save dev for later use in dev_xxx() routines */ hsdev->dev = dev; - /* Enable SATA Interrupts */ - sata_dwc_enable_interrupts(hsdev); - /* Get SATA interrupt number */ irq = irq_of_parse_and_map(np, 0); if (!irq) { @@ -1204,6 +1201,8 @@ static int sata_dwc_probe(struct platform_device *ofdev) if (err) dev_err(dev, "failed to activate host"); + /* Enable SATA Interrupts */ + sata_dwc_enable_interrupts(hsdev); return 0; error_out: -- cgit v1.2.3 From a4af122106f73ea510bb35a9ea1dedd980fc0db7 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 14:37:25 -0700 Subject: ata: sata_dwc_460ex: use platform_get_irq() Replace irq_of_parse_and_map() with platform_get_irq() in both sata_dwc_dma_init_old() and sata_dwc_probe(). This is the preferred way to obtain IRQs for platform devices and provides better error reporting. Remove the now-unnecessary #include . irq_of_parse_and_map() requires irq_dispose_mapping(), which is missing. Also fix unused variable when CONFIG_SATA_DWC_OLD_DMA is disabled. Fixes: 62936009f35a ("[libata] Add 460EX on-chip SATA driver, sata_dwc_460ex") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_dwc_460ex.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index e10eeb8ca030..85c5e67e9175 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -226,7 +225,6 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev, struct sata_dwc_device *hsdev) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; hsdev->dma = devm_kzalloc(dev, sizeof(*hsdev->dma), GFP_KERNEL); if (!hsdev->dma) @@ -236,11 +234,9 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev, hsdev->dma->id = pdev->id; /* Get SATA DMA interrupt number */ - hsdev->dma->irq = irq_of_parse_and_map(np, 1); - if (!hsdev->dma->irq) { - dev_err(dev, "no SATA DMA irq\n"); - return -ENODEV; - } + hsdev->dma->irq = platform_get_irq(pdev, 1); + if (hsdev->dma->irq < 0) + return hsdev->dma->irq; /* Get physical SATA DMA register base address */ hsdev->dma->regs = devm_platform_ioremap_resource(pdev, 1); @@ -1126,7 +1122,6 @@ static const struct ata_port_info sata_dwc_port_info[] = { static int sata_dwc_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; - struct device_node *np = dev->of_node; struct sata_dwc_device *hsdev; u32 idr, versionr; char *ver = (char *)&versionr; @@ -1170,14 +1165,12 @@ static int sata_dwc_probe(struct platform_device *ofdev) hsdev->dev = dev; /* Get SATA interrupt number */ - irq = irq_of_parse_and_map(np, 0); - if (!irq) { - dev_err(dev, "no SATA DMA irq\n"); - return -ENODEV; - } + irq = platform_get_irq(ofdev, 0); + if (irq < 0) + return irq; #ifdef CONFIG_SATA_DWC_OLD_DMA - if (!of_property_present(np, "dmas")) { + if (!of_property_present(dev->of_node, "dmas")) { err = sata_dwc_dma_init_old(ofdev, hsdev); if (err) return err; -- cgit v1.2.3 From 66c4e310ad71f41e41736d33dd8a1fb5eaaec7f3 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 14:37:27 -0700 Subject: ata: sata_dwc_460ex: fix clear_interrupt_bit() clearing all pending interrupts clear_interrupt_bit() ignores the bit argument and performs a read-write-back of the entire INTPR register. If INTPR uses standard Write-1-to-Clear semantics, this clears every pending interrupt bit, not just the intended one. Coalesced interrupts (e.g. DMAT + NEWFP) would be cleared together, silently losing the second event. Write only the specific bit to clear so that other pending interrupts are preserved. Fixes: 62936009f35a ("[libata] Add 460EX on-chip SATA driver, sata_dwc_460ex") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_dwc_460ex.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 85c5e67e9175..bc543a408963 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -394,8 +394,7 @@ static void clear_serror(struct ata_port *ap) static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit) { - sata_dwc_writel(&hsdev->sata_dwc_regs->intpr, - sata_dwc_readl(&hsdev->sata_dwc_regs->intpr)); + sata_dwc_writel(&hsdev->sata_dwc_regs->intpr, bit); } static u32 qcmd_tag_to_mask(u8 tag) -- cgit v1.2.3 From c2130f6553f4a5cbdc259de069600117a995f197 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 14:37:28 -0700 Subject: ata: sata_dwc_460ex: fix infinite loop in NCQ tag completion bit-scanning The hand-rolled bit-scanning loop in the NCQ completion path has an infinite loop bug. When tag_mask has only high bits set (e.g. 0x80000000), the inner while loop left-shifts tag_mask until it overflows to 0. At that point !(0 & 1) is always true and 0 <<= 1 stays 0, causing an infinite loop in hardirq context with a spinlock held. Replace the open-coded bit-scanning with __ffs() which correctly finds the least significant set bit and is bounded by the width of the argument. Fixes: 62936009f35a ("[libata] Add 460EX on-chip SATA driver, sata_dwc_460ex") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_dwc_460ex.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index bc543a408963..8e3fc713891a 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -607,14 +607,9 @@ DRVSTILLBUSY: status = ap->ops->sff_check_status(ap); dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status); - tag = 0; while (tag_mask) { - while (!(tag_mask & 0x00000001)) { - tag++; - tag_mask <<= 1; - } - - tag_mask &= (~0x00000001); + tag = __ffs(tag_mask); + tag_mask &= ~(1U << tag); qc = ata_qc_from_tag(ap, tag); if (unlikely(!qc)) { dev_err(ap->dev, "failed to get qc"); -- cgit v1.2.3 From 936190fcfcf66695348127992249051467de7072 Mon Sep 17 00:00:00 2001 From: Fangyu Yu Date: Wed, 10 Jun 2026 17:39:22 +0800 Subject: RISC-V: KVM: Avoid redundant page-table allocations in ioremap topup kvm_riscv_mmu_ioremap() currently tops up its on-stack page-table cache via kvm_mmu_topup_memory_cache(), which allocates up to KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE (32) objects per topup. ioremap only consumes non-leaf page-table pages, at most pgd_levels - 1 (1 to 4) per call, and for contiguous mappings within the same huge page the non-leaf pages are allocated once and reused by subsequent pages. Topping up to 32 objects therefore triggers many unnecessary GFP_KERNEL_ACCOUNT allocations on every call, all of which are freed when the function returns. In hot paths (such as vCPU migration), this creates avoidable allocator churn and wastes CPU cycles. Use __kvm_mmu_topup_memory_cache() with a capacity of pgd_levels so the on-stack cache is sized to the maximum demand of a single mapping. This removes the redundant allocations and reduces per-call overhead without changing behavior. Reviewed-by: Anup Patel Signed-off-by: Fangyu Yu Link: https://lore.kernel.org/r/20260610093922.51617-1-fangyu.yu@linux.alibaba.com Signed-off-by: Anup Patel --- arch/riscv/kvm/mmu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 082f9b261733..8a0aa5e0e216 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -41,6 +41,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, pgprot_t prot; unsigned long pfn; phys_addr_t addr, end; + unsigned long pgd_levels = kvm->arch.pgd_levels; struct kvm_mmu_memory_cache pcache = { .gfp_custom = (in_atomic) ? GFP_ATOMIC | __GFP_ACCOUNT : 0, .gfp_zero = __GFP_ZERO, @@ -63,7 +64,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, if (!writable) map.pte = pte_wrprotect(map.pte); - ret = kvm_mmu_topup_memory_cache(&pcache, kvm->arch.pgd_levels); + ret = __kvm_mmu_topup_memory_cache(&pcache, pgd_levels, pgd_levels); if (ret) goto out; -- cgit v1.2.3 From b8aa7571e943591c26512667da824988917d3b67 Mon Sep 17 00:00:00 2001 From: SeungJu Cheon Date: Wed, 24 Jun 2026 22:02:38 +0900 Subject: KVM: riscv: SBI FWFT: Apply LOCK flag only on successful set kvm_sbi_fwft_set() applies the caller's flags to conf->flags before invoking the set() callback. If the callback returns an error, the LOCK bit persists and the feature becomes permanently locked without its value ever being changed. Move the flags assignment after the callback so LOCK takes effect only on success. Fixes: 6b72fd170592 ("RISC-V: KVM: add support for FWFT SBI extension") Signed-off-by: SeungJu Cheon Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260624130238.524706-1-suunj1331@gmail.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_sbi_fwft.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kvm/vcpu_sbi_fwft.c b/arch/riscv/kvm/vcpu_sbi_fwft.c index ab39ac464ffd..1342adb3180c 100644 --- a/arch/riscv/kvm/vcpu_sbi_fwft.c +++ b/arch/riscv/kvm/vcpu_sbi_fwft.c @@ -327,9 +327,11 @@ static int kvm_sbi_fwft_set(struct kvm_vcpu *vcpu, u32 feature, if (conf->flags & SBI_FWFT_SET_FLAG_LOCK) return SBI_ERR_DENIED_LOCKED; - conf->flags = flags; + ret = conf->feature->set(vcpu, conf, false, value); + if (ret == SBI_SUCCESS) + conf->flags = flags; - return conf->feature->set(vcpu, conf, false, value); + return ret; } static int kvm_sbi_fwft_get(struct kvm_vcpu *vcpu, unsigned long feature, -- cgit v1.2.3 From e4159045c2704dfe146f0ccb0445d9d074cd6882 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Fri, 10 Jul 2026 12:13:31 +0200 Subject: accel/ivpu: Fix wrong register read in LNL failure diagnostics diagnose_failure_lnl() read VPU_HW_BTRS_MTL_INTERRUPT_STAT instead of VPU_HW_BTRS_LNL_INTERRUPT_STAT, which on LNL and newer parts is a different register with a different bit layout, so failure diagnostics decoded the wrong register and reported a bogus error cause. Read the LNL interrupt status register instead. Fixes: 8a27ad81f7d3 ("accel/ivpu: Split IP and buttress code") Reviewed-by: Andrzej Kacprowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260710101331.1899505-1-karol.wachowski@linux.intel.com --- drivers/accel/ivpu/ivpu_hw_btrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c index dac935164e11..a17c829adb89 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.c +++ b/drivers/accel/ivpu/ivpu_hw_btrs.c @@ -927,7 +927,7 @@ static void diagnose_failure_mtl(struct ivpu_device *vdev) static void diagnose_failure_lnl(struct ivpu_device *vdev) { - u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK; + u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK; if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) { ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n", -- cgit v1.2.3 From 1572282de6d3377ca8605d48c50df3a8c08468e9 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 13 Jul 2026 16:35:29 +0800 Subject: 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)) -- cgit v1.2.3 From 42a97c0480f96a2977e6d51ce512adc780f1ef5d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 8 Jul 2026 16:31:35 +0200 Subject: USB: serial: keyspan_pda: fix data loss on receive throttling Killing the interrupt-in urb when the line disciple requests throttling may lead to data loss if an ongoing transfer is cancelled. Instead set a flag to prevent the completion handler from resubmitting the urb until the port is unthrottled. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/keyspan_pda.c | 44 ++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index f05bcce60600..dd4cfd17f7ad 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -33,6 +33,8 @@ struct keyspan_pda_private { struct work_struct unthrottle_work; struct usb_serial *serial; struct usb_serial_port *port; + bool throttled; + bool throttle_req; }; static int keyspan_pda_write_start(struct usb_serial_port *port); @@ -148,6 +150,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) int retval; int status = urb->status; struct keyspan_pda_private *priv; + bool throttled = false; unsigned long flags; priv = usb_get_serial_port_data(port); @@ -209,16 +212,24 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) } exit: - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(&port->dev, - "%s - usb_submit_urb failed with result %d\n", - __func__, retval); + spin_lock_irqsave(&port->lock, flags); + if (priv->throttle_req) { + priv->throttled = true; + throttled = true; + } + spin_unlock_irqrestore(&port->lock, flags); + + if (!throttled) { + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(&port->dev, "failed to resubmit in urb: %d\n", retval); + } } static void keyspan_pda_rx_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct keyspan_pda_private *priv = usb_get_serial_port_data(port); /* * Stop receiving characters. We just turn off the URB request, and @@ -228,16 +239,29 @@ static void keyspan_pda_rx_throttle(struct tty_struct *tty) * send an XOFF, although it might make sense to foist that off upon * the device too. */ - usb_kill_urb(port->interrupt_in_urb); + spin_lock_irq(&port->lock); + priv->throttle_req = true; + spin_unlock_irq(&port->lock); } static void keyspan_pda_rx_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct keyspan_pda_private *priv = usb_get_serial_port_data(port); + bool throttled; + int ret; - /* just restart the receive interrupt URB */ - if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL)) - dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n"); + spin_lock_irq(&port->lock); + throttled = priv->throttled; + priv->throttled = false; + priv->throttle_req = false; + spin_unlock_irq(&port->lock); + + if (throttled) { + ret = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (ret) + dev_err(&port->dev, "failed to submit in urb: %d\n", ret); + } } static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud) @@ -577,6 +601,8 @@ static int keyspan_pda_open(struct tty_struct *tty, spin_lock_irq(&port->lock); priv->tx_room = rc; + priv->throttled = false; + priv->throttle_req = false; spin_unlock_irq(&port->lock); rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); -- cgit v1.2.3 From 47b87f469a35b5ffc81c16eee6b13a9b6c8d55c6 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Mon, 1 Jun 2026 15:50:00 +0800 Subject: 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); -- cgit v1.2.3 From fad0fd120e29041b3e6cdf41bb12e3184fb524a2 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Sat, 11 Jul 2026 17:36:30 +0000 Subject: USB: serial: ftdi_sio: add support for E+H FXA291 The Commubox FXA291 by Endress+Hauser AG is a USB serial converter based on FT232B which is used to communicate with field devices. It enumerates using the FTDI vendor ID and a custom PID. usb 1-9: New USB device found, idVendor=0403, idProduct=e510, bcdDevice= 4.00 usb 1-9: New USB device strings: Mfr=1, Product=2, SerialNumber=0 usb 1-9: Product: FXA291 usb 1-9: Manufacturer: Endress+Hauser usb 1-9: SerialNumber: 00000000 ftdi_sio 1-9:1.0: FTDI USB Serial Device converter detected usb 1-9: Detected FT232B usb 1-9: FTDI USB Serial Device converter now attached to ttyUSB0 Signed-off-by: Tim Pambor Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 2 ++ drivers/usb/serial/ftdi_sio_ids.h | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 88dd32da82c2..c6ffa23bcc8f 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1072,6 +1072,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) }, /* Abacus Electrics */ { USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) }, + /* Endress+Hauser AG devices */ + { USB_DEVICE(FTDI_VID, FTDI_EH_FXA291_PID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 6c76cfebfd0e..9c83c17853c8 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -313,6 +313,11 @@ #define FTDI_ELV_UDF77_PID 0xFB5E /* USB DCF Funkuhr (UDF 77) */ #define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */ +/* + * Endress+Hauser AG product ids (FTDI_VID) + */ +#define FTDI_EH_FXA291_PID 0xE510 + /* * EVER Eco Pro UPS (http://www.ever.com.pl/) */ -- cgit v1.2.3 From 93b47e66cc6d6c6382d44b44f5e7f6fc3a7b38c3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 9 Jul 2026 11:56:06 +0200 Subject: 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 */ -- cgit v1.2.3 From 07ae060d35900977a93d4a05ec995b06c85c2552 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 9 Jul 2026 17:12:06 +0100 Subject: 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); -- cgit v1.2.3 From 9064637fb2a80b43105900a47d414997630e5b6b Mon Sep 17 00:00:00 2001 From: Chen Bowen Date: Fri, 10 Jul 2026 21:34:09 +0800 Subject: 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), -- cgit v1.2.3 From d35dfb6329accfe1cfa0b57e35214b5cbbe0f9ae Mon Sep 17 00:00:00 2001 From: Damien Laine Date: Sun, 12 Jul 2026 23:37:08 +0200 Subject: 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), -- cgit v1.2.3 From a4876f11aa1d076802676e23f8af500706e780e3 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 9 Jul 2026 10:46:32 +0200 Subject: 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 @@ err_infra: 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 @@ err_infra: 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 @@ err_reg: 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 @@ err_put_node: 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); -- cgit v1.2.3 From d52a13adbb8ccbab99cd3bad36804e87d8b5c052 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 7 Jul 2026 23:04:54 +0800 Subject: firewire: net: Fix fragmented datagram reassembly fwnet_frag_new() keeps a sorted list of received fragments for a partial datagram. When a new fragment is adjacent to an existing fragment, the code checks whether the new fragment also closes the gap to the next or previous list entry. Those neighbor lookups currently assume that the current fragment always has a real next or previous fragment. At a list edge, the next or previous entry is the list head, not a struct fwnet_fragment_info. The gap checks also compare against the old edge of the current fragment instead of the edge after adding the new fragment. As a result, a fragment that bridges two existing ranges may leave two adjacent ranges unmerged, so fwnet_pd_is_complete() can miss a complete datagram. Check for the list head before looking up the neighboring fragment, and compare the neighbor against the new fragment's far edge when deciding whether to merge all three ranges. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: c76acec6d551 ("firewire: add IPv4 support") Signed-off-by: Ruoyu Wang Link: https://lore.kernel.org/r/20260707150454.2265951-1-ruoyuw560@gmail.com Signed-off-by: Takashi Sakamoto --- drivers/firewire/net.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index e5361f4f8bbd..961f1a046025 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -297,31 +297,34 @@ static struct fwnet_fragment_info *fwnet_frag_new( if (fi->offset + fi->len == offset) { /* The new fragment can be tacked on to the end */ /* Did the new fragment plug a hole? */ - fi2 = list_entry(fi->fi_link.next, - struct fwnet_fragment_info, fi_link); - if (fi->offset + fi->len == fi2->offset) { - /* glue fragments together */ - fi->len += len + fi2->len; - list_del(&fi2->fi_link); - kfree(fi2); - } else { - fi->len += len; + if (!list_is_last(&fi->fi_link, &pd->fi_list)) { + fi2 = list_next_entry(fi, fi_link); + if (offset + len == fi2->offset) { + /* glue fragments together */ + fi->len += len + fi2->len; + list_del(&fi2->fi_link); + kfree(fi2); + + return fi; + } } + fi->len += len; return fi; } if (offset + len == fi->offset) { /* The new fragment can be tacked on to the beginning */ /* Did the new fragment plug a hole? */ - fi2 = list_entry(fi->fi_link.prev, - struct fwnet_fragment_info, fi_link); - if (fi2->offset + fi2->len == fi->offset) { - /* glue fragments together */ - fi2->len += fi->len + len; - list_del(&fi->fi_link); - kfree(fi); - - return fi2; + if (!list_is_first(&fi->fi_link, &pd->fi_list)) { + fi2 = list_prev_entry(fi, fi_link); + if (fi2->offset + fi2->len == offset) { + /* glue fragments together */ + fi2->len += fi->len + len; + list_del(&fi->fi_link); + kfree(fi); + + return fi2; + } } fi->offset = offset; fi->len += len; -- cgit v1.2.3 From d091132889c1378dd0944a72f86eae3e4da1e4fa Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Fri, 10 Jul 2026 16:08:35 +0900 Subject: ASoC: fsl_sai: Fix spurious BCLK on resume by clearing BYP When the BCLK divider ratio is 1:1, fsl_sai_set_bclk() enables bypass mode by setting BYP, but never clears the bit. The BYP=1 value remains in the regcache, and is restored by regcache_sync() on the next runtime resume. Since BYP=1 combined with BCD=1 immediately outputs the ungated MCLK as BCLK without waiting for BCE/TE/RE to be enabled, the clock is driven prematurely before the stream is fully configured, causing noise on some codecs. Fix this by clearing BYP and BCI in fsl_sai_hw_free() taking into account sync mode and the opposite stream's state, so that the regcache holds BYP=0 before runtime suspend and regcache_sync() on resume will not restore bypass mode prematurely. Fixes: a50b7926d015 ("ASoC: fsl_sai: implement 1:1 bclk:mclk ratio support") Cc: stable@vger.kernel.org Signed-off-by: Chancel Liu Reviewed-by: Shengjiu Wang Link: https://patch.msgid.link/20260710070835.3749817-1-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 9661602b53c5..d232c8f53061 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -808,6 +808,8 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; unsigned int ofs = sai->soc_data->reg_offset; + int adir = tx ? RX : TX; + int dir = tx ? TX : RX; /* Clear xMR to avoid channel swap with mclk_with_tere enabled case */ regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0); @@ -815,10 +817,29 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), FSL_SAI_CR3_TRCE_MASK, 0); - if (!sai->is_consumer_mode[tx] && - sai->mclk_streams & BIT(substream->stream)) { - clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); - sai->mclk_streams &= ~BIT(substream->stream); + if (!sai->is_consumer_mode[tx]) { + bool adir_active = !!(sai->mclk_streams & BIT(!substream->stream)); + /* + * If opposite stream provides clocks for synchronous mode and + * it is inactive, Clear BYP and BCI + */ + if (fsl_sai_dir_is_synced(sai, adir) && !adir_active) + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), + FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0); + /* + * Clear BYP and BCI of current stream if either of: + * 1. current stream doesn't provide clocks for synchronous mode + * 2. current stream provides clocks for synchronous mode but no + * more stream is active. + */ + if (!fsl_sai_dir_is_synced(sai, dir) || !adir_active) + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), + FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0); + + if (sai->mclk_streams & BIT(substream->stream)) { + clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); + sai->mclk_streams &= ~BIT(substream->stream); + } } return 0; -- cgit v1.2.3 From 0cc15f2c7a55820bc0a1c7713222d1d7ee46cab4 Mon Sep 17 00:00:00 2001 From: Shengwen Cheng Date: Fri, 26 Jun 2026 13:40:51 +0800 Subject: KVM: riscv: PMU: Bound counter mask scan to BITS_PER_LONG The PMU SBI handler passes the guest argument registers directly to the PMU start/stop helpers: kvm_riscv_vcpu_pmu_ctr_start(vcpu, cp->a0, cp->a1, cp->a2, ...) kvm_riscv_vcpu_pmu_ctr_stop(vcpu, cp->a0, cp->a1, cp->a2, ...) which map to: unsigned long ctr_base unsigned long ctr_mask unsigned long flags Thus cp->a1 is a single unsigned long ctr_mask, not a bitmap array sized for RISCV_MAX_COUNTERS. On RV32, RISCV_MAX_COUNTERS is 64 while BITS_PER_LONG is 32. Using for_each_set_bit() with RISCV_MAX_COUNTERS can therefore make find_next_bit() access bits beyond the storage of ctr_mask on RV32. Limit the scan to BITS_PER_LONG. The requested counter range is already validated by kvm_pmu_validate_counter_mask(), so this preserves RV64 behavior and avoids an out-of-bounds bitmap read on RV32. Fixes: 0cb74b65d2e5 ("RISC-V: KVM: Implement perf support without sampling") Signed-off-by: Shengwen Cheng Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260626054051.3360865-1-shengwen1997.tw@gmail.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index bb46dcbfb24d..2025b664961c 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -586,7 +586,7 @@ int kvm_riscv_vcpu_pmu_ctr_start(struct kvm_vcpu *vcpu, unsigned long ctr_base, } } /* Start the counters that have been configured and requested by the guest */ - for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) { + for_each_set_bit(i, &ctr_mask, BITS_PER_LONG) { pmc_index = array_index_nospec(i + ctr_base, RISCV_KVM_MAX_COUNTERS); if (!test_bit(pmc_index, kvpmu->pmc_in_use)) @@ -658,7 +658,7 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base, } /* Stop the counters that have been configured and requested by the guest */ - for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) { + for_each_set_bit(i, &ctr_mask, BITS_PER_LONG) { pmc_index = array_index_nospec(i + ctr_base, RISCV_KVM_MAX_COUNTERS); if (!test_bit(pmc_index, kvpmu->pmc_in_use)) -- cgit v1.2.3 From 1cc935ec2d87673e3c52ba04f943ab1276c0635b Mon Sep 17 00:00:00 2001 From: "Dylan.Wu" Date: Wed, 1 Jul 2026 03:52:39 -0400 Subject: riscv: kvm: Skip TLB flush when G-stage PTE becomes valid with Svvptc The gstage_tlb_flush() in the kvm_riscv_gstage_set_pte() is not needed when an invalid G-stage PTE becomes valid and Svvptc extension is available because new valid PTEs become visible to the page-table walker within a bounded time. Assisted-by: YuanSheng: deepseek-v4-pro Co-developed-by: Quan Zhou Signed-off-by: Quan Zhou Signed-off-by: Dylan.Wu Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260701075239.95542-1-fredwudi0305@gmail.com Signed-off-by: Anup Patel --- arch/riscv/kvm/gstage.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index c4c3b79567f1..b0474fcf065a 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -5,11 +5,13 @@ */ #include +#include #include #include #include #include #include +#include #ifdef CONFIG_64BIT unsigned long kvm_riscv_gstage_max_pgd_levels __ro_after_init = 3; @@ -171,8 +173,10 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, } if (pte_val(*ptep) != pte_val(map->pte)) { + bool was_invalid = !pte_val(*ptep); set_pte(ptep, map->pte); - if (gstage_pte_leaf(ptep)) + if (gstage_pte_leaf(ptep) && + !(was_invalid && riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC))) gstage_tlb_flush(gstage, current_level, map->addr); } -- cgit v1.2.3 From 298276da73cafd837ca9f762b3f9868216124eeb Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 6 Jul 2026 23:45:22 +0530 Subject: RISC-V: KVM: Zicbo[m|z|p] block sizes should be always present in ONE_REG All config and core registers of the KVM RISC-V ONE_REG interface are expected to be always available to the KVM user-space and the KVM get-reg-list selftest assumes these registers to be as base registers. Currently, the Zicbo[m|z|p] block size config registers are only available when corresponding ISA extension is present on the host which breaks the above expectation. In fact, KVM get-reg-list selftest fails when any of the Zicbo[m|z|p] ISA extension is not present on host. To address this issue, drop the ISA extension checks from kvm_riscv_vcpu_get/set_reg_config() and copy_config_reg_indices() functions. Fixes: 031f9efafc08 ("KVM: riscv: Add KVM_GET_REG_LIST API support") Fixes: a044ef71043e ("RISC-V: KVM: use ENOENT in *_one_reg() when extension is unavailable") Fixes: 48e2febcda74 ("RISC-V: KVM: Provide UAPI for Zicbop block size") Fixes: cf05b059d59f ("RISC-V: KVM: Introduce common kvm_riscv_isa_check_host()") Signed-off-by: Anup Patel Link: https://lore.kernel.org/r/20260706181522.2003922-1-anup.patel@oss.qualcomm.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_onereg.c | 38 ++++++-------------------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index bb920e8923c9..61988382570f 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -50,19 +50,13 @@ static int kvm_riscv_vcpu_get_reg_config(struct kvm_vcpu *vcpu, reg_val = vcpu->arch.isa[0] & KVM_RISCV_BASE_ISA_MASK; break; case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): - if (kvm_riscv_isa_check_host(ZICBOM)) - return -ENOENT; - reg_val = riscv_cbom_block_size; + reg_val = (kvm_riscv_isa_check_host(ZICBOM)) ? 0 : riscv_cbom_block_size; break; case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): - if (kvm_riscv_isa_check_host(ZICBOZ)) - return -ENOENT; - reg_val = riscv_cboz_block_size; + reg_val = (kvm_riscv_isa_check_host(ZICBOZ)) ? 0 : riscv_cboz_block_size; break; case KVM_REG_RISCV_CONFIG_REG(zicbop_block_size): - if (kvm_riscv_isa_check_host(ZICBOP)) - return -ENOENT; - reg_val = riscv_cbop_block_size; + reg_val = (kvm_riscv_isa_check_host(ZICBOP)) ? 0 : riscv_cbop_block_size; break; case KVM_REG_RISCV_CONFIG_REG(mvendorid): reg_val = vcpu->arch.mvendorid; @@ -144,21 +138,15 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu, } break; case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): - if (kvm_riscv_isa_check_host(ZICBOM)) - return -ENOENT; - if (reg_val != riscv_cbom_block_size) + if (reg_val && reg_val != riscv_cbom_block_size) return -EINVAL; break; case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): - if (kvm_riscv_isa_check_host(ZICBOZ)) - return -ENOENT; - if (reg_val != riscv_cboz_block_size) + if (reg_val && reg_val != riscv_cboz_block_size) return -EINVAL; break; case KVM_REG_RISCV_CONFIG_REG(zicbop_block_size): - if (kvm_riscv_isa_check_host(ZICBOP)) - return -ENOENT; - if (reg_val != riscv_cbop_block_size) + if (reg_val && reg_val != riscv_cbop_block_size) return -EINVAL; break; case KVM_REG_RISCV_CONFIG_REG(mvendorid): @@ -614,20 +602,6 @@ static int copy_config_reg_indices(const struct kvm_vcpu *vcpu, u64 size; u64 reg; - /* - * Avoid reporting config reg if the corresponding extension - * was not available. - */ - if (i == KVM_REG_RISCV_CONFIG_REG(zicbom_block_size) && - kvm_riscv_isa_check_host(ZICBOM)) - continue; - else if (i == KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) && - kvm_riscv_isa_check_host(ZICBOZ)) - continue; - else if (i == KVM_REG_RISCV_CONFIG_REG(zicbop_block_size) && - kvm_riscv_isa_check_host(ZICBOP)) - continue; - size = IS_ENABLED(CONFIG_32BIT) ? KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; reg = KVM_REG_RISCV | size | KVM_REG_RISCV_CONFIG | i; -- cgit v1.2.3 From 9c66085e6dcfb44b70970aa4e323003fc7f2b738 Mon Sep 17 00:00:00 2001 From: Daniel Paziyski Date: Fri, 10 Jul 2026 15:40:54 +0200 Subject: 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]" : "") ); -- cgit v1.2.3 From a2fe9dc70f3b8d5716fbcfed5fbfb9cf3948d402 Mon Sep 17 00:00:00 2001 From: Yingying Tang Date: Tue, 9 Jun 2026 22:33:15 -0700 Subject: wifi: ath12k: Fix low MLO RX throughput on WCN7850 Commit [1] introduced a regression causing severely degraded MLO RX throughput on WCN7850. On WCN7850, there is only a single ar instance, but MLO uses two link IDs. ath12k_dp_peer->hw_links[] is indexed using ar->hw_link_id, which causes both MLO link IDs to be stored at the same index. As a result, an incorrect link ID is assigned to MSDUs in ath12k_dp_rx_deliver_msdu(), leading to severe MLO RX throughput loss. Different chipsets identify the per-MSDU link differently: - On QCN9274 / IPQ5332, the host owns multiple ar instances and the per-MSDU hw_link_id from the RX descriptor maps cleanly through dp_peer->hw_links[hw_link_id] to the IEEE link_id. - On single-ar chipsets like WCN7850 / QCC2072, there is only one ar instance for both MLO links, so dp_peer->hw_links[] has just one valid slot and cannot be used to distinguish the two links. To resolve the link, walk dp_peer->link_peers[] and match by rxcb->peer_id, which on the link_peer side identifies the link peer for the MSDU. Add a new hw_op set_rx_link_id() so each chipset resolves the link on the RX fast path using whatever signal it actually has, and let the op itself decide whether to populate rx_status::link_valid and rx_status::link_id: QCN9274 / IPQ5332 : always derive link_id from dp_peer->hw_links[rxcb->hw_link_id] and set link_valid. WCN7850 / QCC2072 : walk the link_peers[] of dp_peer to find the link_peer whose peer_id matches rxcb->peer_id, and set link_valid only when a match is found. Otherwise leave link_valid clear so that mac80211 can fall back to its own link resolution path (via addr2 / deflink). For WCN7850 / QCC2072, walking dp_peer->link_peers[] is bounded by the number of links actually populated, so introduce a link_peers_map bitmap (unsigned long) in struct ath12k_dp_peer that tracks populated slots and use for_each_set_bit() to iterate. Non-MLO clients hit one slot, current MLO clients hit two; the full ATH12K_NUM_MAX_LINKS array is never scanned. The bitmap is maintained with WRITE_ONCE() on the write side (under dp_hw->peer_lock) paired with READ_ONCE() on both the lockless RX read side and the write-side RMW for KCSAN correctness. Also guard the dp_peer dereference in ath12k_mac_peer_cleanup_all() with a NULL check, since peer->dp_peer can be NULL for self-peers or peers not yet fully assigned, the pre-existing rcu_assign_pointer() call there had the same latent issue. This restores the correct link ID on WCN7850 without changing the QCN9274 / IPQ5332 data path, which keeps its O(1) hw_links[] indexing. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 11157e0910fd ("wifi: ath12k: Use ath12k_dp_peer in per packet Tx & Rx paths") # [1] Signed-off-by: Yingying Tang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260610053315.2249912-1-yingying.tang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_peer.c | 4 ++++ drivers/net/wireless/ath/ath12k/dp_peer.h | 1 + drivers/net/wireless/ath/ath12k/dp_rx.c | 7 +++--- drivers/net/wireless/ath/ath12k/hw.h | 16 +++++++++++++ drivers/net/wireless/ath/ath12k/mac.c | 10 +++++--- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 33 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h | 6 +++++ drivers/net/wireless/ath/ath12k/wifi7/hw.c | 3 +++ 8 files changed, 73 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index 47d009a0d61f..2660b4c7449b 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -570,6 +570,8 @@ int ath12k_dp_link_peer_assign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_hw, peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id); rcu_assign_pointer(dp_peer->link_peers[peer->link_id], peer); + WRITE_ONCE(dp_peer->link_peers_map, + READ_ONCE(dp_peer->link_peers_map) | BIT(peer->link_id)); rcu_assign_pointer(dp_hw->dp_peers[peerid_index], dp_peer); @@ -632,6 +634,8 @@ void ath12k_dp_link_peer_unassign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_ peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id); rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL); + WRITE_ONCE(dp_peer->link_peers_map, + READ_ONCE(dp_peer->link_peers_map) & ~BIT(peer->link_id)); rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL); diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h index f5067e66f1e1..7c9709bf717b 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.h +++ b/drivers/net/wireless/ath/ath12k/dp_peer.h @@ -140,6 +140,7 @@ struct ath12k_dp_peer { /* Info used in MMIC verification of * RX fragments */ struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; + unsigned long link_peers_map; struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS]; struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 06e74124e57e..8fa0e90b4531 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1384,10 +1384,9 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc pubsta = peer ? peer->sta : NULL; - if (pubsta && pubsta->valid_links) { - status->link_valid = 1; - status->link_id = peer->hw_links[rxcb->hw_link_id]; - } + status->link_valid = 0; + if (pubsta && pubsta->valid_links) + ath12k_hw_set_rx_link_id(dp->hw_params, peer, rxcb, status); ath12k_dbg(dp->ab, ATH12K_DBG_DATA, "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index d135b2936378..86fb8b719613 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -13,6 +13,10 @@ #include "wmi.h" #include "hal.h" +struct ath12k_dp_peer; +struct ath12k_skb_rxcb; +struct ieee80211_rx_status; + /* Target configuration defines */ /* Num VDEVS per radio */ @@ -243,6 +247,9 @@ struct ath12k_hw_ops { bool (*dp_srng_is_tx_comp_ring)(int ring_num); bool (*is_frame_link_agnostic)(struct ath12k_link_vif *arvif, struct ieee80211_mgmt *mgmt); + void (*set_rx_link_id)(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); }; static inline @@ -273,6 +280,15 @@ static inline int ath12k_hw_mac_id_to_srng_id(const struct ath12k_hw_params *hw, return 0; } +static inline void ath12k_hw_set_rx_link_id(const struct ath12k_hw_params *hw, + struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status) +{ + if (hw->hw_ops->set_rx_link_id) + hw->hw_ops->set_rx_link_id(dp_peer, rxcb, status); +} + struct ath12k_fw_ie { __le32 id; __le32 len; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index af354bef5c0d..51c4df32e716 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1238,9 +1238,13 @@ void ath12k_mac_peer_cleanup_all(struct ath12k *ar) /* cleanup dp peer */ spin_lock_bh(&dp_hw->peer_lock); dp_peer = peer->dp_peer; - peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id); - rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL); - rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL); + if (dp_peer) { + peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id); + rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL); + WRITE_ONCE(dp_peer->link_peers_map, + READ_ONCE(dp_peer->link_peers_map) & ~BIT(peer->link_id)); + rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL); + } spin_unlock_bh(&dp_hw->peer_lock); ath12k_dp_link_peer_rhash_delete(dp, peer); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index cb9dd8cb28b6..95d87dd67872 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -5,6 +5,7 @@ */ #include "dp_rx.h" +#include "../dp_peer.h" #include "../dp_tx.h" #include "../peer.h" #include "hal_qcn9274.h" @@ -2315,3 +2316,35 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, return tlv_tag == HAL_RX_MPDU_START; } + +void +ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status) +{ + status->link_valid = 1; + status->link_id = dp_peer->hw_links[rxcb->hw_link_id]; +} + +void +ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status) +{ + struct ath12k_dp_link_peer *link_peer; + unsigned long links_map; + int i; + + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), + "ath12k set rx link id called without rcu lock"); + + links_map = READ_ONCE(dp_peer->link_peers_map); + for_each_set_bit(i, &links_map, ATH12K_NUM_MAX_LINKS) { + link_peer = rcu_dereference(dp_peer->link_peers[i]); + if (link_peer && link_peer->peer_id == rxcb->peer_id) { + status->link_valid = 1; + status->link_id = link_peer->link_id; + return; + } + } +} diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h index 8aa79faf567f..1d3a4788a2dd 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h @@ -57,4 +57,10 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, struct hal_rx_desc *rx_desc); int ath12k_wifi7_dp_rx_tid_delete_handler(struct ath12k_base *ab, struct ath12k_dp_rx_tid_rxq *rx_tid); +void ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); +void ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); #endif diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index 3d59fa452ec0..d9fdd2fc8298 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -158,6 +158,7 @@ static const struct ath12k_hw_ops qcn9274_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_qcn9274, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_qcn9274, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_qcn9274, + .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_qcn9274, }; static const struct ath12k_hw_ops wcn7850_ops = { @@ -168,6 +169,7 @@ static const struct ath12k_hw_ops wcn7850_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850, + .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850, }; static const struct ath12k_hw_ops qcc2072_ops = { @@ -178,6 +180,7 @@ static const struct ath12k_hw_ops qcc2072_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850, + .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850, }; #define ATH12K_TX_RING_MASK_0 0x1 -- cgit v1.2.3 From 70231dcd782201579990ded73e0435d18bb524ca Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Mon, 15 Jun 2026 13:21:03 +0200 Subject: wifi: ath12k: fix NULL pointer dereference in rhash table destroy When unbinding the ath12k driver, kernel NULL pointer dereferences occur in irq_work_sync() called from rhashtable_destroy(). Two hash tables are affected: 1. ath12k_link_sta hash table in ath12k_base 2. ath12k_dp_link_peer hash table in ath12k_dp The issue happens because the destroy functions are called unconditionally in cleanup paths, but the hash tables are only initialized late in their respective init functions. If the device was never fully started or if the init functions failed before initializing the hash tables, the pointers will be NULL. The issues are always reproducible from a VM because the MSI addressing initialization is failing. Call trace for ath12k_link_sta_rhash_tbl_destroy: RIP: irq_work_sync+0x1e/0x70 rhashtable_destroy+0x12/0x60 ath12k_link_sta_rhash_tbl_destroy+0x19/0x40 [ath12k] ath12k_core_stop+0xe/0x80 [ath12k] ath12k_core_hw_group_cleanup+0x6b/0xb0 [ath12k] ath12k_pci_remove+0x60/0x110 [ath12k] Call trace for ath12k_dp_link_peer_rhash_tbl_destroy: RIP: irq_work_sync+0x1e/0x70 rhashtable_destroy+0x12/0x60 ath12k_dp_link_peer_rhash_tbl_destroy+0x29/0x50 [ath12k] ath12k_dp_cmn_device_deinit+0x21/0x140 [ath12k] ath12k_core_hw_group_cleanup+0x6b/0xb0 [ath12k] ath12k_pci_remove+0x60/0x110 [ath12k] Fix this by adding NULL checks before calling rhashtable_destroy() in both destroy functions. The NULL check approach was chosen because the rhashtable pointer serves as the initialization state indicator. The init can fail at various points, leaving some components uninitialized. Checking the pointer directly is simpler than adding separate state flags that would need synchronization. Fixes: 57ccca410237 ("wifi: ath12k: Add hash table for ath12k_link_sta in ath12k_base") Fixes: a88cf5f71adf ("wifi: ath12k: Add hash table for ath12k_dp_link_peer") Cc: stable@vger.kernel.org Signed-off-by: Jose Ignacio Tornos Martinez Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260615112103.601982-1-jtornosm@redhat.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_peer.c | 7 +++++-- drivers/net/wireless/ath/ath12k/peer.c | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index 2660b4c7449b..a12073afc307 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -274,11 +274,14 @@ int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp) void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp) { - mutex_lock(&dp->link_peer_rhash_tbl_lock); + guard(mutex)(&dp->link_peer_rhash_tbl_lock); + + if (!dp->rhead_peer_addr) + return; + rhashtable_destroy(dp->rhead_peer_addr); kfree(dp->rhead_peer_addr); dp->rhead_peer_addr = NULL; - mutex_unlock(&dp->link_peer_rhash_tbl_lock); } static int ath12k_dp_link_peer_rhash_insert(struct ath12k_dp *dp, diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index c222bdaa333c..2681a047d4d5 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -453,6 +453,9 @@ err_free: void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab) { + if (!ab->rhead_sta_addr) + return; + rhashtable_destroy(ab->rhead_sta_addr); kfree(ab->rhead_sta_addr); ab->rhead_sta_addr = NULL; -- cgit v1.2.3 From 44126b6994eeb28f2103b638e698f40a1244f327 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 2 Jul 2026 00:50:20 +0000 Subject: wifi: ath6kl: fix OOB access from firmware ADDBA window size aggr_recv_addba_req_evt() logs a debug message when the firmware-supplied win_sz is outside [AGGR_WIN_SZ_MIN, AGGR_WIN_SZ_MAX] but does not return. The out-of-range win_sz is then used in TID_WINDOW_SZ() to compute a kzalloc size and stored in rxtid->hold_q_sz, leading to zero-size or overflowed allocations and subsequent out-of-bounds access. Clean up any previously active aggregation session for the TID first, then return early when win_sz is out of the valid range, instead of proceeding with a broken allocation size. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Cc: stable@vger.kernel.org Reviewed-by: Vasanthakumar Thiagarajan Signed-off-by: Tristan Madani Link: https://patch.msgid.link/20260702005020.708717-1-tristmd@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath6kl/txrx.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 0e268017af52..d81825413906 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1723,13 +1723,15 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no, rxtid = &aggr_conn->rx_tid[tid]; - if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) - ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n", - __func__, win_sz, tid); - if (rxtid->aggr) aggr_delete_tid_state(aggr_conn, tid); + if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) { + ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n", + __func__, win_sz, tid); + return; + } + rxtid->seq_next = seq_no; hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q); rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL); -- cgit v1.2.3 From 3a21c89215cc18f1a97c5e5bfd1da6d4f3d44495 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Thu, 25 Jun 2026 23:29:07 +0000 Subject: wifi: ath6kl: fix OOB read from firmware num_msg in TX complete handler The firmware-controlled num_msg field (u8, 0-255) drives the loop in ath6kl_wmi_tx_complete_event_rx() without validation against the buffer length. This allows out-of-bounds reads of up to 1020 bytes past the WMI event buffer when the firmware sends an inflated num_msg. Add a check that the buffer is large enough to hold the fixed struct and the num_msg variable-length entries. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Signed-off-by: Tristan Madani Link: https://patch.msgid.link/20260625232907.3620746-1-tristmd@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath6kl/wmi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 72611a2ceb9d..7e65a03be0b7 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -484,6 +484,18 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len) evt = (struct wmi_tx_complete_event *) datap; + if (len < sizeof(*evt)) { + ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d\n", + len); + return -EINVAL; + } + + if (len < sizeof(*evt) + evt->num_msg * sizeof(struct tx_complete_msg_v1)) { + ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d for %u msgs\n", + len, evt->num_msg); + return -EINVAL; + } + ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n", evt->num_msg, evt->msg_len, evt->msg_type); -- cgit v1.2.3 From 6b47b29730de3232b919d8362749f6814c5f2a33 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:08 +0000 Subject: wifi: ath6kl: fix OOB read from firmware IE lengths in connect event The firmware-controlled beacon_ie_len, assoc_req_len, and assoc_resp_len fields in ath6kl_wmi_connect_event_rx() are not validated against the buffer length. Their sum (up to 765) can exceed the actual WMI event data, causing out-of-bounds reads during IE parsing and state corruption of wmi->is_wmm_enabled. Add a check that the total IE length fits within the buffer. Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") Signed-off-by: Tristan Madani Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260421135009.348084-3-tristmd@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath6kl/wmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 7e65a03be0b7..2b0c5038ae04 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -874,6 +874,14 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, ev = (struct wmi_connect_event *) datap; + if (len < sizeof(*ev) + ev->beacon_ie_len + + ev->assoc_req_len + ev->assoc_resp_len) { + ath6kl_dbg(ATH6KL_DBG_WMI, + "connect event: IE lengths %u+%u+%u exceed buffer %d\n", + ev->beacon_ie_len, ev->assoc_req_len, + ev->assoc_resp_len, len); + return -EINVAL; + } if (vif->nw_type == AP_NETWORK) { /* AP mode start/STA connected event */ struct net_device *dev = vif->ndev; -- cgit v1.2.3 From 4cde55b2feff9504d1f993ab80e84e7ccb62791c Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:49:26 +0000 Subject: wifi: carl9170: bound memcpy length in cmd callback to prevent OOB read When the firmware sends a command response with a length mismatch, carl9170_cmd_callback() logs the mismatch and calls carl9170_restart() but then falls through to memcpy(ar->readbuf, buffer + 4, len - 4). Since len comes from the firmware and can exceed ar->readlen, this copies more data than the readbuf was allocated for. Bound the memcpy to min(len - 4, ar->readlen) so that the response is still completed -- avoiding repeated restarts from queued garbage -- while preventing an overread past the response buffer. Fixes: a84fab3cbfdc ("carl9170: 802.11 rx/tx processing and usb backend") Signed-off-by: Tristan Madani Acked-by: Christian Lamparter Closes: https://syzkaller.appspot.com/bug?extid=5c1ca6ccaa1215781cac Link: https://patch.msgid.link/20260421134929.325662-2-tristmd@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/carl9170/rx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 6833430130f4..f6855efc05c0 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -150,7 +150,8 @@ static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer) spin_lock(&ar->cmd_lock); if (ar->readbuf) { if (len >= 4) - memcpy(ar->readbuf, buffer + 4, len - 4); + memcpy(ar->readbuf, buffer + 4, + min_t(u32, len - 4, ar->readlen)); ar->readbuf = NULL; } -- cgit v1.2.3 From a3f42f1049ad80c65560d2b078ad426c3134f78d Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:49:27 +0000 Subject: wifi: carl9170: fix OOB read from off-by-two in TX status handler The bounds check in carl9170_tx_process_status() uses `i > ((cmd->hdr.len / 2) + 1)` which is off by two, allowing 2 extra iterations past valid _tx_status entries when the firmware- controlled hdr.ext exceeds hdr.len/2. Fix by using the correct comparison `i >= (cmd->hdr.len / 2)`. Fixes: a84fab3cbfdc ("carl9170: 802.11 rx/tx processing and usb backend") Signed-off-by: Tristan Madani Acked-by: Christian Lamparter Closes: https://syzkaller.appspot.com/bug?extid=5c1ca6ccaa1215781cac Link: https://patch.msgid.link/20260421134929.325662-3-tristmd@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/carl9170/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 59caf1e4b158..06aaf281655b 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -692,7 +692,7 @@ void carl9170_tx_process_status(struct ar9170 *ar, unsigned int i; for (i = 0; i < cmd->hdr.ext; i++) { - if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) { + if (WARN_ON(i >= (cmd->hdr.len / 2))) { print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE, (void *) cmd, cmd->hdr.len + 4); break; -- cgit v1.2.3 From a1a21995c2e1cc2ca6b2226cfe4f5f018370182a Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:49:28 +0000 Subject: wifi: carl9170: fix buffer overflow in rx_stream failover path The failover continuation in carl9170_rx_stream() copies the full tlen from the second USB transfer instead of capping at rx_failover_missing bytes. When both transfers are near maximum size, the total exceeds the 65535-byte failover SKB, triggering skb_over_panic. Limit the copy size to the missing byte count. Fixes: a84fab3cbfdc ("carl9170: 802.11 rx/tx processing and usb backend") Signed-off-by: Tristan Madani Acked-by: Christian Lamparter Closes: https://syzkaller.appspot.com/bug?extid=5c1ca6ccaa1215781cac Link: https://patch.msgid.link/20260421134929.325662-4-tristmd@gmail.com [Fix checkpatch CHECK:PARENTHESIS_ALIGNMENT] Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/carl9170/rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index f6855efc05c0..0383d5c9698b 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -918,7 +918,9 @@ static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len) } } - skb_put_data(ar->rx_failover, tbuf, tlen); + skb_put_data(ar->rx_failover, tbuf, + min_t(unsigned int, tlen, + ar->rx_failover_missing)); ar->rx_failover_missing -= tlen; if (ar->rx_failover_missing <= 0) { -- cgit v1.2.3 From c90164ca0f7036942ba088eb7ea8d3f6c2352020 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 4 Jul 2026 16:10:30 -0700 Subject: 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); -- cgit v1.2.3 From 2bf282f8f715f5d05d6f4c49ffb3bd241c5e667e Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Tue, 7 Jul 2026 12:15:18 +0800 Subject: 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 @@ unlock: 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); + + 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; - return hci_le_conn_update_sync(hdev, conn, params); + 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); + } } } -- cgit v1.2.3 From 609c5b04a28dc1b0f3af6a7bc93055135b2d2059 Mon Sep 17 00:00:00 2001 From: Laxman Acharya Padhya Date: Fri, 10 Jul 2026 23:10:03 +0545 Subject: 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 -- cgit v1.2.3 From d5efd6e4b8b0634af6843178fe1a7dd2b2178a3d Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:40 +0300 Subject: 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 | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 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)) + 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; -- cgit v1.2.3 From 16cd66443957e4ad42155c6fec401012f600c6f8 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:41 +0300 Subject: 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; } -- cgit v1.2.3 From da55f570191d5d72f10c607a7043b947eb05ea46 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:42 +0300 Subject: 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); } -- cgit v1.2.3 From c363202ec841df36421ec280eea3d5f94f556143 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 10 Jul 2026 11:23:43 +0300 Subject: 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 @@ unlock: } 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 -- cgit v1.2.3 From bf587a10c33e5571a299742e45bc18960b9912e7 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 9 Jul 2026 14:22:50 +0800 Subject: 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; -- cgit v1.2.3 From c1cec2bbbeb5922d42d28c6af1707c4f3f8647e3 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Fri, 10 Jul 2026 14:47:31 +0500 Subject: 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 @@ unlock: 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; } -- cgit v1.2.3 From f52524da7084c1a54683ae9fbc73e93fff19dd64 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 13 Jul 2026 18:03:29 +0800 Subject: 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. */ -- cgit v1.2.3 From 5caf27a2bf7f86f92f03e851d252901b64ed498c Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Mon, 13 Jul 2026 12:35:50 +0000 Subject: 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 */ -- cgit v1.2.3 From 806c00c23e3ce8eae397a40ced536ef88ae4e012 Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Sat, 11 Jul 2026 19:54:02 -0700 Subject: 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; } -- cgit v1.2.3 From 5906d0e82e8e07d756f3ed1abfac8f8cea2c20dc Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:55 +0800 Subject: 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, -- cgit v1.2.3 From b09ae45d85dc816987a71db9eebc54b0ae288e94 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:56 +0800 Subject: 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); -- cgit v1.2.3 From 9e4ec3be67af41ab859302d7109b34976efd9258 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:57 +0800 Subject: 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; -- cgit v1.2.3 From 7a06d3b816d73448b4e38b83d65049f090b7b201 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:58 +0800 Subject: 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); -- cgit v1.2.3 From 5bd1d3dcc25a51663b3878cbc506e16be15af354 Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Fri, 3 Jul 2026 13:32:59 +0800 Subject: 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; -- cgit v1.2.3 From 8986c932905ea508d66da421eb2eb6e676ace1fe Mon Sep 17 00:00:00 2001 From: Shoichiro Miyamoto Date: Sat, 11 Jul 2026 22:33:26 +0900 Subject: 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 @@ calc_size_exit: 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 */ -- cgit v1.2.3 From d489a5305b9d5480d6fb97d5636f5f4b1e0b3827 Mon Sep 17 00:00:00 2001 From: Jason Macnak Date: Thu, 25 Jun 2026 10:08:28 -0700 Subject: drm/virtio: Don't detach GEM from a non-created context Applies the same treatment as commit 7cf6dd467e87 ("drm/virtio: Don't attach GEM to a non-created context in gem_object_open()") to virtio_gpu_gem_object_close() to avoid trying to detach a resource that was never attached due to a context never being created when context_init is supported. Fixes: 086b9f27f0ab ("drm/virtio: Don't create a context with default param if context_init is supported") Cc: # v6.14+ Signed-off-by: Jason Macnak Signed-off-by: Dmitry Osipenko Link: https://patch.msgid.link/20260625170828.3335431-1-natsu@google.com --- drivers/gpu/drm/virtio/virtgpu_gem.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c index 435d37d36034..66c3f6f74e9c 100644 --- a/drivers/gpu/drm/virtio/virtgpu_gem.c +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -139,13 +139,15 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj, if (!vgdev->has_virgl_3d) return; - objs = virtio_gpu_array_alloc(1); - if (!objs) - return; - virtio_gpu_array_add_obj(objs, obj); + if (vfpriv->context_created) { + objs = virtio_gpu_array_alloc(1); + if (!objs) + return; + virtio_gpu_array_add_obj(objs, obj); - virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id, - objs); + virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id, + objs); + } virtio_gpu_notify(vgdev); } -- cgit v1.2.3 From d1b894c5bbb3fee0012bd14356286dc2384e8213 Mon Sep 17 00:00:00 2001 From: Ryosuke Yasuoka Date: Mon, 13 Jul 2026 22:01:00 +0900 Subject: drm/virtio: fix deadlock in display_info_cb by removing hotplug from dequeue worker A probe-time deadlock can occur between the dequeue worker and drm_client_register(). During probe, drm_client_register() holds clientlist_mutex and calls the fbdev hotplug callback, which triggers an atomic commit that ends up sleeping in virtio_gpu_queue_ctrl_sgs() waiting for virtqueue space. The dequeue worker that would free that space calls virtio_gpu_cmd_get_display_info_cb(), which invokes drm_kms_helper_hotplug_event() -> drm_client_dev_hotplug(), attempting to acquire the same clientlist_mutex. Since wake_up() is only called after the resp_cb loop, the probe thread is never woken and both threads deadlock. Fix this by removing the hotplug notification from virtio_gpu_cmd_get_display_info_cb(). The display data (outputs[i].info) is still updated synchronously in the callback. For the init path, drm_client_register() already fires an initial hotplug when the client is registered, which picks up the connector state updated by display_info_cb. For the runtime config_changed path, add a wait_event_timeout() in config_changed_work_func() so that display_info_cb updates the connector data before the hotplug notification is sent. Also replace drm_helper_hpd_irq_event() with drm_kms_helper_hotplug_event() since virtio-gpu never calls drm_kms_helper_poll_init() and thus drm_helper_hpd_irq_event() always returns false without doing anything. Fixes: 27655b9bb9f0 ("drm/client: Send hotplug event after registering a client") Closes: https://syzkaller.appspot.com/bug?id=d6dd6f86d3aaf7eebe7406e45c1c6e549453f224 Closes: https://syzkaller.appspot.com/bug?id=908bd910da5dd79b88de4cf7baf376cc873a922e Suggested-by: Dmitry Osipenko Signed-off-by: Ryosuke Yasuoka Signed-off-by: Dmitry Osipenko Link: https://patch.msgid.link/20260713-virtiogpu_syzbot-v2-1-2958fa37d46d@redhat.com --- drivers/gpu/drm/virtio/virtgpu_kms.c | 5 ++++- drivers/gpu/drm/virtio/virtgpu_vq.c | 3 --- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index cfde9f573df6..b4329f28e976 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -49,7 +49,10 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) virtio_gpu_cmd_get_edids(vgdev); virtio_gpu_cmd_get_display_info(vgdev); virtio_gpu_notify(vgdev); - drm_helper_hpd_irq_event(vgdev->ddev); + wait_event_timeout(vgdev->resp_wq, + !vgdev->display_info_pending, + 5 * HZ); + drm_kms_helper_hotplug_event(vgdev->ddev); } events_clear |= VIRTIO_GPU_EVENT_DISPLAY; } diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index c8b9475a7472..e5e1af8b8e8a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -840,9 +840,6 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev, vgdev->display_info_pending = false; spin_unlock(&vgdev->display_info_lock); wake_up(&vgdev->resp_wq); - - if (!drm_helper_hpd_irq_event(vgdev->ddev)) - drm_kms_helper_hotplug_event(vgdev->ddev); } static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev, -- cgit v1.2.3 From ba5c0f28a26e7d9be1e0997f8920dd638e2782fd Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Fri, 10 Jul 2026 20:29:52 +0800 Subject: iommufd: Fix wrong hwpt passed to iommufd_auto_response_faults on replace iommufd_hwpt_replace_device() calls: iommufd_auto_response_faults(hwpt, old_handle); passing the *new* hwpt together with the handle of the device's *old* domain. This should be a parameter mismatch: 1. Semantically, iommufd_auto_response_faults(x, handle) scans x->fault's deliver list and response xarray for groups matching "handle". A group is queued under the hwpt that was attached at fault-delivery time. old_handle is fetched *before* the domain switch, so its group lives on old->fault, not on the new hwpt->fault. 2. Historically, the first argument was "old". The routine was introduced by commit b7d8833677ba ("iommufd: Fault-capable hwpt attach/detach/replace") as __fault_domain_replace_dev() in fault.c, correctly calling iommufd_auto_response_faults(old, curr). Commit fb21b1568ada ("iommufd: Make attach_handle generic than fault specific") moved this into iommufd_hwpt_replace_device() in device.c and swapped it to "hwpt". This should be a refactor regression, not an intentional change. Fix this by passing "old" instead. Link: https://patch.msgid.link/r/9D652384339C69D5+20260710122952.885325-1-peiyang_he@smail.nju.edu.cn Fixes: fb21b1568ada ("iommufd: Make attach_handle generic than fault specific") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 170a7005f0bc..2895e5370910 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -589,7 +589,7 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev, if (rc) goto out_free_handle; - iommufd_auto_response_faults(hwpt, old_handle); + iommufd_auto_response_faults(old, old_handle); kfree(old_handle); return 0; -- cgit v1.2.3 From 5f3fc0ad9a41883a62098359b9fdbe4257f20e53 Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Thu, 9 Jul 2026 13:08:00 +0800 Subject: iommufd: Reject DMABUF pages from the access pin path DMABUF pages are not supported for iommufd access pinning. iommufd_access_pin_pages() returns struct page pointers for in-kernel CPU access, but DMABUF-backed iopt_pages do not carry a userspace address that can be passed to the GUP path. iopt_pages_rw_access() already rejects IOPT_ADDRESS_DMABUF before doing CPU access. Apply the same rejection to iopt_area_add_access() before it takes pages->mutex and calls iopt_pages_fill_xarray(). Otherwise a DMABUF-backed iopt_pages can reach the hole-fill path, where pfn_reader_user_pin() interprets the union as uptr and calls pin_user_pages_fast()/pin_user_pages_remote(). This fix also avoids the lockdep warning reported from that path, where pages_dmabuf_mutex_key is held while gup_fast_fallback() may acquire mmap_lock. Link: https://patch.msgid.link/r/CD68F549BF3761B7+20260709050800.520607-1-peiyang_he@smail.nju.edu.cn Reported-by: Peiyang He Closes: https://lore.kernel.org/all/E8540D7D05768C91+8b2ef227-3368-494e-909d-7b28e1489dfb@smail.nju.edu.cn/ Fixes: 71db84a092c3 ("iommufd: Add DMABUF to iopt_pages") Cc: stable@vger.kernel.org Tested-by: Peiyang He Signed-off-by: Peiyang He Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/pages.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c index 03c8379bbc34..404f31d8f729 100644 --- a/drivers/iommu/iommufd/pages.c +++ b/drivers/iommu/iommufd/pages.c @@ -2451,6 +2451,9 @@ int iopt_area_add_access(struct iopt_area *area, unsigned long start_index, if ((flags & IOMMUFD_ACCESS_RW_WRITE) && !pages->writable) return -EPERM; + if (iopt_is_dmabuf(pages)) + return -EINVAL; + mutex_lock(&pages->mutex); access = iopt_pages_get_exact_access(pages, start_index, last_index); if (access) { -- cgit v1.2.3 From b6ea9680f8c101967caf9981c2980b80b818ccbf Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Mon, 13 Jul 2026 11:29:52 -0600 Subject: riscv: mm: Make mark_new_valid_map() stuff depend on 64BIT && MMU None of the code relating to mark_new_valid_map() does anything useful without CONFIG_64BIT=y && CONFIG_MMU=y, because the new_valid_map_cpus_check code is only used if CONFIG_64BIT, and the exception codes checked there can only happen with CONFIG_MMU=y. Therefore, make these conditional on CONFIG_64BIT=y && CONFIG_MMU=y to simplify programming, since we do not have to handle CONFIG_MMU=n when changing this code in the future. This also removes some unused code on the entry path for CONFIG_MMU=n. Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260713-mark-after-vmemmap-populate-v6-1-b945ceba29d4@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cacheflush.h | 2 +- arch/riscv/kernel/entry.S | 2 +- arch/riscv/mm/init.c | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 8cfe59483a8f..58e787fad029 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -40,7 +40,7 @@ do { \ flush_icache_mm(vma->vm_mm, 0); \ } while (0) -#ifdef CONFIG_64BIT +#if defined(CONFIG_64BIT) && defined(CONFIG_MMU) /* This is accessed in assembly code. cpumask_var_t would be too complex. */ extern DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); extern char _end[]; diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 08df724e13b9..d799c4e56f80 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -137,7 +137,7 @@ SYM_CODE_START(handle_exception) .Lrestore_kernel_tpsp: csrr tp, CSR_SCRATCH -#ifdef CONFIG_64BIT +#if defined(CONFIG_64BIT) && defined(CONFIG_MMU) /* * The RISC-V kernel does not flush TLBs on all CPUS after each new * vmalloc mapping or kfence_unprotect(), which may result in diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 5b1b3c88b4d1..3e450890be07 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -37,7 +37,9 @@ #include "../kernel/head.h" +#if defined(CONFIG_64BIT) && defined(CONFIG_MMU) DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); +#endif struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); -- cgit v1.2.3 From 4edd70ee6a7d0408a4e3ac921185779e7605f29c Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Mon, 13 Jul 2026 11:29:52 -0600 Subject: mm/sparse-vmemmap: flush_cache_vmap() after hotplugging vmemmap section_activate() does not flush TLB after populating new vmemmap pages. On most architectures, this is okay. However it is a problem on RISC-V since there the TLB caching non-present entries is permitted, which causes spurious faults on some hardwares. This seems to be most easily reproduced with DEBUG_VM=y and PAGE_POISONING=y, which causes these newly mapped struct pages to be poisoned i.e. written to immediately after mapping. Extend the RISC-V flush_cache_vmap() to also handle the vmemmap range, and call it after hotplugging vmemmap, which gets the possible spurious fault handled in the exception handler. At least for now, the only other architecture with both SPARSEMEM_VMEMMAP and flush_cache_vmap() is PowerPC, which has a similar problem with newly valid PTEs. But there flush_cache_vmap() is just a ptesync. So it should be safe to do this for generic code while having minimal performance impact. Suggested-by: Muchun Song Signed-off-by: Vivian Wang Reviewed-by: Muchun Song Acked-by: David Hildenbrand (Arm) Link: https://patch.msgid.link/20260713-mark-after-vmemmap-populate-v6-2-b945ceba29d4@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cacheflush.h | 3 ++- mm/sparse-vmemmap.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 58e787fad029..c2b0a2928f06 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -56,7 +56,8 @@ static inline void mark_new_valid_map(void) #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) { - if (is_vmalloc_or_module_addr((void *)start)) + if (is_vmalloc_or_module_addr((void *)start) || + (start >= VMEMMAP_START && end <= VMEMMAP_END)) mark_new_valid_map(); } #define flush_cache_vmap_early(start, end) local_flush_tlb_kernel_range(start, end) diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 99e2be39671b..ebd3ac997f64 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -564,6 +564,8 @@ struct page * __meminit __populate_section_memmap(unsigned long pfn, if (r < 0) return NULL; + flush_cache_vmap(start, end); + return pfn_to_page(pfn); } -- cgit v1.2.3 From 3a2694bf6ac8e47b3814293e80343f58fc72937f Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Mon, 6 Jul 2026 21:04:14 +0800 Subject: riscv: Gate FUNCTION_ALIGNMENT_4B on DYNAMIC_FTRACE The FUNCTION_ALIGNMENT_4B select forces the whole kernel to be built with -fmin-function-alignment=4. This alignment is only needed so the patchable-function-entry NOPs, which arch/riscv/Makefile emits under CONFIG_DYNAMIC_FTRACE, can be patched reliably on RISCV_ISA_C=y builds where compressed instructions otherwise allow 2-byte function alignment. The select is currently gated on HAVE_DYNAMIC_FTRACE, a capability bit that is selected whenever the toolchain supports dynamic ftrace, rather than on whether tracing is actually enabled. As a result every RISCV_ISA_C=y build gets 4-byte function alignment across the entire kernel even when function tracing is disabled, needlessly growing the kernel image and wasting instruction cache for a feature that is not in use. Gate the select on DYNAMIC_FTRACE instead, matching the condition under which arch/riscv/Makefile emits -fpatchable-function-entry, so the alignment is only applied when it is actually needed. Fixes: c41bf4326c7b ("riscv: ftrace: align patchable functions to 4 Byte boundary") Signed-off-by: Rui Qi Link: https://patch.msgid.link/20260706130415.463682-1-qirui.001@bytedance.com Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c0a6992933e4..f7028caaeae0 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -157,7 +157,7 @@ config RISCV select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_CONTIGUOUS if MMU select HAVE_DYNAMIC_FTRACE if MMU && (CLANG_SUPPORTS_DYNAMIC_FTRACE || GCC_SUPPORTS_DYNAMIC_FTRACE) - select FUNCTION_ALIGNMENT_4B if HAVE_DYNAMIC_FTRACE && RISCV_ISA_C + select FUNCTION_ALIGNMENT_4B if DYNAMIC_FTRACE && RISCV_ISA_C select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS if HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS if (DYNAMIC_FTRACE_WITH_ARGS && !CFI) select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE -- cgit v1.2.3 From 6dc3934152d1cc48b0395264d0061ebb4aed359e Mon Sep 17 00:00:00 2001 From: Yunhui Cui Date: Fri, 3 Jul 2026 20:28:30 +0800 Subject: riscv: io: avoid null-pointer arithmetic in PIO helpers When port I/O is not supported, exposing the port-string helpers is both unnecessary and can make clang diagnose null-pointer arithmetic from the PCI_IOBASE based address expression. Keep the MMIO string helpers available as before, but only provide the port I/O variants when CONFIG_HAS_IOPORT is enabled. Signed-off-by: Yunhui Cui Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20260703122832.15984-2-cuiyunhui@bytedance.com Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/io.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/riscv/include/asm/io.h b/arch/riscv/include/asm/io.h index 09bb5f57a9d3..92d5f831f349 100644 --- a/arch/riscv/include/asm/io.h +++ b/arch/riscv/include/asm/io.h @@ -102,12 +102,14 @@ __io_reads_ins(reads, u32, l, __io_br(), __io_ar(addr)) #define readsw(addr, buffer, count) __readsw(addr, buffer, count) #define readsl(addr, buffer, count) __readsl(addr, buffer, count) +#ifdef CONFIG_HAS_IOPORT __io_reads_ins(ins, u8, b, __io_pbr(), __io_par(addr)) __io_reads_ins(ins, u16, w, __io_pbr(), __io_par(addr)) __io_reads_ins(ins, u32, l, __io_pbr(), __io_par(addr)) #define insb(addr, buffer, count) __insb(PCI_IOBASE + (addr), buffer, count) #define insw(addr, buffer, count) __insw(PCI_IOBASE + (addr), buffer, count) #define insl(addr, buffer, count) __insl(PCI_IOBASE + (addr), buffer, count) +#endif __io_writes_outs(writes, u8, b, __io_bw(), __io_aw()) __io_writes_outs(writes, u16, w, __io_bw(), __io_aw()) @@ -116,26 +118,32 @@ __io_writes_outs(writes, u32, l, __io_bw(), __io_aw()) #define writesw(addr, buffer, count) __writesw(addr, buffer, count) #define writesl(addr, buffer, count) __writesl(addr, buffer, count) +#ifdef CONFIG_HAS_IOPORT __io_writes_outs(outs, u8, b, __io_pbw(), __io_paw()) __io_writes_outs(outs, u16, w, __io_pbw(), __io_paw()) __io_writes_outs(outs, u32, l, __io_pbw(), __io_paw()) #define outsb(addr, buffer, count) __outsb(PCI_IOBASE + (addr), buffer, count) #define outsw(addr, buffer, count) __outsw(PCI_IOBASE + (addr), buffer, count) #define outsl(addr, buffer, count) __outsl(PCI_IOBASE + (addr), buffer, count) +#endif #ifdef CONFIG_64BIT __io_reads_ins(reads, u64, q, __io_br(), __io_ar(addr)) #define readsq(addr, buffer, count) __readsq(addr, buffer, count) +#ifdef CONFIG_HAS_IOPORT __io_reads_ins(ins, u64, q, __io_pbr(), __io_par(addr)) #define insq(addr, buffer, count) __insq(PCI_IOBASE + (addr), buffer, count) +#endif __io_writes_outs(writes, u64, q, __io_bw(), __io_aw()) #define writesq(addr, buffer, count) __writesq(addr, buffer, count) +#ifdef CONFIG_HAS_IOPORT __io_writes_outs(outs, u64, q, __io_pbr(), __io_paw()) #define outsq(addr, buffer, count) __outsq(PCI_IOBASE + (addr), buffer, count) #endif +#endif #include -- cgit v1.2.3 From ad6dcfa023762e37962f77ee48e752b7570e9440 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 1 Jul 2026 11:21:22 +0200 Subject: riscv: vdso: Do not use LTO for the vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With LTO enabled the compiler assumes that the vDSO functions are not used and optimizes them away completely. Currently this happens to __vdso_clock_getres(), __vdso_clock_gettime(), __vdso_getrandom(), __vdso_gettimeofday() and __vdso_riscv_hwprobe(). Disable LTO for the vDSO, as these functions are hand-optimized anyways. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202606301855.WvkSC4kD-lkp@intel.com/ Fixes: 021d23428bdb ("RISC-V: build: Allow LTO to be selected") Cc: stable@vger.kernel.org Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260701-riscv-vdso-lto-v1-1-89db0cd82077@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/vdso/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index a842dc034571..43ee881f6c6f 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -69,9 +69,9 @@ CPPFLAGS_$(vdso_lds) += -DHAS_VGETTIMEOFDAY endif # Disable -pg to prevent insert call site -CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) -CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) -CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) +CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) +CFLAGS_REMOVE_getrandom.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) +CFLAGS_REMOVE_hwprobe.o = $(CC_FLAGS_FTRACE) $(CC_FLAGS_SCS) $(CC_FLAGS_LTO) # Force dependency $(obj)/$(vdso_o): $(obj)/$(vdso_so) -- cgit v1.2.3 From 27d090f3ccd4cade897fb7e0c8c0a6e2fd3a95a0 Mon Sep 17 00:00:00 2001 From: Syed Saba Kareem Date: Fri, 10 Jul 2026 15:59:19 +0530 Subject: ASoC: amd: acp: add ACP70 DMI override for new ASUS TUF platforms Some ASUS TUF ACP70-based systems expose ACP ACPI configuration flags that select a non-working fallback audio path, similar to previously affected ASUS platforms. Add DMI-based overrides in snd_amd_acp_find_config() for the following systems to skip ACP ACPI flag-based selection: - ASUS TUF Gaming Vivobook 18 - ASUS TUF Gaming A14 FA401EA This ensures the intended SoundWire-based machine driver is selected on these platforms. Signed-off-by: Syed Saba Kareem Link: https://patch.msgid.link/20260710102926.1633385-1-syed.sabakareem@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 0d977f4f758d..051946562725 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -44,6 +44,18 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "83W5"), }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), + DMI_MATCH(DMI_PRODUCT_NAME, "TUF Gaming A14 FA401EA"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook 18 M1807GA"), + }, + }, {} }; -- cgit v1.2.3 From a82f1bb8191aec98a971a2196136016ef70c0880 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sun, 5 Jul 2026 16:02:25 +0800 Subject: drm/i915/gt: use correct selftest config symbol intel_engine_user.c checks CONFIG_DRM_I915_SELFTESTS before running the engine UABI isolation check. Kconfig defines DRM_I915_SELFTEST, without the trailing "S", and the rest of i915 uses CONFIG_DRM_I915_SELFTEST. Because CONFIG_DRM_I915_SELFTESTS is not backed by any Kconfig symbol, the IS_ENABLED() test is always false. Use the existing selftest symbol so the debug/selftest guarded path can be reached when selftests are enabled. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the Kconfig definition and the inconsistent guard in intel_engine_user.c. Fixes: 750e76b4f9f6 ("drm/i915/gt: Move the [class][inst] lookup for engines onto the GT") Signed-off-by: Pengpeng Hou Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260705080225.436-1-pengpeng@iscas.ac.cn (cherry picked from commit 14a2012a490258f3f93857bc4f1b203405964be7) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/gt/intel_engine_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gt/intel_engine_user.c b/drivers/gpu/drm/i915/gt/intel_engine_user.c index be4bbff1a57c..d5190e11b270 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_user.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_user.c @@ -259,7 +259,7 @@ void intel_engines_driver_register(struct drm_i915_private *i915) p = &prev->rb_right; } - if (IS_ENABLED(CONFIG_DRM_I915_SELFTESTS) && + if (IS_ENABLED(CONFIG_DRM_I915_SELFTEST) && IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) { struct intel_engine_cs *engine; unsigned int isolation; -- cgit v1.2.3 From 83201804efa4a5168be754e1dfc9b2faee760cac Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 9 Jun 2026 16:45:39 +0100 Subject: btrfs: fix use-after-free on reloc root after error in insert_dirty_subvol() If during relocation we fail in insert_dirty_subvol() because btrfs_update_reloc_root() returned an error, we will leave a root's reloc_root field pointing to a reloc root that was freed instead of NULL, resulting later in a use-after-free, or double free attempt during unmount. The sequence of steps is this: 1) During relocation the call to btrfs_update_reloc_root() in insert_dirty_subvol() fails, so insert_dirty_subvol() returns the error to merge_reloc_root() without adding the root to the list rc->dirty_subvol_roots; 2) Then merge_reloc_root() aborts the current transaction because insert_dirty_subvol() returned an error; 3) Up the call chain, merge_reloc_roots() gets the error, adds the reloc root for root X to the local reloc_roots list and jumps to the 'out' label, where it calls free_reloc_roots() to free all the reloc roots in the local reloc_roots list. This frees the reloc root for root X; 4) We go up the call chain to relocate_block_group() which calls clean_dirty_subvols() to go over dirty roots and set their ->reloc_root field to NULL, but root X is not in the dirty_subvol_roots list, so its ->reloc_root still points to a reloc root; 5) Relocation finishes, with an error and a transaction abort, but the ->reloc_root field for root X still points to the reloc root that was freed in step 3; 6) When unmounting the fs we end up calling: btrfs_free_fs_roots() btrfs_drop_and_free_fs_root() --> calls btrfs_put_root() against root X's ->reloc_root which is not NULL and points to the already freed reloc root in step 4 above Resulting in a use-after-free to a double free attempt. Syzbot reported this with the following dmesg/syslog: [ 106.004389][ T5339] BTRFS error (device loop0 state A): Transaction aborted (error -5) [ 106.014266][ T5339] BTRFS: error (device loop0 state A) in merge_reloc_root:1655: errno=-5 IO failure [ 106.021891][ T1061] BTRFS error (device loop0 state A): error while writing out transaction: -5 [ 106.026964][ T1061] BTRFS warning (device loop0 state A): Skipping commit of aborted transaction. [ 106.033807][ T5340] BTRFS error (device loop0 state A): bdev /dev/loop0 errs: wr 3, rd 0, flush 0, corrupt 0, gen 0 [ 106.039265][ T1061] BTRFS: error (device loop0 state A) in cleanup_transaction:2067: errno=-5 IO failure [ 106.044382][ T5339] BTRFS info (device loop0 state EA): forced readonly [ 106.074329][ T5339] BTRFS: error (device loop0 state EA) in merge_reloc_roots:1887: errno=-5 IO failure [ 106.081004][ T5356] BTRFS info (device loop0 state EA): scrub: started on devid 1 [ 106.085611][ T5339] BTRFS info (device loop0 state EA): balance: ended with status: -30 [ 106.089517][ T5356] BTRFS info (device loop0 state EA): scrub: not finished on devid 1 with status: -30 [ 106.662365][ T5338] BTRFS info (device loop0 state EA): last unmount of filesystem 3a375e4e-b156-4d76-a2ad-16e198ce1409 [ 106.682946][ T5338] ================================================================== [ 106.686574][ T5338] BUG: KASAN: slab-use-after-free in btrfs_put_root+0x2f/0x250 [ 106.690090][ T5338] Write of size 4 at addr ffff88803f978630 by task syz.0.0/5338 [ 106.693173][ T5338] [ 106.694279][ T5338] CPU: 0 UID: 0 PID: 5338 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(full) [ 106.694293][ T5338] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 106.694300][ T5338] Call Trace: [ 106.694308][ T5338] [ 106.694314][ T5338] dump_stack_lvl+0xe8/0x150 [ 106.694331][ T5338] print_address_description+0x55/0x1e0 [ 106.694343][ T5338] ? btrfs_put_root+0x2f/0x250 [ 106.694358][ T5338] print_report+0x58/0x70 [ 106.694368][ T5338] kasan_report+0x117/0x150 [ 106.694384][ T5338] ? btrfs_put_root+0x2f/0x250 [ 106.694399][ T5338] kasan_check_range+0x264/0x2c0 [ 106.694416][ T5338] btrfs_put_root+0x2f/0x250 [ 106.694430][ T5338] btrfs_drop_and_free_fs_root+0x160/0x210 [ 106.694447][ T5338] btrfs_free_fs_roots+0x2f9/0x3c0 [ 106.694464][ T5338] ? __pfx_btrfs_free_fs_roots+0x10/0x10 [ 106.694479][ T5338] ? free_root_pointers+0x5bf/0x5f0 [ 106.694494][ T5338] close_ctree+0x798/0x12d0 [ 106.694511][ T5338] ? __pfx_close_ctree+0x10/0x10 [ 106.694526][ T5338] ? _raw_spin_unlock_irqrestore+0x74/0x80 [ 106.694599][ T5338] ? rcu_preempt_deferred_qs_irqrestore+0x906/0xbc0 [ 106.694620][ T5338] ? __rcu_read_unlock+0x83/0xe0 [ 106.694636][ T5338] ? btrfs_put_super+0x48/0x1c0 [ 106.694652][ T5338] ? __pfx_btrfs_put_super+0x10/0x10 [ 106.694667][ T5338] generic_shutdown_super+0x13d/0x2d0 [ 106.694682][ T5338] kill_anon_super+0x3b/0x70 [ 106.694695][ T5338] btrfs_kill_super+0x41/0x50 [ 106.694710][ T5338] deactivate_locked_super+0xbc/0x130 [ 106.694722][ T5338] cleanup_mnt+0x437/0x4d0 [ 106.694736][ T5338] ? _raw_spin_unlock_irq+0x23/0x50 [ 106.694752][ T5338] task_work_run+0x1d9/0x270 [ 106.694769][ T5338] ? __pfx_task_work_run+0x10/0x10 [ 106.694784][ T5338] ? do_raw_spin_unlock+0x4d/0x210 [ 106.694802][ T5338] do_exit+0x70f/0x22c0 [ 106.694817][ T5338] ? trace_irq_disable+0x3b/0x140 [ 106.694835][ T5338] ? __pfx_do_exit+0x10/0x10 [ 106.694848][ T5338] ? preempt_schedule_thunk+0x16/0x30 [ 106.694863][ T5338] ? preempt_schedule_common+0x82/0xd0 [ 106.694878][ T5338] ? preempt_schedule_thunk+0x16/0x30 [ 106.694892][ T5338] do_group_exit+0x21b/0x2d0 [ 106.694906][ T5338] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 106.694918][ T5338] __x64_sys_exit_group+0x3f/0x40 [ 106.694932][ T5338] x64_sys_call+0x221a/0x2240 [ 106.694944][ T5338] do_syscall_64+0x174/0x580 [ 106.694954][ T5338] ? clear_bhb_loop+0x40/0x90 [ 106.694967][ T5338] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 106.694978][ T5338] RIP: 0033:0x7f958ef9ce59 [ 106.694988][ T5338] Code: Unable to access opcode bytes at 0x7f958ef9ce2f. [ 106.694994][ T5338] RSP: 002b:00007fffd4058318 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 [ 106.695008][ T5338] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f958ef9ce59 [ 106.695015][ T5338] RDX: 00007f958c3f8000 RSI: 0000000000000000 RDI: 0000000000000000 [ 106.695022][ T5338] RBP: 0000000000000003 R08: 0000000000000000 R09: 00007f958f1e73e0 [ 106.695028][ T5338] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 [ 106.695034][ T5338] R13: 00007f958f1e73e0 R14: 0000000000000003 R15: 00007fffd40583d0 [ 106.695046][ T5338] [ 106.695050][ T5338] [ 106.821635][ T5338] Allocated by task 1061: [ 106.823446][ T5338] kasan_save_track+0x3e/0x80 [ 106.825498][ T5338] __kasan_kmalloc+0x93/0xb0 [ 106.827381][ T5338] __kmalloc_cache_noprof+0x31c/0x660 [ 106.829525][ T5338] btrfs_alloc_root+0x75/0x930 [ 106.831458][ T5338] read_tree_root_path+0x127/0xb00 [ 106.833556][ T5338] btrfs_read_tree_root+0x34/0x60 [ 106.835553][ T5338] create_reloc_root+0x6b3/0xcb0 [ 106.837556][ T5338] btrfs_init_reloc_root+0x2ec/0x4b0 [ 106.839557][ T5338] record_root_in_trans+0x2ab/0x350 [ 106.841685][ T5338] btrfs_record_root_in_trans+0x15c/0x180 [ 106.844237][ T5338] start_transaction+0x39c/0x1820 [ 106.846638][ T5338] btrfs_finish_one_ordered+0x88e/0x2680 [ 106.849436][ T5338] btrfs_work_helper+0x37b/0xc20 [ 106.851549][ T5338] process_scheduled_works+0xb5d/0x1860 [ 106.853807][ T5338] worker_thread+0xa53/0xfc0 [ 106.855773][ T5338] kthread+0x389/0x470 [ 106.857548][ T5338] ret_from_fork+0x514/0xb70 [ 106.859493][ T5338] ret_from_fork_asm+0x1a/0x30 [ 106.861504][ T5338] [ 106.862527][ T5338] Freed by task 5339: [ 106.864224][ T5338] kasan_save_track+0x3e/0x80 [ 106.866180][ T5338] kasan_save_free_info+0x46/0x50 [ 106.868371][ T5338] __kasan_slab_free+0x5c/0x80 [ 106.870462][ T5338] kfree+0x1c5/0x640 [ 106.872180][ T5338] __del_reloc_root+0x341/0x3b0 [ 106.874290][ T5338] free_reloc_roots+0x5f/0x90 [ 106.876282][ T5338] merge_reloc_roots+0x73f/0x8a0 [ 106.878489][ T5338] relocate_block_group+0xbcc/0xe70 [ 106.880742][ T5338] do_nonremap_reloc+0xa8/0x5b0 [ 106.882885][ T5338] btrfs_relocate_block_group+0x7e6/0xc40 [ 106.885336][ T5338] btrfs_relocate_chunk+0x115/0x820 [ 106.887502][ T5338] __btrfs_balance+0x1db0/0x2ae0 [ 106.889543][ T5338] btrfs_balance+0xaf3/0x11b0 [ 106.891456][ T5338] btrfs_ioctl_balance+0x3d3/0x610 [ 106.893672][ T5338] __se_sys_ioctl+0xfc/0x170 [ 106.895530][ T5338] do_syscall_64+0x174/0x580 [ 106.897518][ T5338] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 106.900101][ T5338] [ 106.901123][ T5338] The buggy address belongs to the object at ffff88803f978000 [ 106.901123][ T5338] which belongs to the cache kmalloc-4k of size 4096 [ 106.906907][ T5338] The buggy address is located 1584 bytes inside of [ 106.906907][ T5338] freed 4096-byte region [ffff88803f978000, ffff88803f979000) [ 106.912980][ T5338] [ 106.914022][ T5338] The buggy address belongs to the physical page: [ 106.916716][ T5338] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x3f978 [ 106.920390][ T5338] head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 [ 106.923834][ T5338] flags: 0x4fff00000000040(head|node=1|zone=1|lastcpupid=0x7ff) [ 106.927104][ T5338] page_type: f5(slab) [ 106.928898][ T5338] raw: 04fff00000000040 ffff88801ac42140 dead000000000122 0000000000000000 [ 106.932507][ T5338] raw: 0000000000000000 0000000800040004 00000000f5000000 0000000000000000 [ 106.936193][ T5338] head: 04fff00000000040 ffff88801ac42140 dead000000000122 0000000000000000 [ 106.939856][ T5338] head: 0000000000000000 0000000800040004 00000000f5000000 0000000000000000 [ 106.943601][ T5338] head: 04fff00000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff [ 106.947268][ T5338] head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008 [ 106.950988][ T5338] page dumped because: kasan: bad access detected [ 106.953710][ T5338] page_owner tracks the page as allocated [ 106.956198][ T5338] page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd2820(GFP_ATOMIC|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 24, tgid 24 (kworker/u4:2), ts 105728970387, free_ts 29540875453 [ 106.964984][ T5338] post_alloc_hook+0x22d/0x280 [ 106.966956][ T5338] get_page_from_freelist+0x2593/0x2610 [ 106.969307][ T5338] __alloc_frozen_pages_noprof+0x18d/0x380 [ 106.971839][ T5338] allocate_slab+0x77/0x660 [ 106.973709][ T5338] refill_objects+0x339/0x3d0 [ 106.975696][ T5338] __pcs_replace_empty_main+0x321/0x720 [ 106.978136][ T5338] __kmalloc_node_track_caller_noprof+0x572/0x7b0 [ 106.981009][ T5338] __alloc_skb+0x2c1/0x7d0 [ 106.982983][ T5338] nsim_dev_trap_report_work+0x29a/0xb90 [ 106.985356][ T5338] process_scheduled_works+0xb5d/0x1860 [ 106.987710][ T5338] worker_thread+0xa53/0xfc0 [ 106.989847][ T5338] kthread+0x389/0x470 [ 106.991727][ T5338] ret_from_fork+0x514/0xb70 [ 106.993722][ T5338] ret_from_fork_asm+0x1a/0x30 [ 106.995900][ T5338] page last free pid 77 tgid 77 stack trace: [ 106.998479][ T5338] __free_frozen_pages+0xc1c/0xd30 [ 107.000819][ T5338] vfree+0x1d1/0x2f0 [ 107.002631][ T5338] delayed_vfree_work+0x55/0x80 [ 107.004848][ T5338] process_scheduled_works+0xb5d/0x1860 [ 107.007366][ T5338] worker_thread+0xa53/0xfc0 [ 107.009388][ T5338] kthread+0x389/0x470 [ 107.011177][ T5338] ret_from_fork+0x514/0xb70 [ 107.013313][ T5338] ret_from_fork_asm+0x1a/0x30 [ 107.015454][ T5338] [ 107.016460][ T5338] Memory state around the buggy address: [ 107.019052][ T5338] ffff88803f978500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 107.022691][ T5338] ffff88803f978580: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 107.026264][ T5338] >ffff88803f978600: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 107.029721][ T5338] ^ [ 107.032062][ T5338] ffff88803f978680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 107.035547][ T5338] ffff88803f978700: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 107.038865][ T5338] ================================================================== Fix this by resetting a root's ->reloc_root if we get an error while trying to merge a reloc root. Reported-by: syzbot+b3d472d13f9d7bf20669@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/6a1ebde9.c1435f33.112120.0176.GAE@google.com/ Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 0d63d117db59..a8d0acb0ad35 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1518,6 +1518,17 @@ static int insert_dirty_subvol(struct btrfs_trans_handle *trans, return 0; } +static void clear_reloc_root(struct btrfs_root *root) +{ + root->reloc_root = NULL; + /* + * Need barrier to ensure clear_bit() only happens after + * root->reloc_root = NULL. Pairs with have_reloc_root(). + */ + smp_wmb(); + clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); +} + static int clean_dirty_subvols(struct reloc_control *rc) { struct btrfs_root *root; @@ -1532,13 +1543,7 @@ static int clean_dirty_subvols(struct reloc_control *rc) struct btrfs_root *reloc_root = root->reloc_root; list_del_init(&root->reloc_dirty_list); - root->reloc_root = NULL; - /* - * Need barrier to ensure clear_bit() only happens after - * root->reloc_root = NULL. Pairs with have_reloc_root. - */ - smp_wmb(); - clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); + clear_reloc_root(root); if (reloc_root) { /* * btrfs_drop_snapshot drops our ref we hold for @@ -1914,13 +1919,32 @@ again: goto out; } ret = merge_reloc_root(rc, root); - btrfs_put_root(root); if (ret) { - if (list_empty(&reloc_root->root_list)) + /* + * Clear the reloc root since below we will call + * free_reloc_roots(), otherwise we leave + * root->reloc_root pointing to a freed reloc + * root and trigger a use-after-free during + * unmount or elsewhere. + */ + clear_reloc_root(root); + btrfs_put_root(root); + /* + * We are adding the reloc_root to the local + * reloc_roots list, so we add a ref for this + * list which will be dropped below by the call + * to free_reloc_roots(). + */ + if (list_empty(&reloc_root->root_list)) { list_add_tail(&reloc_root->root_list, &reloc_roots); + btrfs_grab_root(reloc_root); + } + /* Now drop the ref for root->reloc_root. */ + btrfs_put_root(reloc_root); goto out; } + btrfs_put_root(root); } else { if (!IS_ERR(root)) { if (root->reloc_root == reloc_root) { -- cgit v1.2.3 From b78fe9563e2d5ae47805f1e5dc722c91fd30e1f8 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Fri, 12 Jun 2026 11:52:55 +0100 Subject: btrfs: fix reloc root cleanup in merge_reloc_roots() If the root we got has zero root refs in its root item, we are resetting the root's ->reloc_root without using barriers like we do everywhere else. Sashiko complained about this while reviewing another patch, and it's correct (see the Link tag below). Also, we should not clear BTRFS_ROOT_DEAD_RELOC_TREE from the root unless the root points to the reloc root we have. Fix this by using clear_reloc_root(), which issues the memory barrier after setting the root's ->reloc_root to NULL and before clearing the bit BTRFS_ROOT_DEAD_RELOC_TREE from the root. Link: https://sashiko.dev/#/patchset/cf84f1a217c719e25b6b69e4298dd7afd36c9427.1781194426.git.fdmanana%40suse.com Reviewed-by: Boris Burkov Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index a8d0acb0ad35..75bf2e5fcb6e 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1948,11 +1948,10 @@ again: } else { if (!IS_ERR(root)) { if (root->reloc_root == reloc_root) { - root->reloc_root = NULL; + clear_reloc_root(root); + /* Drop the ref for root->reloc_root. */ btrfs_put_root(reloc_root); } - clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, - &root->state); btrfs_put_root(root); } -- cgit v1.2.3 From b95181f3929ff98949fa9460ca93eccebbf2d7fc Mon Sep 17 00:00:00 2001 From: You-Kai Zheng Date: Tue, 16 Jun 2026 18:39:07 +0800 Subject: btrfs: declare btrfs_ioctl_search_args_v2::buf as __u8 The variable-sized buffer buf in struct btrfs_ioctl_search_args_v2 is declared as __u64[], but it holds a packed byte stream of search results, where all offsets into the buffer are in bytes. Declaring buf as __u64[] makes it easy for user space to write incorrect pointer arithmetic: adding a byte offset directly to a __u64 pointer scales the offset by 8, landing at byte position offset*8 instead of offset. This recently caused an infinite loop in btrfs-progs: the accessor read all-zero data from misaddressed items, which fed zeroed search keys back into the ioctl loop and spun forever. The issue was worked around at the time by disabling TREE_SEARCH_V2 entirely in btrfs-progs (d73e69824854: "btrfs-progs: temporarily disable usage of v2 of search tree ioctl"). The kernel side already treats buf as a byte buffer, so change the declaration to __u8[] to match the actual semantics and prevent similar misuse in user space. The change is ABI compatible: both the structure size and alignment are unchanged. Fixes: cc68a8a5a433 ("btrfs: new ioctl TREE_SEARCH_V2") Reviewed-by: Qu Wenruo Signed-off-by: You-Kai Zheng Reviewed-by: David Sterba Signed-off-by: David Sterba --- include/uapi/linux/btrfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 9b576603b3f1..0a13baf3d8d1 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -598,7 +598,7 @@ struct btrfs_ioctl_search_args_v2 { __u64 buf_size; /* in - size of buffer * out - on EOVERFLOW: needed size * to store item */ - __u64 buf[]; /* out - found items */ + __u8 buf[]; /* out - found items */ }; /* With a @src_length of zero, the range from @src_offset->EOF is cloned! */ -- cgit v1.2.3 From ae4316f332e03e628712e9dfb89f2b7d3c70c21a Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 23 Jun 2026 21:07:14 +0930 Subject: btrfs: do not try compression for data reloc inodes [BUG] There is a syzbot report that the check inside get_new_location() triggered: BTRFS info (device loop0): found 31 extents, stage: move data extents BTRFS info (device loop0): leaf 8908800 gen 16 total ptrs 28 free space 1676 owner 18446744073709551607 item 0 key (256 INODE_ITEM 0) itemoff 3835 itemsize 160 inode generation 5 transid 0 size 0 nbytes 0 block group 0 mode 40755 links 1 uid 0 gid 0 rdev 0 sequence 0 flags 0x0 atime 1669132761.0 ctime 1669132761.0 mtime 1669132761.0 otime 0.0 item 1 key (256 INODE_REF 256) itemoff 3823 itemsize 12 index 0 name_len 2 item 2 key (258 INODE_ITEM 0) itemoff 3663 itemsize 160 inode generation 1 transid 16 size 733184 nbytes 106496 block group 0 mode 100600 links 0 uid 0 gid 0 rdev 0 sequence 24 flags 0x18 item 3 key (258 EXTENT_DATA 0) itemoff 3595 itemsize 68 generation 16 type 0 inline extent data size 47 ram_bytes 4096 compression 1 [...] item 27 key (18446744073709551611 ORPHAN_ITEM 258) itemoff 2376 itemsize 0 BTRFS error (device loop0): unexpected non-zero offset in file extent item for data reloc inode 258 key offset 0 offset 9277520992061368337 ------------[ cut here ]------------ btrfs_abort_should_print_stack(__error) [CAUSE] The above dump tree shows the first file extent item is inlined, which should make no sense for data reloc inodes, as such inodes just represent where the data extents are in the relocation destination chunk. However the relocation path preallocates space for each block, then dirties them, cluster by cluster. It's possible to have a single block at the beginning of the block group, and no other block in the same cluster. So relocation will preallocate a file extent for that block and dirty the first block. Then memory pressure forces the data reloc inode to be written back, before any other blocks are dirtied/allocated. Finally commit 3eaf5f082c4c ("btrfs: extract inlined creation into a dedicated delalloc helper") changed the sequence of delalloc. Before that commit we always tried NOCOW first, so that dirtied block would be written back into the preallocated space, and appear as a regular extent. But with that commit, we always try inline first, and since compression is forced, we try compressing the first block, and then inline the compressed data, resulting in the above inlined file extent in the data reloc tree. Then the check in get_new_location() will check the file offset, without checking if the file extent is inlined or not, resulting in the above failure. [FIX] Do not allow compression for data reloc inodes. Since data reloc inode sizes are always block aligned, as long as we do not compress, @data_len will always be at least one block, and that will cause can_cow_file_range_inline() to return false, thus no inlined extent will be created. Reported-by: syzbot+d950c6ba09b79f6e1864@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/6a373dc5.764cf64f.168fbe.0001.GAE@google.com/ Fixes: 3eaf5f082c4c ("btrfs: extract inlined creation into a dedicated delalloc helper") CC: stable@vger.kernel.org Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index d5d81f9546c3..7fdc6c3fd066 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -476,6 +476,8 @@ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode) if (inode->flags & BTRFS_INODE_NODATACOW || inode->flags & BTRFS_INODE_NODATASUM) return false; + if (btrfs_is_data_reloc_root(inode->root)) + return false; return true; } -- cgit v1.2.3 From 800b51960215417752f6712ce7e384ca49ecff39 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 23 Jun 2026 21:07:15 +0930 Subject: btrfs: reject inline file extents item in get_new_location() Commit a6908f88c9da ("btrfs: validate data reloc tree file extent item members") introduced extra checks on file extent items for data reloc inodes, but it checked the file extent offset without checking if the file extent is inlined. This can lead to either false alerts (as the offset member is inside the inlined data) or even reading beyond the item range. This has already triggered a warning in a syzbot report. Although the root fix is to avoid compression for data reloc inodes, for the sake of consistency, reject inlined file extents first. Fixes: a6908f88c9da ("btrfs: validate data reloc tree file extent item members") CC: stable@vger.kernel.org Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 75bf2e5fcb6e..6409c2cc1926 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -890,6 +890,13 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr, leaf = path->nodes[0]; fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); + if (unlikely(btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE)) { + btrfs_print_leaf(leaf); + btrfs_err(fs_info, + "unexpected inline file extent item for data reloc inode %llu key offset %llu", + btrfs_ino(BTRFS_I(reloc_inode)), bytenr); + return -EUCLEAN; + } /* * The cluster-boundary key searched above is always written by -- cgit v1.2.3 From 9411aafdf352b8d72668732af5a37dcb27383e25 Mon Sep 17 00:00:00 2001 From: Dave Chen Date: Fri, 26 Jun 2026 14:45:24 +0800 Subject: btrfs: only account delalloc bytes for regular file inodes in btrfs_getattr() btrfs_getattr() unconditionally reads BTRFS_I(inode)->new_delalloc_bytes and adds it (sector-aligned) to stat->blocks for every inode type. However, new_delalloc_bytes lives in a union with last_dir_index_offset: union { u64 new_delalloc_bytes; /* files only */ u64 last_dir_index_offset; /* directories only */ }; For a directory inode this memory holds last_dir_index_offset, which is set during directory logging (e.g. flush_dir_items_batch()) to the offset of the last logged BTRFS_DIR_INDEX_KEY. That offset grows with the number of entries ever created in the directory (dir indexes are monotonic and never reused), so it can be arbitrarily large. As a result, after a directory has been logged (e.g. via an fsync that triggers directory logging), btrfs_getattr() reports inflated st_blocks for that directory. The inflation is purely in-core and disappears after the inode is evicted and reloaded (btrfs_alloc_inode() zeroes the union), e.g. after a remount. Reproducer (on a btrfs filesystem): D=/mnt/btrfs/d mkdir -p $D for i in $(seq 1 20000); do touch $D/f$i; done sync # commit, push dir index high touch $D/trigger # dirty the dir in a new transaction xfs_io -c fsync $D # log the directory -> sets last_dir_index_offset stat -c '%b' $D # st_blocks is now inflated (e.g. 40) # umount + mount -> st_blocks drops back to the correct value The evict path already knows this union is type-dependent and guards the corresponding WARN_ON with !S_ISDIR() in btrfs_destroy_inode(); only btrfs_getattr() was missing the equivalent check. Only read new_delalloc_bytes for regular files, which are the only inodes that ever set it. Reviewed-by: Filipe Manana Signed-off-by: Dave Chen Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 16f097f58acc..04ea10b61bbb 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8068,7 +8068,8 @@ static int btrfs_getattr(struct mnt_idmap *idmap, stat->result_mask |= STATX_SUBVOL; spin_lock(&BTRFS_I(inode)->lock); - delalloc_bytes = BTRFS_I(inode)->new_delalloc_bytes; + delalloc_bytes = S_ISREG(inode->i_mode) ? + BTRFS_I(inode)->new_delalloc_bytes : 0; inode_bytes = inode_get_bytes(inode); spin_unlock(&BTRFS_I(inode)->lock); stat->blocks = (ALIGN(inode_bytes, blocksize) + -- cgit v1.2.3 From f0c1f14cc103a28ba80b3e48b88f23026709db3a Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 29 Jun 2026 15:47:34 +0100 Subject: btrfs: fix NULL pointer deref during assertion in btrfs_backref_free_node() In btrfs_backref_free_node() we have the following assertion: ASSERT(node->eb == NULL, "node->eb->start=%llu", node->eb->start); and a user reported the following crash: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 0 UID: 0 PID: 10422 Comm: syz.0.17 Not tainted 7.1.0-02765-g6b5a2b7d9bc1-dirty #44 PREEMPT(full) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 RIP: 0010:btrfs_backref_free_node fs/btrfs/backref.c:3057 [inline] RIP: 0010:btrfs_backref_free_node+0xb9/0x200 fs/btrfs/backref.c:3051 Code: 00 fc ff (...) RSP: 0018:ffa0000006b0f3c0 EFLAGS: 00010246 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: ffffffff840eb78b RDX: 0000000000000000 RSI: ffffffff840eafa5 RDI: ff110000742ab768 RBP: ff110000742ab700 R08: 0000000000000000 R09: 0000000000000000 R10: ff110000742ab700 R11: 00000000000a81f9 R12: ff11000107a92020 R13: ff1100005c182ea8 R14: 0000000000000000 R15: dffffc0000000000 FS: 0000555575536500(0000) GS:ff11000183985000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fa3d0e9d580 CR3: 000000002232a000 CR4: 0000000000753ef0 PKRU: 00000000 Call Trace: btrfs_backref_cleanup_node+0x27/0x30 fs/btrfs/backref.c:3133 relocate_tree_block fs/btrfs/relocation.c:2604 [inline] relocate_tree_blocks+0x11b0/0x1a20 fs/btrfs/relocation.c:2707 relocate_block_group+0x499/0xf30 fs/btrfs/relocation.c:3635 do_nonremap_reloc fs/btrfs/relocation.c:5323 [inline] btrfs_relocate_block_group+0x1749/0x5fb0 fs/btrfs/relocation.c:5490 btrfs_relocate_chunk+0x12b/0x950 fs/btrfs/volumes.c:3647 __btrfs_balance fs/btrfs/volumes.c:4586 [inline] btrfs_balance+0x1c7f/0x55c0 fs/btrfs/volumes.c:4973 btrfs_ioctl_balance fs/btrfs/ioctl.c:3474 [inline] btrfs_ioctl+0x38a4/0x5d20 fs/btrfs/ioctl.c:5570 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl fs/ioctl.c:583 [inline] __x64_sys_ioctl+0x18f/0x210 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x11f/0x860 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fb38e3b56dd Code: 02 b8 ff (...) RSP: 002b:00007fff04115788 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 00007fb38f6b0020 RCX: 00007fb38e3b56dd RDX: 00002000000003c0 RSI: 00000000c4009420 RDI: 0000000000000004 RBP: 00007fb38e451b48 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 0000000000000000 R14: 00007fb38f6b0020 R15: 00007fb38f6b002c It seems that this happens on some systems for some reason, when the ASSERT() macro calls the inline function verify_assert_printk_format() to evaluate the format string and arguments, causing the NULL pointer dereference on node->eb. So change the assertion to check for a NULL node->eb before dereferencing it. Also, while at it, make the assertion more useful by printing the owner of the extent buffer as well as its level. Reported-by: Yue Sun Link: https://lore.kernel.org/linux-btrfs/20260626065542.38413-1-samsun1006219@gmail.com/ Fixes: c4e7778580d6 ("btrfs: use verbose assertions in backref.c") Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/backref.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 23c3eeb58dc1..1be632c742bd 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -3054,7 +3054,10 @@ void btrfs_backref_free_node(struct btrfs_backref_cache *cache, if (node) { ASSERT(list_empty(&node->list)); ASSERT(list_empty(&node->lower)); - ASSERT(node->eb == NULL, "node->eb->start=%llu", node->eb->start); + ASSERT(node->eb == NULL, "node->eb->start=%llu level=%d owner=%llu", + node->eb ? node->eb->start : 0, + node->eb ? btrfs_header_level(node->eb) : 0, + node->eb ? btrfs_header_owner(node->eb) : 0); cache->nr_nodes--; btrfs_put_root(node->root); kfree(node); -- cgit v1.2.3 From 8b5a09ceb61b18b1f0797cd30a549d7dc85d8d50 Mon Sep 17 00:00:00 2001 From: Dave Chen Date: Mon, 29 Jun 2026 15:08:43 +0800 Subject: btrfs: fix u32 to s64 type conversion in dirty_metadata_bytes accounting The percpu_counter dirty_metadata_bytes is updated by negating eb->len and passing it to percpu_counter_add_batch(), whose amount parameter is s64. Since commit 84cda1a6087d ("btrfs: cache folio size and shift in extent_buffer"), eb->len is u32. The u32 result of -eb->len, when widened to the s64 parameter, becomes a large positive value instead of the intended negative value. For eb->len == 16384 the counter adds +4294950912 instead of subtracting 16384. The counter therefore grows on every metadata writeback instead of shrinking by the extent buffer size, permanently exceeding BTRFS_DIRTY_METADATA_THRESH and causing __btrfs_btree_balance_dirty() to trigger balance_dirty_pages_ratelimited() unconditionally, adding unnecessary writeback pressure. Cast eb->len to s64 before negation at both call sites so the subtraction is performed in signed 64-bit arithmetic. Reviewed-by: Filipe Manana Fixes: 84cda1a6087d ("btrfs: cache folio size and shift in extent_buffer") Signed-off-by: Dave Chen Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 7d604524e83c..de5785117a47 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2004,7 +2004,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, - -eb->len, + -(s64)eb->len, fs_info->dirty_metadata_batch); ret = true; } else { @@ -3774,7 +3774,7 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, return; buffer_tree_clear_mark(eb, PAGECACHE_TAG_DIRTY); - percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -eb->len, + percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -(s64)eb->len, fs_info->dirty_metadata_batch); for (int i = 0; i < num_extent_folios(eb); i++) { -- cgit v1.2.3 From 5eff4d5b17fa1950e80bfd1ba43dc0699e61a644 Mon Sep 17 00:00:00 2001 From: Leo Martins Date: Tue, 30 Jun 2026 12:58:05 -0700 Subject: btrfs: don't propagate EXTENT_FLAG_LOGGING to split extent maps When btrfs_drop_extent_map_range() splits an extent map, the new split maps inherit the original map's flags through a local 'flags' variable. Commit f86f7a75e2fb ("btrfs: use the flags of an extent map to identify the compression type") changed the EXTENT_FLAG_LOGGING clearing to operate on em->flags instead of that local 'flags' copy, so a split of an extent map that is currently being logged wrongly inherits EXTENT_FLAG_LOGGING. The flag is then never cleared on the split, and when it is freed while still on the inode's modified_extents list (for example by the extent map shrinker) it trips the WARN_ON(!list_empty(&em->list)) in btrfs_free_extent_map() and leads to a use-after-free. Clear EXTENT_FLAG_LOGGING from the local 'flags' copy used for the splits and only clear EXTENT_FLAG_PINNED from em->flags, restoring the behaviour prior to f86f7a75e2fb. CC: Jeff Layton Link: https://lore.kernel.org/all/20260629-btrfs-skip-logging-v1-1-4e3a28c1acaf@kernel.org/ Fixes: f86f7a75e2fb ("btrfs: use the flags of an extent map to identify the compression type") Reviewed-by: Jeff Layton Reviewed-by: Filipe Manana Signed-off-by: Leo Martins Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/extent_map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index fce9c5cc0122..6ad7b39ae358 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -866,13 +866,13 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, goto next; } - flags = em->flags; /* * In case we split the extent map, we want to preserve the * EXTENT_FLAG_LOGGING flag on our extent map, but we don't want * it on the new extent maps. */ - em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING); + flags = em->flags & ~EXTENT_FLAG_LOGGING; + em->flags &= ~EXTENT_FLAG_PINNED; modified = !list_empty(&em->list); /* -- cgit v1.2.3 From 9b73625a4f24971d7a1a07df5d7fd58c07bf3f9f Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 7 Jul 2026 16:24:30 +0930 Subject: btrfs: fix a regression where PAGECACHE_TAG_DIRTY is never cleared [BUG] The following script (already submitted as generic/798) will report incorrect dirty page numbers, with 64K page size systems and 4K fs block size: # mkfs.btrfs -s 4k -f $dev # mount $dev $mnt # xfs_io -f -c "pwrite 0 64K" -c fsync -c "cachestat 0 64K" $mnt/foobar Cached: 1, Dirty: 1, Writeback: 0, Evicted: 0, Recently Evicted: 0 Note that the dirtied page number is still 1. [CAUSE] The cachestat() goes through the XArray of the page cache, but instead of checking each folio's flag, it uses the PAGECACHE_TAG_DIRTY tag to report dirty pages. Since commit 095be159f3eb ("btrfs: unify folio dirty flag clearing"), btrfs replaced a folio_clear_dirty_for_io() call inside extent_write_cache_pages() with folio_test_dirty(). This will cause the following call sequence for the folio at file offset 0: extent_write_cache_pages() |- folio_test_dirty() | The folio is still dirty, continue to writeback. | |- extent_writepage() |- extent_writepage_io() |- submit_one_sector() for range [0, 4K) | |- btrfs_folio_clear_dirty() | |- btrfs_folio_set_writeback() | |- folio_start_writeback() | It's the first writeback block, we set the writeback | flag for the folio. | But the folio is still dirty, PAGECACHE_TAG_DIRTY is | kept | |- submit_one_sector() for range [4K, 8K) | |- btrfs_folio_clear_dirty() | |- btrfs_folio_set_writeback() | The folio already has writeback flag, no need to call | folio_start_writeback() | | ... |- submit_one_sector() for range [60K, 64K) |- btrfs_folio_clear_dirty() |- btrfs_folio_set_writeback() The folio already has writeback flag, no need to call folio_start_writeback() So the PAGECACHE_TAG_DIRTY is never cleared. Meanwhile for the old code, before that commit, the sequence looks like: extent_write_cache_pages() |- folio_clear_dirty_for_io() | The folio is still dirty, so continue to writeback. | But the folio dirty flag is cleared now. | |- extent_writepage() |- extent_writepage_io() |- submit_one_sector() for range [0, 4K) | |- btrfs_folio_clear_dirty() | |- btrfs_folio_set_writeback() | |- folio_start_writeback() | |- xas_clear(PAGECACHE_TAG) | | It's the first writeback block, we set the writeback | flag for the folio. | And the folio is not dirty, PAGECACHE_TAG_DIRTY is | cleared | |- submit_one_sector() for range [4K, 8K) | |- btrfs_folio_clear_dirty() | |- btrfs_folio_set_writeback() | The folio already has writeback flag, no need to call | folio_start_writeback() | | ... |- submit_one_sector() for range [60K, 64K) |- btrfs_folio_clear_dirty() |- btrfs_folio_set_writeback() The folio already has writeback flag, no need to call folio_start_writeback() Unlike the new code, old code will clear PAGECACHE_TAG_DIRTY for the first writeback block. There is a deeper problem, dirty and writeback folio flags are updated at very different timing. The dirty flag is only cleared when the last sub-folio block has dirty flag cleared. But the writeback flag is set when the first block starts writeback, and later blocks that go through writeback will not call folio_start_writeback() again. If we rely on folio_start_writeback() to update the PAGECACHE_TAG_DIRTY and PAGECACHE_TAG_TOWRITE, it will always be incorrect in one way or another. [FIX] Do not let folio_start_writeback() do any PAGECACHE_TAG_TOWRITE handling. Instead, manually clear both PAGECACHE_TAG_TOWRITE and PAGECACHE_TAG_DIRTY flags when the folio is no longer dirty during btrfs_subpage_set_writeback(). However this is only a hot-fix, for the long term solution we will follow iomap, by calling folio_start_writeback() immediately for the whole folio, and folio_end_writeback() after all writeback finished for the folio. Fixes: 095be159f3eb ("btrfs: unify folio dirty flag clearing") Reviewed-by: Boris Burkov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/subpage.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c index 56060acac2e9..2a9397be8116 100644 --- a/fs/btrfs/subpage.c +++ b/fs/btrfs/subpage.c @@ -359,6 +359,23 @@ void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info, folio_mark_dirty(folio); } +static void folio_clear_tags(struct folio *folio) +{ + struct address_space *mapping = folio_mapping(folio); + XA_STATE(xas, &mapping->i_pages, folio->index); + unsigned long flags; + + ASSERT(folio_test_locked(folio)); + ASSERT(mapping); + ASSERT(mapping_use_writeback_tags(mapping)); + + xas_lock_irqsave(&xas, flags); + xas_load(&xas); + xas_clear_mark(&xas, PAGECACHE_TAG_DIRTY); + xas_clear_mark(&xas, PAGECACHE_TAG_TOWRITE); + xas_unlock_irqrestore(&xas, flags); +} + /* * Extra clear_and_test function for subpage dirty bitmap. * @@ -403,7 +420,6 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info, unsigned int start_bit = subpage_calc_start_bit(fs_info, folio, writeback, start, len); unsigned long flags; - bool keep_write; spin_lock_irqsave(&bfs->lock, flags); bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits); @@ -413,10 +429,14 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info, * folio. Doing so can cause WB_SYNC_ALL writepages() to overlook it, * assume writeback is complete, and exit too early — violating sync * ordering guarantees. + * + * Instead we manually clear the DIRTY and TOWRITE tags after the folio + * is no longer dirty. */ - keep_write = folio_test_dirty(folio); if (!folio_test_writeback(folio)) - __folio_start_writeback(folio, keep_write); + __folio_start_writeback(folio, true); + if (!folio_test_dirty(folio)) + folio_clear_tags(folio); spin_unlock_irqrestore(&bfs->lock, flags); } -- cgit v1.2.3 From 6a8269b6459ed870a8156c106a0f597383907872 Mon Sep 17 00:00:00 2001 From: Guanghui Yang <3497809730@qq.com> Date: Sun, 12 Jul 2026 03:17:28 +0000 Subject: btrfs: free mapping node on duplicate reloc root insert __add_reloc_root() allocates a mapping_node before inserting it into rc->reloc_root_tree. If rb_simple_insert() finds an existing entry, it returns the existing rb_node and leaves the newly allocated node unlinked. The error path then returns -EEXIST without freeing the new node. Since the node was never inserted into reloc_root_tree, the later cleanup in put_reloc_control() cannot find it either. Free the newly allocated node before returning -EEXIST. The callers currently assert that -EEXIST should not happen, so this is a defensive cleanup for an unexpected duplicate insert path. If the path is ever reached, the local allocation should still be released. Fixes: 57a304cfd43b ("btrfs: do not panic in __add_reloc_root") Reviewed-by: Qu Wenruo Signed-off-by: Guanghui Yang <3497809730@qq.com> 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 6409c2cc1926..fc5c14b5adad 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -588,6 +588,7 @@ static int __add_reloc_root(struct btrfs_root *root, struct reloc_control *rc) btrfs_err(fs_info, "Duplicate root found for start=%llu while inserting into relocation tree", node->bytenr); + kfree(node); return -EEXIST; } -- cgit v1.2.3 From 75a41e3e51eb0d6f7a4969c5056298c2aa3e85f2 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 13 Jul 2026 09:50:42 +0200 Subject: btrfs: fix GET_SUBVOL_INFO after compat refactor btrfs_search_slot() returns a positive value when the search key does not exactly match an item. This is expected here, since offset 0 is used to find the first ROOT_BACKREF for the subvolume and the actual key has the parent root ID as its offset. Before the compat ioctl refactoring, the native handler still copied the filled structure to userspace when the search returned 1. After the lookup was moved to a shared helper, both native and compat callers treat the positive return value as a failure and skip copy_to_user(), leaving BTRFS_IOC_GET_SUBVOL_INFO unusable for non-top-level subvolumes. Reset ret after successfully validating and reading the ROOT_BACKREF so the helper reports success and both callers copy the result to userspace. Fixes: 538e5bdbc899 ("btrfs: add 32-bit compat ioctl for BTRFS_IOC_GET_SUBVOL_INFO") Reviewed-by: Qu Wenruo Signed-off-by: Daan De Meyer Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 81e87bc39828..68b33f365fda 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2048,6 +2048,7 @@ static int _btrfs_ioctl_get_subvol_info(struct inode *inode, ret = -ENOENT; goto out; } + ret = 0; } out: -- cgit v1.2.3 From 6fa6ee724d8dadf392139e242ac936b5da730c4b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 10 Jul 2026 18:04:22 +0200 Subject: 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 --- arch/arm64/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 @@ 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; + }; }; }; -- cgit v1.2.3 From 79adf48fb091e32ed94113d919908be20e09f5eb Mon Sep 17 00:00:00 2001 From: Alexander Hölzl Date: Fri, 19 Jun 2026 11:00:35 +0200 Subject: 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. -- cgit v1.2.3 From c43122fef328a70045fe7621c06de6b2b8e19264 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 9 Jul 2026 16:41:59 +0000 Subject: 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); } } -- cgit v1.2.3 From 2f4acd0fcd862e22eab45690ec2c08c80b6ef2e7 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:03:44 -0700 Subject: 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) -- cgit v1.2.3 From ffa0aa5b625fe0bed7463ac613f8b06676ff4542 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 13 Jul 2026 21:49:25 +0200 Subject: x86/boot: Validate console=uart8250 baud rate to fix early boot hang When the baud rate is empty, 0, invalid, or overflows to 0 when stored as an int, the system will hang during early boot because of a division by zero in early_serial_init(). Fall back to DEFAULT_BAUD when the resulting baud rate is 0 to prevent an early system hang. Fixes: ce0aa5dd20e4 ("x86, setup: Make the setup code also accept console=uart8250") Signed-off-by: Thorsten Blum Signed-off-by: Ingo Molnar Cc: "H. Peter Anvin" Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260713194924.126472-3-thorsten.blum@linux.dev --- arch/x86/boot/early_serial_console.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/boot/early_serial_console.c b/arch/x86/boot/early_serial_console.c index 023bf1c3de8b..5b83beab89e1 100644 --- a/arch/x86/boot/early_serial_console.c +++ b/arch/x86/boot/early_serial_console.c @@ -117,7 +117,7 @@ static unsigned int probe_baud(int port) static void parse_console_uart8250(void) { char optstr[64], *options; - int baud = DEFAULT_BAUD; + int baud; int port = 0; /* @@ -136,10 +136,13 @@ static void parse_console_uart8250(void) else return; - if (options && (options[0] == ',')) - baud = simple_strtoull(options + 1, &options, 0); - else + if (options && (options[0] == ',')) { + baud = simple_strtoull(options + 1, NULL, 0); + if (!baud) + baud = DEFAULT_BAUD; + } else { baud = probe_baud(port); + } if (port) early_serial_init(port, baud); -- cgit v1.2.3 From a1caeeadbf57ff86dfc3454398c46de86056a74e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:08 -0700 Subject: 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 @@ out_rtg: 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 @@ bad: #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, rep); - trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len); + if (got->br_startoff == rep->br_startoff) + state |= BMAP_LEFT_FILLING; + if (got_endoff == rep_endoff) + state |= BMAP_RIGHT_FILLING; - if (got->br_blockcount == repl->len) { + 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; - /* - * 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); + 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; + } } /* @@ -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, -- cgit v1.2.3 From bcb0621204e6999bb8f3a6c7deb5c6abfe0f197b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:24 -0700 Subject: 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); -- cgit v1.2.3 From 60a1dde9d28948a88ed5a06cf0b6179b1d487f85 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:39 -0700 Subject: 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 @@ bad: 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 @@ out_trans_cancel: 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 -- cgit v1.2.3 From d766e4e5e85d829629c3ba503802fe1303d7b591 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:04:55 -0700 Subject: 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; } -- cgit v1.2.3 From ee248157da501f0c02688fb64e5359f2832b0b01 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:10 -0700 Subject: 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) -- cgit v1.2.3 From 881f2eb0fcdea2f46a50fa6a892fe9e3ccf19e01 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:26 -0700 Subject: 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); -- cgit v1.2.3 From 9af789fa274bc14b907f811f79fa988a6fc6d4e7 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:41 -0700 Subject: 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 */ -- cgit v1.2.3 From ea6e2d9de25d2095845e0cdf2274e581fd40d636 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:05:57 -0700 Subject: 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; -- cgit v1.2.3 From 540ddc626245f12f56326ee0c1601f71ebb41d64 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:12 -0700 Subject: 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; -- cgit v1.2.3 From 5d72a68f2007ba2a968d6bf47dba3f4620bd182e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:28 -0700 Subject: 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); -- cgit v1.2.3 From 353a5900bc85234e7df05caac37698042dc26348 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:43 -0700 Subject: 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; } -- cgit v1.2.3 From ba150ce63453ccd74bae1404c1dfedbd01ecfd55 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:06:59 -0700 Subject: 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, -- cgit v1.2.3 From 15e38a9366b31d3d61081ead115f1dff59379e24 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:07:15 -0700 Subject: 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)); } -- cgit v1.2.3 From 59c462b0f5cfa107794228051724b34ae9334168 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 13 Jul 2026 23:07:30 -0700 Subject: 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); -- cgit v1.2.3 From 2c4dc0ed50b05cd847a4b34b8cebf0775f19aeb9 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Tue, 14 Jul 2026 10:29:23 +0200 Subject: ALSA: seq: close a re-opened queue timer in the destructor queue_delete() closes the queue timer, then frees it. snd_seq_timer_close() clears q->timer->timeri. snd_use_lock_sync() then drains borrowers, and snd_seq_timer_delete() frees q->timer. A borrower can re-open the timer inside that window. A SET_QUEUE_CLIENT that took a queueptr() use_lock reference before the queue was unlinked runs snd_seq_timer_open() after the close. Open refuses re-open only while timeri is set, and the close just cleared it, so it re-opens timeri. snd_seq_timer_delete() does not close that instance. Its snd_seq_timer_stop() is a no-op, because running was cleared first. So it frees q->timer with the instance still live. The queue is freed next. The instance stays on the global timer with callback_data pointing at the freed queue. A non-owner START on the unlocked queue arms it. The next tick derefs the freed queue in snd_seq_timer_interrupt(). Reachable by an unprivileged user with access to /dev/snd/seq. No CAP and no queue ownership required. Close any lingering instance in the destructor. There, ->timeri can no longer change: the queue is unlinked and all use_lock borrowers have drained, so no snd_seq_queue_use() can re-open it. Close it before clearing q->timer. snd_timer_close() waits for any in-flight snd_seq_timer_interrupt() to finish, and that callback still reads q->timer (via snd_seq_check_queue()), so q->timer must stay valid until it drains. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/422FDB81-2A68-47C7-A22D-2D3301E2E86D@doyensec.com Signed-off-by: Takashi Iwai --- sound/core/seq/seq_timer.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 4cd7211ccf48..419288eec4bb 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -61,12 +61,23 @@ struct snd_seq_timer *snd_seq_timer_new(void) void snd_seq_timer_delete(struct snd_seq_timer **tmr) { struct snd_seq_timer *t = *tmr; - *tmr = NULL; + struct snd_timer_instance *ti; if (t == NULL) { pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n"); return; } + + scoped_guard(spinlock_irq, &t->lock) { + ti = t->timeri; + t->timeri = NULL; + } + if (ti) { + snd_timer_close(ti); + snd_timer_instance_free(ti); + } + + *tmr = NULL; t->running = 0; /* reset time */ -- cgit v1.2.3 From 1e5185c090589f4146d728ab36417d8a5419f127 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 7 May 2026 10:22:23 +0200 Subject: 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) { -- cgit v1.2.3 From 0bcd59706aeda8a5d48ba656bab74cb26f2b423e Mon Sep 17 00:00:00 2001 From: Wang YuWei <1973615295@qq.com> Date: Mon, 6 Jul 2026 11:33:13 +0800 Subject: spi: dw-dma: Wait for controller idle before completing Tx dw_spi_dma_wait_tx_done() polls dw_spi_dma_tx_busy(), which only checks DW_SPI_SR_TF_EMPT. An empty TX FIFO merely means the last data word has been moved into the shift register; the transfer is not complete on the bus until DW_SPI_SR_BUSY is also cleared. As a result the wait can return while the controller is still shifting out the final word. Any caller that tears down or reconfigures the controller right after the transfer can then lose the tail of the transfer. The memory-operation path in spi-dw-core.c already waits for both DW_SPI_SR_BUSY == 0 and DW_SPI_SR_TF_EMPT == 1. Use the same completion condition in the DMA path so the transfer is guaranteed to be finished on the bus before the wait returns. Signed-off-by: Wang YuWei <1973615295@qq.com> Link: https://patch.msgid.link/tencent_4EA7B5C94669ED4C38A5F6C1C9126E5D9106@qq.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index bd70a7ed8067..f7d848fec9ab 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -282,7 +282,8 @@ static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed) static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws) { - return !(dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_TF_EMPT); + return (dw_readl(dws, DW_SPI_SR) & + (DW_SPI_SR_BUSY | DW_SPI_SR_TF_EMPT)) != DW_SPI_SR_TF_EMPT; } static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, -- cgit v1.2.3 From bc889dfcea9294a1eae7f8e2f3573a90764ae4d0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 22:45:39 +0800 Subject: ASoC: tas2781: bound firmware description string parsing The TAS2781 firmware parser reads several variable-length description strings with strlen() before checking that the string terminator is present inside the firmware blob. A malformed firmware image without a NUL terminator can therefore make the parser walk past the end of the firmware buffer before the later size checks run. Add a small bounded string-length helper and use it for all description fields that are parsed from the firmware buffer. Keep the existing size checks for the fixed bytes that follow each string. Fixes: 915f5eadebd2 ("ASoC: tas2781: firmware lib") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260706144540.93929-1-pengpeng@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-fmwlib.c | 63 +++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index bd16d5326a23..dcbeb9618195 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1100,13 +1101,42 @@ static int tasdevice_load_block_kernel( return 0; } +static int tasdevice_fw_strnlen(const struct firmware *fmw, int offset) +{ + const u8 *start; + const u8 *nul; + size_t remaining; + size_t len; + + if (offset < 0 || offset >= fmw->size) + return -EINVAL; + + start = fmw->data + offset; + remaining = fmw->size - offset; + nul = memchr(start, '\0', remaining); + if (!nul) + return -EINVAL; + + len = nul - start; + if (len > INT_MAX) + return -EOVERFLOW; + + return len; +} + static int fw_parse_variable_hdr(struct tasdevice_priv *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr, const struct firmware *fmw, int offset) { const unsigned char *buf = fmw->data; - int len = strlen((char *)&buf[offset]); + int len; + len = tasdevice_fw_strnlen(fmw, offset); + if (len < 0) { + dev_err(tas_priv->dev, "%s: Description error\n", __func__); + offset = len; + goto out; + } len++; if (offset + len + 8 > fmw->size) { @@ -1238,7 +1268,12 @@ static int fw_parse_data(struct tasdevice_fw *tas_fmw, memcpy(img_data->name, &data[offset], 64); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + offset = n; + goto out; + } n++; if (offset + n + 2 > fmw->size) { dev_err(tas_fmw->dev, "%s: Description error\n", __func__); @@ -1309,7 +1344,12 @@ static int fw_parse_program_data(struct tasdevice_priv *tas_priv, } offset += 64; - n = strlen((char *)&buf[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } /* skip '\0' and 5 unused bytes */ n += 6; if (offset + n > fmw->size) { @@ -1372,7 +1412,12 @@ static int fw_parse_configuration_data( memcpy(config->name, &data[offset], 64); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } n += 15; if (offset + n > fmw->size) { dev_err(tas_priv->dev, "Description err\n"); @@ -2134,7 +2179,8 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, { struct tasdevice_calibration *calibration; unsigned char *data = (unsigned char *)fmw->data; - unsigned int i, n; + unsigned int i; + int n; if (offset + 2 > fmw->size) { dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__); @@ -2166,7 +2212,12 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, calibration = &(tas_fmw->calibrations[i]); offset += 64; - n = strlen((char *)&data[offset]); + n = tasdevice_fw_strnlen(fmw, offset); + if (n < 0) { + dev_err(tas_priv->dev, "Description err\n"); + offset = n; + goto out; + } /* skip '\0' and 2 unused bytes */ n += 3; if (offset + n > fmw->size) { -- cgit v1.2.3 From e919ca35a6e84b5adf085da9ffa1544c01d4ce1e Mon Sep 17 00:00:00 2001 From: Srikanth Boyapally Date: Wed, 8 Jul 2026 10:21:48 +0530 Subject: spi: cadence-quadspi: Fix indirect write timeout when DMA read mode is enabled When use_dma_read is enabled, the IRQ handler unconditionally overwrites irq_status with the return value of get_dma_status(). For write operations, DMA status returns 0 since no DMA read is in progress, causing irq_status to become 0. The subsequent completion signal is never triggered and the write operation times out with -ETIMEDOUT: cadence-qspi f1010000.spi: Indirect write timeout spi-nor spi0.1: operation failed with -110 Fix this by separating the DMA completion path from the write interrupt path. If get_dma_status() indicates DMA read completion, signal completion and return immediately. Otherwise, preserve the original irq_status so that write completion interrupts are correctly recognized and signalled. Fixes: aac733a96636 ("spi: cadence-qspi: Fix style and improve readability") Signed-off-by: Srikanth Boyapally Reviewed-by: Radhey Shyam Pandey Link: https://patch.msgid.link/20260708045148.2993313-1-srikanth.boyapally@amd.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index aaba1a3ad577..ecb0be394696 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -382,12 +382,16 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev) /* Clear interrupt */ writel(irq_status, cqspi->iobase + CQSPI_REG_IRQSTATUS); - if (cqspi->use_dma_read && ddata && ddata->get_dma_status) - irq_status = ddata->get_dma_status(cqspi); - else if (cqspi->slow_sram) + if (cqspi->use_dma_read && ddata && ddata->get_dma_status) { + if (ddata->get_dma_status(cqspi)) { + complete(&cqspi->transfer_complete); + return IRQ_HANDLED; + } + } else if (cqspi->slow_sram) { irq_status &= CQSPI_IRQ_MASK_RD_SLOW_SRAM | CQSPI_IRQ_MASK_WR; - else + } else { irq_status &= CQSPI_IRQ_MASK_RD | CQSPI_IRQ_MASK_WR; + } if (irq_status) complete(&cqspi->transfer_complete); -- cgit v1.2.3 From 4d638dc09128de1cb8311dff51e5de7d606d9346 Mon Sep 17 00:00:00 2001 From: Qingwei Hu Date: Tue, 7 Jul 2026 20:25:48 +0800 Subject: RISC-V: KVM: Inject instruction access fault on unmapped guest fetch When an instruction guest-page-fault targets a GPA that is not backed by any memslot, KVM has no MMIO emulation path for the fetch. Load and store guest-page faults can be routed through MMIO emulation, but an instruction fetch has no data payload or access size for userspace to complete in the same way. Treat this case as an architectural access fault in the guest. On bare metal, fetching from an inaccessible physical address raises an instruction access fault for the supervisor to handle through its trap vector. Reflect EXC_INST_ACCESS back to the guest so the guest observes the same class of exception rather than leaving the fetch as a host-handled condition. stval contains the virtual address of the portion of the instruction that caused the fault, while sepc points to the beginning of the instruction. Signed-off-by: Qingwei Hu Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260707122548.281685-1-qingwei.hu@bytedance.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_exit.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c index 0bb0c51e3c89..6c8530b9f29e 100644 --- a/arch/riscv/kvm/vcpu_exit.c +++ b/arch/riscv/kvm/vcpu_exit.c @@ -38,6 +38,25 @@ static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run, return kvm_riscv_vcpu_mmio_store(vcpu, run, fault_addr, trap->htinst); + case EXC_INST_GUEST_PAGE_FAULT: { + /* + * No memslot backs this GPA and an instruction fetch + * cannot be emulated as MMIO. On bare metal a fetch + * from an unbacked physical address raises an + * instruction access fault, so reflect that back to + * the guest. + */ + struct kvm_cpu_trap inst_trap = { + .sepc = trap->sepc, + .scause = EXC_INST_ACCESS, + .stval = trap->stval, + .htval = 0, + .htinst = 0, + }; + + kvm_riscv_vcpu_trap_redirect(vcpu, &inst_trap); + return 1; + } default: return -EOPNOTSUPP; }; -- cgit v1.2.3 From f61289af2667a942f48859fe0b030894153ad1d2 Mon Sep 17 00:00:00 2001 From: Vidya Srinivas Date: Thu, 18 Jun 2026 23:48:37 +0530 Subject: drm/i915/display: Fix NV12 ceiling division for bigjoiner case Commit 16df4cc63c58 ("drm/i915/display: Use ceiling division for NV12 UV surface offset calculation") computes the UV (chroma) surface start/size as ceiling(half of Y plane start/size) directly from the U16.16 fixed-point source rectangle: x = fp_16_16_to_int_ceil(fp_16_16_div2(src.x1)); For a single pipe the source coordinates are integers, so this is correct. (UV start = ceiling(half of Y plane start)). With bigjoiner + a plane scaler the picture changes. The pipe boundary is a fixed integer destination pixel, but the plane's position and the scaler ratio are arbitrary, so drm_rect_clip_scaled() maps the seam back to a *fractional* per-pipe source. For a 1280->2407 upscaled NV12 plane crossing the seam: master src: width = 1204 * 1280/2407 = 640.265899, x1 = 0 joiner src: width = 1203 * 1280/2407 = 639.734115, x1 = 640.265884 The luma path floors this to an integer (src.x1 >> 16 = 640), but the UV path takes ceiling(640.265884 / 2) = ceil(320.13) = 321. The Y plane then starts at column 640 while the UV plane starts at 321*2 = 642, pushing the chroma read one column past the 640-wide chroma surface on the joiner secondary: [CRTC:382:pipe C] PLANE ATS fault [CRTC:382:pipe C][PLANE:267:plane 1C] fault (CTL=0x81009400, ...) The spec "Y plane start" is the integer pixel the luma surface actually programs (640), not the pre-floor fixed-point value (640.27). Convert the Y plane start/size to integer first - matching skl_check_main_surface() - and then apply the ceiling. This is a no-op for the integer (non-joiner) case and yields the correct, in-bounds chroma offset for the fractional joiner seam: before fix after fix master 1B: x=0 w=321 x=0 w=320 -> [0, 320) slave 1C: x=321 w=320 x=320 w=320 -> [320, 640) The two halves now tile the 640-wide chroma plane exactly and the ATS fault is gone. Assisted-by: GitHub-Copilot:Claude-Opus-4.8 Fixes: 16df4cc63c58 ("drm/i915/display: Use ceiling division for NV12 UV surface offset calculation") Signed-off-by: Vidya Srinivas Reviewed-by: Juha-Pekka Heikkila Signed-off-by: Uma Shankar Link: https://patch.msgid.link/20260618181837.687302-1-vidya.srinivas@intel.com (cherry picked from commit 0c59cc78241c10e5f02d92b28d811b0435e706a7) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/skl_universal_plane.c | 33 +++++++++------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index ad4bfff6903d..164b7d61c9a3 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -2126,19 +2126,6 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state) return 0; } - -/* Divide a U16.16 fixed-point value by 2, staying in fixed-point domain */ -static inline u32 fp_16_16_div2(u32 fp) -{ - return fp >> 1; -} - -/* Convert a U16.16 fixed-point value to integer, rounding up */ -static inline int fp_16_16_to_int_ceil(u32 fp) -{ - return DIV_ROUND_UP(fp, 1 << 16); -} - static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state) { struct intel_display *display = to_intel_display(plane_state); @@ -2154,14 +2141,20 @@ static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state) int max_height = intel_plane_max_height(plane, fb, uv_plane, rotation); /* - * LNL+ UV surface start/size = - * ceiling(half of Y plane start/size). Use ceiling division - * unconditionally; it is a no-op for even values. + * UV (chroma) start/size = ceiling(half of the *integer* Y plane + * start/size), i.e. the value the luma surface programs (src >> 16), + * not the raw U16.16. A bigjoiner seam mapped through the scaler can + * give a fractional luma src; ceiling that directly would round the + * chroma one column too far and read past the chroma surface. */ - int x = fp_16_16_to_int_ceil(fp_16_16_div2(plane_state->uapi.src.x1)); - int y = fp_16_16_to_int_ceil(fp_16_16_div2(plane_state->uapi.src.y1)); - int w = fp_16_16_to_int_ceil(fp_16_16_div2(drm_rect_width(&plane_state->uapi.src))); - int h = fp_16_16_to_int_ceil(fp_16_16_div2(drm_rect_height(&plane_state->uapi.src))); + int luma_x = plane_state->uapi.src.x1 >> 16; + int luma_y = plane_state->uapi.src.y1 >> 16; + int luma_w = drm_rect_width(&plane_state->uapi.src) >> 16; + int luma_h = drm_rect_height(&plane_state->uapi.src) >> 16; + int x = DIV_ROUND_UP(luma_x, 2); + int y = DIV_ROUND_UP(luma_y, 2); + int w = DIV_ROUND_UP(luma_x + luma_w, 2) - x; + int h = DIV_ROUND_UP(luma_y + luma_h, 2) - y; u32 offset; /* FIXME not quite sure how/if these apply to the chroma plane */ -- cgit v1.2.3 From 339bd11591593ab7ce88136ab7fd01ef3813b724 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:09 -0700 Subject: iommufd/viommu: Release the igroup lock on the vdevice_size error path iommufd_vdevice_alloc_ioctl() takes idev->igroup->lock, then validates the driver's vdevice_size against the core structure size with a WARN_ON_ONCE. On failure that guard jumps to out_put_idev, below out_unlock_igroup, so it skips the mutex_unlock(), leaving the igroup lock held and deadlocking the next vDEVICE operation on that group. Jump to out_unlock_igroup instead. Fixes: ed42eee797ff3 ("iommufd/viommu: Add driver-defined vDEVICE support") Link: https://patch.msgid.link/r/e903f775d491296a525097e2a90b3eb6a47cf2ef.1783311134.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/viommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index 4081deda9b33..0c12c7e352a1 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -189,7 +189,7 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) if (WARN_ON_ONCE(viommu->ops->vdevice_size < vdev_size || !viommu->ops->vdevice_init)) { rc = -EOPNOTSUPP; - goto out_put_idev; + goto out_unlock_igroup; } vdev_size = viommu->ops->vdevice_size; } -- cgit v1.2.3 From 9be311cfbe6154da146a7408e0d5e518a9321ed3 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:10 -0700 Subject: iommufd/viommu: Publish a vDEVICE only after vdevice_init() succeeds iommufd_vdevice_alloc_ioctl() adds the vDEVICE to the viommu->vdevs xarray with xa_cmpxchg() before the driver's vdevice_init() op runs. That op is where a driver validates the device and may reject it, but the xarray entry is already live by then: a concurrent IOMMU_HWPT_INVALIDATE can look it up with iommufd_viommu_find_dev() and run the driver invalidation path against a device that vdevice_init() would have refused. Reserve the index with xa_insert(): it stores a zero entry that reads back as NULL, and returns -EBUSY on a duplicate virt_id. Run vdevice_init() and store the vDEVICE pointer only once it succeeds. A failed vdevice_init() releases the reservation, so lookups observe the vDEVICE only after it is fully initialized and accepted. Fixes: ed42eee797ff3 ("iommufd/viommu: Add driver-defined vDEVICE support") Link: https://patch.msgid.link/r/1e05999347f4bf583edbc6a1312c857d5548708c.1783311134.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/viommu.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index 0c12c7e352a1..fc13cf4737ea 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -143,7 +143,7 @@ void iommufd_vdevice_destroy(struct iommufd_object *obj) int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) { struct iommu_vdevice_alloc *cmd = ucmd->cmd; - struct iommufd_vdevice *vdev, *curr; + struct iommufd_vdevice *vdev; size_t vdev_size = sizeof(*vdev); struct iommufd_viommu *viommu; struct iommufd_device *idev; @@ -218,18 +218,28 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) */ idev->vdev = vdev; - curr = xa_cmpxchg(&viommu->vdevs, virt_id, NULL, vdev, GFP_KERNEL); - if (curr) { - rc = xa_err(curr) ?: -EEXIST; + /* + * Reserve the slot with a zero entry (reads back as NULL) until the + * vdevice_init() op accepts the vDEVICE. Only the xa_* helpers hide a + * reserved entry, so never use a raw xas_* iterator on this xarray. + */ + rc = xa_insert(&viommu->vdevs, virt_id, NULL, GFP_KERNEL); + if (rc) { + if (rc == -EBUSY) + rc = -EEXIST; goto out_abort; } if (viommu->ops && viommu->ops->vdevice_init) { rc = viommu->ops->vdevice_init(vdev); - if (rc) + if (rc) { + xa_release(&viommu->vdevs, virt_id); goto out_abort; + } } + xa_store(&viommu->vdevs, virt_id, vdev, GFP_KERNEL); + cmd->out_vdevice_id = vdev->obj.id; rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); if (rc) -- cgit v1.2.3 From c3b8ee84a965058b41275069d4696f37a8b14bf6 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:11 -0700 Subject: iommu/arm-smmu-v3-iommufd: Require exactly one Stream ID for a vDEVICE arm_vsmmu_vsid_to_sid() maps a guest's vSID to a single physical Stream ID taken from master->streams[0], assuming a device has exactly one stream. A device with several streams gets only its first one mapped, so a guest vSID invalidation cannot reach the others' ATC and IOTLB entries; a device with none makes master->streams a ZERO_SIZE_PTR, read out of bounds. Add an arm_vsmmu_vdevice_init() op to reject the vDEVICE with -EOPNOTSUPP when master->num_streams is not one, rather than mapping it silently. Fixes: d68beb276ba26 ("iommu/arm-smmu-v3: Support IOMMU_HWPT_INVALIDATE using a VIOMMU object") Link: https://patch.msgid.link/r/b15f2b73520f389f3f57881da2f040e7bdc18876.1783311134.git.nicolinc@nvidia.com Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c index 1e9f7d2de344..85ebfdb3d2a7 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c @@ -297,6 +297,20 @@ unlock: return ret; } +static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev) +{ + struct device *dev = iommufd_vdevice_to_device(vdev); + struct arm_smmu_master *master = dev_iommu_priv_get(dev); + + /* + * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so + * more streams would leave the rest stale and none reads out of bounds. + */ + if (master->num_streams != 1) + return -EOPNOTSUPP; + return 0; +} + /* This is basically iommu_viommu_arm_smmuv3_invalidate in u64 for conversion */ struct arm_vsmmu_invalidation_cmd { union { @@ -403,6 +417,7 @@ out: static const struct iommufd_viommu_ops arm_vsmmu_ops = { .alloc_domain_nested = arm_vsmmu_alloc_domain_nested, .cache_invalidate = arm_vsmmu_cache_invalidate, + .vdevice_init = arm_vsmmu_vdevice_init, }; size_t arm_smmu_get_viommu_size(struct device *dev, -- cgit v1.2.3 From d9e96f859de3ea3e99bce927a988449a1816483c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 14 Jul 2026 19:33:03 +0800 Subject: ASoC: sun4i-codec: Set quirks.playback_only for H616 codec The H616 codec does not have capture capabilities. Set the .playback_only quirks flag to denote this. This was somehow missing from the original driver patch, even though the patch prior to it in the series added this quirk. Fixes: 9155c321a1d0 ("ASoC: sun4i-codec: support allwinner H616 codec") Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20260714113304.270224-1-wens@kernel.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index f4e22af594fa..93f511fb6190 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -2237,6 +2237,7 @@ static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = { .reg_dac_fifoc = REG_FIELD(SUN50I_H616_CODEC_DAC_FIFOC, 0, 31), .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, .has_reset = true, + .playback_only = true, .dma_max_burst = SUN4I_DMA_MAX_BURST, }; -- cgit v1.2.3 From 627b6c94b817c2ee00c854d102a5da08105ad0a7 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Thu, 25 Jun 2026 18:32:21 +0200 Subject: CREDITS: Add Wolfram Sang Wolfram Sang has decided that a decade-plus of I2C was enough and is moving on to new things. Thank you, Wolfram, for your years of dedication and for keeping the bus in line. Your legacy is now officially cemented in the CREDITS file. Suggested-by: Sebastian Reichel Signed-off-by: Andi Shyti Cc: Wolfram Sang Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/20260625163221.183414-1-andi.shyti@kernel.org --- CREDITS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CREDITS b/CREDITS index 84793a967a0b..91c51c14e993 100644 --- a/CREDITS +++ b/CREDITS @@ -3626,6 +3626,13 @@ S: 69 rue Dunois S: 75013 Paris S: France +N: Wolfram Sang +E: wsa@kernel.org +W: sang-engineering.com +P: rsa4096/140DE4CC14A029B6 3991 B1EA B9E2 6751 A4F7 645D 140D E4CC 14A0 29B6 +D: I2C Maintainer 2012 - 2026 +S: Berlin, Germany + N: Aleksa Sarai E: cyphar@cyphar.com W: https://www.cyphar.com/ -- cgit v1.2.3 From cb2fc37857693b55909fb77dc2c87cfbc1cdc476 Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Mon, 13 Jul 2026 20:11:59 +0200 Subject: i2c: imx: fix locked bus on SMBus block-read of 0 (atomic) SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the atomic (polling) path rejects it as -EPROTO. Worse, it returns without a NACK+STOP: the next receive cycle has already started, so the target keeps holding SDA and the bus stays stuck until a power cycle for this i2c controller. Reading I2DR to obtain the count likewise arms the next byte on the count > I2C_SMBUS_BLOCK_MAX path, which also returned -EPROTO directly and left the bus held. Handle both: NACK the in-flight dummy byte (TXAK) and extend msgs->len so the existing last-byte handling emits STOP; the dummy byte is discarded. A count of 0 is a valid empty block read; a count above I2C_SMBUS_BLOCK_MAX is still reported as -EPROTO, but only after the bus has been released. The interrupt-driven path has the same flaw from a later commit and is fixed separately, as it carries a different Fixes: tag and stable range. Fixes: 8e8782c71595 ("i2c: imx: add SMBus block read support") Signed-off-by: Vincent Jardin Cc: # v3.16+ Acked-by: Oleksij Rempel Acked-by: Carlos Song Reviewed-by: Stefan Eichenberger Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260713-for-upstream-i2c-lx2160-fix-v1-v3-1-073ac9e103a5@free.fr --- drivers/i2c/busses/i2c-imx.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 28313d0fad37..cfd1e63359e7 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1415,6 +1415,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, int i, result; unsigned int temp; int block_data = msgs->flags & I2C_M_RECV_LEN; + int block_err = 0; result = i2c_imx_prepare_read(i2c_imx, msgs, false); if (result) @@ -1436,8 +1437,20 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, */ if ((!i) && block_data) { len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); - if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) - return -EPROTO; + if ((len == 0) || (len > I2C_SMBUS_BLOCK_MAX)) { + /* + * SMBus 3.1 6.5.7: support count byte of 0. + * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. + */ + if (len > I2C_SMBUS_BLOCK_MAX) + block_err = -EPROTO; + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + msgs->buf[0] = 0; + msgs->len = 2; + continue; + } dev_dbg(&i2c_imx->adapter.dev, "<%s> read length: 0x%X\n", __func__, len); @@ -1485,7 +1498,7 @@ static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx, "<%s> read byte: B%d=0x%X\n", __func__, i, msgs->buf[i]); } - return 0; + return block_err; } static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, -- cgit v1.2.3 From 1dba91a0493b7ffe9cbbcf7a8df1b361ddcac515 Mon Sep 17 00:00:00 2001 From: "Harry Yoo (Oracle)" Date: Mon, 13 Jul 2026 23:28:49 +0900 Subject: mm/slab: fix a memory leak due to bootstrapping sheaves twice When kmalloc caches are aliased, multiple cache pointers reference the same kmem_cache. As a result, iterating over kmalloc indices and bootstrapping sheaves can bootstrap the same cache more than once and leak memory. Currently, this could happen when the architecture specifies minimum alignment for slab caches that is larger than ARCH_KMALLOC_MINALIGN. Bootstrap sheaves only when the cache does not have them already. Add a warning when bootstrap_cache_sheaves() is called for a cache that already has sheaves enabled. Fixes: 913ffd3a1bf5 ("slab: handle kmalloc sheaves bootstrap") Cc: stable@vger.kernel.org Signed-off-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-1-47c7bd138de7@kernel.org Signed-off-by: Vlastimil Babka (SUSE) --- mm/slub.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 65febe957886..f9461a0c47d3 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -8497,6 +8497,8 @@ static void __init bootstrap_cache_sheaves(struct kmem_cache *s) bool failed = false; int node, cpu; + VM_WARN_ON_ONCE(cache_has_sheaves(s)); + capacity = calculate_sheaf_capacity(s, &empty_args); /* capacity can be 0 due to debugging or SLUB_TINY */ @@ -8548,8 +8550,11 @@ static void __init bootstrap_kmalloc_sheaves(void) for (type = KMALLOC_NORMAL; type <= KMALLOC_PARTITION_END; type++) { for (int idx = 0; idx < KMALLOC_SHIFT_HIGH + 1; idx++) { - if (kmalloc_caches[type][idx]) - bootstrap_cache_sheaves(kmalloc_caches[type][idx]); + struct kmem_cache *s = kmalloc_caches[type][idx]; + + /* Do not bootstrap twice when caches are aliased */ + if (s && !cache_has_sheaves(s)) + bootstrap_cache_sheaves(s); } } } -- cgit v1.2.3 From 07fd9385f0d87dff4b34f355f68adf701080cb24 Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Mon, 13 Jul 2026 20:12:00 +0200 Subject: i2c: imx: fix locked bus on SMBus block-read of 0 (IRQ) SMBus 3.1 6.5.7 allows a Block Read byte count of 0, but the interrupt-driven block-read state machine rejects it as -EPROTO. Worse, it returns without a NACK+STOP: the next receive cycle has already started, so the target keeps holding SDA and the bus stays stuck until a power cycle of this i2c controller. Accept count=0: NACK the in-flight dummy byte (TXAK) and set msg->len to 2 so i2c_imx_isr_read_continue() emits STOP via its normal last-byte path. The dummy byte is discarded; block-read callers only consume buf[0..count-1]. Reading I2DR has likewise already armed the next byte on the count > I2C_SMBUS_BLOCK_MAX error path, so NACK it (TXAK) before aborting with -EPROTO; otherwise the failing transfer's STOP cannot complete and the bus stays held. The atomic path regressed earlier (v3.16) and is fixed separately; this patch covers only the v6.13 state-machine rework. Fixes: 5f5c2d4579ca ("i2c: imx: prevent rescheduling in non dma mode") Signed-off-by: Vincent Jardin Cc: # v6.13+ Acked-by: Oleksij Rempel Acked-by: Carlos Song Reviewed-by: Stefan Eichenberger Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260713-for-upstream-i2c-lx2160-fix-v1-v3-2-073ac9e103a5@free.fr --- drivers/i2c/busses/i2c-imx.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index cfd1e63359e7..d5e6e2eca3b3 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1061,11 +1061,28 @@ static inline enum imx_i2c_state i2c_imx_isr_read_continue(struct imx_i2c_struct static inline void i2c_imx_isr_read_block_data_len(struct imx_i2c_struct *i2c_imx) { u8 len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); + unsigned int temp; if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) { + /* + * SMBus 3.1 6.5.7: support count byte of 0. + * I2C_SMBUS_BLOCK_MAX case should not hold the SDA either. + * So NACK it (TXAK) to not hold the bus. + */ + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + temp |= I2CR_TXAK; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); + + if (len == 0) { + i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = 0; + i2c_imx->msg->len = 2; + return; + } + i2c_imx->isr_result = -EPROTO; i2c_imx->state = IMX_I2C_STATE_FAILED; wake_up(&i2c_imx->queue); + return; } i2c_imx->msg->len += len; i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = len; -- cgit v1.2.3 From 982e31382d9a1a3c8c4e6a13702a53711f4efe9f Mon Sep 17 00:00:00 2001 From: "Harry Yoo (Oracle)" Date: Mon, 13 Jul 2026 23:28:50 +0900 Subject: mm/slab: decouple SLAB_NO_SHEAVES from SLAB_NO_OBJ_EXT Bootstrap caches are created with SLAB_NO_OBJ_EXT to disallow sheaves and obj_exts. To allow disabling obj_exts while allowing sheaves, decouple SLAB_NO_SHEAVES from SLAB_NO_OBJ_EXT. Bootstrap caches now have both SLAB_NO_SHEAVES and SLAB_NO_OBJ_EXT. No functional change intended. Reviewed-by: Vlastimil Babka (SUSE) Signed-off-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-2-47c7bd138de7@kernel.org Signed-off-by: Vlastimil Babka (SUSE) --- include/linux/slab.h | 13 +++++++++++-- mm/slub.c | 10 ++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 51f03f18c9a7..08d7b6c9c4d6 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -58,10 +58,13 @@ enum _slab_flag_bits { #endif _SLAB_OBJECT_POISON, _SLAB_CMPXCHG_DOUBLE, +#ifdef CONFIG_SLAB_OBJ_EXT _SLAB_NO_OBJ_EXT, -#if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT) +#ifdef CONFIG_64BIT _SLAB_OBJ_EXT_IN_OBJ, #endif +#endif + _SLAB_NO_SHEAVES, _SLAB_FLAGS_LAST_BIT }; @@ -239,8 +242,14 @@ enum _slab_flag_bits { #endif #define SLAB_TEMPORARY SLAB_RECLAIM_ACCOUNT /* Objects are short-lived */ -/* Slab created using create_boot_cache */ +/* Slab caches without obj_exts array */ +#ifdef CONFIG_SLAB_OBJ_EXT #define SLAB_NO_OBJ_EXT __SLAB_FLAG_BIT(_SLAB_NO_OBJ_EXT) +#else +#define SLAB_NO_OBJ_EXT __SLAB_FLAG_UNUSED +#endif + +#define SLAB_NO_SHEAVES __SLAB_FLAG_BIT(_SLAB_NO_SHEAVES) #if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT) #define SLAB_OBJ_EXT_IN_OBJ __SLAB_FLAG_BIT(_SLAB_OBJ_EXT_IN_OBJ) diff --git a/mm/slub.c b/mm/slub.c index f9461a0c47d3..abe748b7dddb 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -7799,12 +7799,12 @@ static unsigned int calculate_sheaf_capacity(struct kmem_cache *s, return 0; /* - * Bootstrap caches can't have sheaves for now (SLAB_NO_OBJ_EXT). + * Bootstrap caches can't have sheaves for now (SLAB_NO_SHEAVES). * SLAB_NOLEAKTRACE caches (e.g., kmemleak's object_cache) must not * have sheaves to avoid recursion when sheaf allocation triggers * kmemleak tracking. */ - if (s->flags & (SLAB_NO_OBJ_EXT | SLAB_NOLEAKTRACE)) + if (s->flags & (SLAB_NO_SHEAVES | SLAB_NOLEAKTRACE)) return 0; /* @@ -8586,7 +8586,8 @@ void __init kmem_cache_init(void) create_boot_cache(kmem_cache_node, "kmem_cache_node", sizeof(struct kmem_cache_node), - SLAB_HWCACHE_ALIGN | SLAB_NO_OBJ_EXT, 0, 0); + SLAB_HWCACHE_ALIGN | SLAB_NO_SHEAVES | SLAB_NO_OBJ_EXT, + 0, 0); hotplug_node_notifier(slab_memory_callback, SLAB_CALLBACK_PRI); @@ -8596,7 +8597,8 @@ void __init kmem_cache_init(void) create_boot_cache(kmem_cache, "kmem_cache", offsetof(struct kmem_cache, per_node) + nr_node_ids * sizeof(struct kmem_cache_per_node_ptrs), - SLAB_HWCACHE_ALIGN | SLAB_NO_OBJ_EXT, 0, 0); + SLAB_HWCACHE_ALIGN | SLAB_NO_SHEAVES | SLAB_NO_OBJ_EXT, + 0, 0); kmem_cache = bootstrap(&boot_kmem_cache); kmem_cache_node = bootstrap(&boot_kmem_cache_node); -- cgit v1.2.3 From a37b0066a10aabf3c968b4566706fb866eaf9a85 Mon Sep 17 00:00:00 2001 From: "Harry Yoo (Oracle)" Date: Mon, 13 Jul 2026 23:28:51 +0900 Subject: lib/alloc_tag: introduce mem_alloc_profiling_permanently_disabled() mem_alloc_profiling_enabled() tells whether memalloc profiling is currently enabled. However, even when this function returns false, it can be enabled later. However, this is not enough. Some optimizations can be applied only when memalloc profiling is permanently disabled. For example, to skip the creation of KMALLOC_NO_OBJ_EXT caches at boot time, mem_profiling must be set to "never", "0" w/ debugging on, or have been shutdown so that it can no longer be enabled. Introduce mem_alloc_profiling_permanently_disabled() for this purpose. Signed-off-by: Harry Yoo (Oracle) Acked-by: Suren Baghdasaryan Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-3-47c7bd138de7@kernel.org Signed-off-by: Vlastimil Babka (SUSE) --- include/linux/alloc_tag.h | 3 +++ lib/alloc_tag.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/include/linux/alloc_tag.h b/include/linux/alloc_tag.h index 068ba2e77c5d..6ed9f82e639f 100644 --- a/include/linux/alloc_tag.h +++ b/include/linux/alloc_tag.h @@ -134,6 +134,8 @@ static inline bool mem_alloc_profiling_enabled(void) &mem_alloc_profiling_key); } +bool mem_alloc_profiling_permanently_disabled(void); + static inline struct alloc_tag_counters alloc_tag_read(struct alloc_tag *tag) { struct alloc_tag_counters v = { 0, 0 }; @@ -239,6 +241,7 @@ static inline bool alloc_tag_is_inaccurate(struct alloc_tag *tag) #define DEFINE_ALLOC_TAG(_alloc_tag) static inline bool mem_alloc_profiling_enabled(void) { return false; } +static inline bool mem_alloc_profiling_permanently_disabled(void) { return true; } static inline void alloc_tag_add(union codetag_ref *ref, struct alloc_tag *tag, size_t bytes) {} static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) {} diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index d9be1cf5187d..e5b218176c5a 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -26,6 +26,15 @@ static bool mem_profiling_support = true; static bool mem_profiling_support; #endif +/* + * Memory allocation profiling is permanently disabled and cannot be enabled. + * Must be called after setup_early_mem_profiling(). + */ +bool mem_alloc_profiling_permanently_disabled(void) +{ + return !mem_profiling_support; +} + static struct codetag_type *alloc_tag_cttype; #ifdef CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU -- cgit v1.2.3 From d9e6a7623938968e3752b67e37eaff097e559a54 Mon Sep 17 00:00:00 2001 From: "Harry Yoo (Oracle)" Date: Mon, 13 Jul 2026 23:28:52 +0900 Subject: mm/slab: prevent unbounded recursion in free path with new kmalloc type Commit 280ea9c3154b ("mm/slab: avoid allocating slabobj_ext array from its own slab") avoided recursive allocation of obj_exts from kmalloc caches of the same size, by bumping the obj_exts array's allocation size whenever the array size equals the size of the object being allocated. However, as reported by Danielle Costantino and Shakeel Butt, even slabs from kmalloc caches of different sizes can form a cycle by allocating obj_exts arrays from each other [1]: What happened: a KMALLOC_NORMAL slab's obj_exts array (used by allocation profiling / memcg accounting) is itself kmalloc()'d from a KMALLOC_NORMAL cache, so the "slab holds another slab's obj_exts array" relation can form cycles. With sizeof(struct slabobj_ext) == 16 and the host's geometry: - kmalloc-512 has 64 objects/slab -> array is 64*16 == 1024 bytes, served from kmalloc-1k; - kmalloc-1k has 32 objects/slab -> array is 32*16 == 512 bytes, served from kmalloc-512. A kmalloc-512 slab and a kmalloc-1k slab therefore hold each other's obj_exts array. Discarding one frees the other's array, which empties and discards that slab, which frees the first's array, and so on: __free_slab() -> free_slab_obj_exts() -> kfree() -> discard_slab() -> __free_slab() recurses along the cycle until the stack is exhausted. With memory allocation profiling, this allows unbounded recursion in the free path and led to a stack overflow on a production host in the Meta fleet [1]: BUG: TASK stack guard page was hit Oops: stack guard page RIP: 0010:kfree+0x8/0x5d0 Call Trace: __free_slab+0x66/0xc0 kfree+0x3f0/0x5d0 ... ( ~125x __free_slab <-> kfree ) ... do_syscall_64 It is proposed [1] to resolve this issue by always serving the obj_exts array allocation from kmalloc caches (or large kmalloc) of sizes larger than the object size. However, as pointed out by Vlastimil Babka [2], this can waste an excessive amount of memory as slabs from large kmalloc sizes (e.g. kmalloc-8k) generally need obj_exts arrays much smaller than the object size. Therefore, rather than bumping the size, let us take a different approach; disallow formation of cycles between kmalloc types when allocating obj_exts arrays. Currently, all obj_exts arrays are served from normal kmalloc caches. Cycles cannot be created if obj_exts arrays of normal kmalloc caches are served from a special kmalloc type that can never have obj_exts arrays. To achieve this, create a new kmalloc type called KMALLOC_NO_OBJ_EXT. KMALLOC_NO_OBJ_EXT caches are created with SLAB_NO_OBJ_EXT flag when either 1) memory allocation profiling is not permanently disabled, or 2) kmalloc types with a priority higher than KMALLOC_CGROUP are aliased with KMALLOC_NORMAL. Sheaf bootstrapping for KMALLOC_NO_OBJ_EXT caches now must be deferred because allocation of a barn can trigger obj_exts array allocation of normal kmalloc caches when the KMALLOC_NO_OBJ_EXT cache for that size is not ready yet. For simplicity, perform bootstrapping of sheaves for all kmalloc caches later. Introduce a new slab alloc flag, SLAB_ALLOC_NO_OBJ_EXT, to prevent allocation of obj_exts arrays, and let kmalloc_slab() override the type to KMALLOC_NO_OBJ_EXT when specified. Note that kmalloc_type() remains unchanged because kmalloc_flags() bypasses the kmalloc fastpath. Do not pass SLAB_ALLOC_NO_RECURSE to kmalloc_flags() in alloc_slab_obj_exts() and instead use SLAB_ALLOC_NO_OBJ_EXT only when the objects are allocated from normal kmalloc caches. While this prevents unbounded recursive allocation of obj_exts, it allows KMALLOC_NO_OBJ_EXT caches to have sheaves. Since sheaf allocations specify SLAB_ALLOC_NO_RECURSE that prevents allocation of both sheaves and obj_exts arrays, the recursion depth is bounded. obj_exts arrays for non-kmalloc-normal caches can now have a valid tag. Do not call mark_obj_codetag_empty() when freeing an obj_exts array to avoid false warnings. KMALLOC_NO_OBJ_EXT don't need this as they never allocate those arrays. Reported-by: Danielle Costantino Reported-by: Shakeel Butt Closes: https://lore.kernel.org/linux-mm/20260625230029.703750-1-shakeel.butt@linux.dev [1] Fixes: 4b8736964640 ("mm/slab: add allocation accounting into slab allocation and free paths") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-mm/c5c4208d-a6f0-413e-bad9-49be12f12d55@kernel.org [2] Signed-off-by: Harry Yoo (Oracle) Reviewed-by: Suren Baghdasaryan Link: https://patch.msgid.link/20260713-kmalloc-no-objext-v3-4-47c7bd138de7@kernel.org Signed-off-by: Vlastimil Babka (SUSE) --- include/linux/slab.h | 6 ++++ mm/slab.h | 32 +++++++++++++++++-- mm/slab_common.c | 19 ++++++++++- mm/slub.c | 90 +++++++++++++++++++--------------------------------- 4 files changed, 85 insertions(+), 62 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 08d7b6c9c4d6..3a14df50766a 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -708,6 +708,9 @@ enum kmalloc_cache_type { #endif #ifndef CONFIG_MEMCG KMALLOC_CGROUP = KMALLOC_NORMAL, +#endif +#ifndef CONFIG_SLAB_OBJ_EXT + KMALLOC_NO_OBJ_EXT = KMALLOC_NORMAL, #endif KMALLOC_PARTITION_START = KMALLOC_NORMAL, KMALLOC_PARTITION_END = KMALLOC_PARTITION_START + KMALLOC_PARTITION_CACHES_NR, @@ -721,6 +724,9 @@ enum kmalloc_cache_type { #endif #ifdef CONFIG_MEMCG KMALLOC_CGROUP, +#endif +#ifdef CONFIG_SLAB_OBJ_EXT + KMALLOC_NO_OBJ_EXT, #endif NR_KMALLOC_TYPES }; diff --git a/mm/slab.h b/mm/slab.h index 281a65233795..f5e336b6b6b0 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -22,6 +22,7 @@ #define SLAB_ALLOC_NOLOCK 0x01 /* a kmalloc_nolock() allocation */ #define SLAB_ALLOC_NEW_SLAB 0x02 /* a flag for alloc_slab_obj_exts() */ #define SLAB_ALLOC_NO_RECURSE 0x04 /* prevent kmalloc() recursion */ +#define SLAB_ALLOC_NO_OBJ_EXT 0x08 /* prevent obj_exts array allocation */ static inline bool alloc_flags_allow_spinning(const unsigned int alloc_flags) { @@ -386,12 +387,17 @@ static inline unsigned int size_index_elem(unsigned int bytes) * KMALLOC_MAX_CACHE_SIZE and the caller must check that. */ static inline struct kmem_cache * -kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, kmalloc_token_t token) +kmalloc_slab(size_t size, kmem_buckets *b, gfp_t flags, kmalloc_token_t token, + unsigned int alloc_flags) { unsigned int index; + enum kmalloc_cache_type type = kmalloc_type(flags, token); + + if (alloc_flags & SLAB_ALLOC_NO_OBJ_EXT) + type = KMALLOC_NO_OBJ_EXT; if (!b) - b = &kmalloc_caches[kmalloc_type(flags, token)]; + b = &kmalloc_caches[type]; if (size <= 192) index = kmalloc_size_index[size_index_elem(size)]; else @@ -426,7 +432,8 @@ static inline bool is_kmalloc_normal(struct kmem_cache *s) { if (!is_kmalloc_cache(s)) return false; - return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT)); + + return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT|SLAB_NO_OBJ_EXT)); } bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj); @@ -529,6 +536,25 @@ static inline void metadata_access_disable(void) kasan_enable_current(); } +/* + * Return true if KMALLOC_NORMAL caches may need obj_exts arrays. + * + * Memory allocation profiling requires obj_exts for all caches. + * Memcg usually doesn't need them for normal kmalloc caches, but kmalloc types + * with a priority higher than KMALLOC_CGROUP can be aliased with KMALLOC_NORMAL. + */ +static inline bool need_kmalloc_no_objext(void) +{ + if (!mem_alloc_profiling_permanently_disabled()) + return true; + + if (!mem_cgroup_kmem_disabled() && + (KMALLOC_NORMAL == KMALLOC_RECLAIM)) + return true; + + return false; +} + #ifdef CONFIG_SLAB_OBJ_EXT /* diff --git a/mm/slab_common.c b/mm/slab_common.c index b6426d7ceec9..03ecac12cd86 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -783,11 +783,15 @@ u8 kmalloc_size_index[24] __ro_after_init = { size_t kmalloc_size_roundup(size_t size) { if (size && size <= KMALLOC_MAX_CACHE_SIZE) { + struct kmem_cache *s; + /* * The flags don't matter since size_index is common to all. * Neither does the caller for just getting ->object_size. */ - return kmalloc_slab(size, NULL, GFP_KERNEL, __kmalloc_token(0))->object_size; + s = kmalloc_slab(size, NULL, GFP_KERNEL, __kmalloc_token(0), + SLAB_ALLOC_DEFAULT); + return s->object_size; } /* Above the smaller buckets, size is a multiple of page size. */ @@ -843,6 +847,12 @@ EXPORT_SYMBOL(kmalloc_size_roundup); #define KMALLOC_PARTITION_NAME(N, sz) #endif +#ifdef CONFIG_SLAB_OBJ_EXT +#define KMALLOC_NO_OBJ_EXT_NAME(sz) .name[KMALLOC_NO_OBJ_EXT] = "kmalloc-no-objext-" #sz, +#else +#define KMALLOC_NO_OBJ_EXT_NAME(sz) +#endif + #define INIT_KMALLOC_INFO(__size, __short_size) \ { \ .name[KMALLOC_NORMAL] = "kmalloc-" #__short_size, \ @@ -850,6 +860,7 @@ EXPORT_SYMBOL(kmalloc_size_roundup); KMALLOC_CGROUP_NAME(__short_size) \ KMALLOC_DMA_NAME(__short_size) \ KMALLOC_PARTITION_NAME(KMALLOC_PARTITION_CACHES_NR, __short_size) \ + KMALLOC_NO_OBJ_EXT_NAME(__short_size) \ .size = __size, \ } @@ -957,6 +968,12 @@ new_kmalloc_cache(int idx, enum kmalloc_cache_type type) return; } flags |= SLAB_ACCOUNT; + } else if (IS_ENABLED(CONFIG_SLAB_OBJ_EXT) && type == KMALLOC_NO_OBJ_EXT) { + if (!need_kmalloc_no_objext()) { + kmalloc_caches[type][idx] = kmalloc_caches[KMALLOC_NORMAL][idx]; + return; + } + flags |= SLAB_NO_OBJ_EXT | SLAB_NO_MERGE; } else if (IS_ENABLED(CONFIG_ZONE_DMA) && (type == KMALLOC_DMA)) { flags |= SLAB_CACHE_DMA; } diff --git a/mm/slub.c b/mm/slub.c index abe748b7dddb..0337e60db5ac 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2123,42 +2123,6 @@ static inline void init_slab_obj_exts(struct slab *slab) slab->obj_exts = 0; } -/* - * Calculate the allocation size for slabobj_ext array. - * - * When memory allocation profiling is enabled, the obj_exts array - * could be allocated from the same slab cache it's being allocated for. - * This would prevent the slab from ever being freed because it would - * always contain at least one allocated object (its own obj_exts array). - * - * To avoid this, increase the allocation size when we detect the array - * may come from the same cache, forcing it to use a different cache. - */ -static inline size_t obj_exts_alloc_size(struct kmem_cache *s, - struct slab *slab, gfp_t gfp) -{ - size_t sz = sizeof(struct slabobj_ext) * slab->objects; - struct kmem_cache *obj_exts_cache; - - if (sz > KMALLOC_MAX_CACHE_SIZE) - return sz; - - if (!is_kmalloc_normal(s)) - return sz; - - obj_exts_cache = kmalloc_slab(sz, NULL, gfp, __kmalloc_token(0)); - /* - * We can't simply compare s with obj_exts_cache, because partitioned kmalloc - * caches have multiple caches per size, selected by caller address or type. - * Since caller address or type may differ between kmalloc_slab() and actual - * allocation, bump size when sizes are equal. - */ - if (s->object_size == obj_exts_cache->object_size) - return obj_exts_cache->object_size + 1; - - return sz; -} - int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, gfp_t gfp, unsigned int alloc_flags) { @@ -2168,14 +2132,18 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, unsigned long new_exts; unsigned long old_exts; struct slabobj_ext *vec; - size_t sz; + size_t sz = sizeof(struct slabobj_ext) * slab->objects; gfp &= ~OBJCGS_CLEAR_MASK; - /* Prevent recursive extension vector allocation */ - alloc_flags |= SLAB_ALLOC_NO_RECURSE; - alloc_flags &= ~SLAB_ALLOC_NEW_SLAB; + /* + * In most cases, obj_exts arrays are allocated from normal kmalloc. + * However, normal kmalloc caches must allocate them from + * KMALLOC_NO_OBJ_EXT caches to prevent recursion. + */ + if (is_kmalloc_normal(s)) + alloc_flags |= SLAB_ALLOC_NO_OBJ_EXT; - sz = obj_exts_alloc_size(s, slab, gfp); + alloc_flags &= ~SLAB_ALLOC_NEW_SLAB; /* This will use kmalloc_nolock() if alloc_flags say so */ vec = kmalloc_flags(sz, gfp | __GFP_ZERO, alloc_flags, slab_nid(slab)); @@ -2193,8 +2161,21 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, return -ENOMEM; } - VM_WARN_ON_ONCE(virt_to_slab(vec) != NULL && - virt_to_slab(vec)->slab_cache == s); + if (IS_ENABLED(CONFIG_DEBUG_VM)) { + struct kmem_cache *exts_cache; + struct slab *exts_slab; + + exts_slab = virt_to_slab(vec); + if (exts_slab) { + /* + * The vector must be allocated from either normal or + * KMALLOC_NO_OBJ_EXT kmalloc caches to avoid cycles. + */ + exts_cache = exts_slab->slab_cache; + WARN_ON_ONCE(!is_kmalloc_normal(exts_cache) && + !(exts_cache->flags & SLAB_NO_OBJ_EXT)); + } + } new_exts = (unsigned long)vec; #ifdef CONFIG_MEMCG @@ -2217,7 +2198,6 @@ retry: * assign slabobj_exts in parallel. In this case the existing * objcg vector should be reused. */ - mark_obj_codetag_empty(vec); if (unlikely(!allow_spin)) kfree_nolock(vec); else @@ -2253,14 +2233,6 @@ static inline void free_slab_obj_exts(struct slab *slab, bool allow_spin) return; } - /* - * obj_exts was created with SLAB_ALLOC_NO_RECURSE flag, therefore its - * corresponding extension will be NULL. alloc_tag_sub() will throw a - * warning if slab has extensions but the extension of an object is - * NULL, therefore replace NULL with CODETAG_EMPTY to indicate that - * the extension for obj_exts is expected to be NULL. - */ - mark_obj_codetag_empty(obj_exts); if (allow_spin) kfree(obj_exts); else @@ -5356,7 +5328,7 @@ void *__do_kmalloc_node(kmem_buckets *b, gfp_t flags, int node, if (unlikely(!size)) return ZERO_SIZE_PTR; - s = kmalloc_slab(size, b, flags, token); + s = kmalloc_slab(size, b, flags, token, ac->alloc_flags); ret = slab_alloc_node(s, flags, node, ac); ret = kasan_kmalloc(s, ret, size, flags); @@ -5419,7 +5391,9 @@ static void *__kmalloc_nolock_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t gfp_f retry: if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) return NULL; - s = kmalloc_slab(size, NULL, gfp_flags, PASS_TOKEN_PARAM(token)); + + s = kmalloc_slab(size, NULL, gfp_flags, PASS_TOKEN_PARAM(token), + ac->alloc_flags); if (!(s->flags & __CMPXCHG_DOUBLE) && !kmem_cache_debug(s)) /* @@ -7979,10 +7953,10 @@ static int calculate_sizes(struct kmem_cache_args *args, struct kmem_cache *s) s->allocflags |= __GFP_RECLAIMABLE; /* - * For KMALLOC_NORMAL caches we enable sheaves later by - * bootstrap_kmalloc_sheaves() to avoid recursion + * For kmalloc caches we enable sheaves later by + * bootstrap_kmalloc_sheaves() to avoid recursion. */ - if (!is_kmalloc_normal(s)) + if (!is_kmalloc_cache(s)) s->sheaf_capacity = calculate_sheaf_capacity(s, args); /* @@ -8548,7 +8522,7 @@ static void __init bootstrap_kmalloc_sheaves(void) { enum kmalloc_cache_type type; - for (type = KMALLOC_NORMAL; type <= KMALLOC_PARTITION_END; type++) { + for (type = KMALLOC_NORMAL; type < NR_KMALLOC_TYPES; type++) { for (int idx = 0; idx < KMALLOC_SHIFT_HIGH + 1; idx++) { struct kmem_cache *s = kmalloc_caches[type][idx]; -- cgit v1.2.3 From 9db20d23aac7916ff49be409a4bfd48fe7cbbfb4 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Fri, 10 Jul 2026 15:21:13 +0800 Subject: i2c: spacemit: fix spurious IRQ handling returning IRQ_HANDLED When the interrupt status register reads zero, the handler should return IRQ_NONE instead of IRQ_HANDLED. What the return value actually feeds into is the spurious interrupt accounting in note_interrupt(): falsely claiming IRQ_HANDLED defeats the "irq XX: nobody cared" detection, so a stuck interrupt source would never be caught. Fixes: 5ea558473fa3 ("i2c: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Pei Xiao Cc: # v6.15+ Reviewed-by: Troy Mitchell Reviewed-by: Mukesh Savaliya Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/ef8b623f45d4e430721e46572c2598d882044aed.1783667875.git.xiaopei01@kylinos.cn --- drivers/i2c/busses/i2c-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 9152cf436bea..51a0c3d80fc9 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -596,7 +596,7 @@ static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) status = readl(i2c->base + SPACEMIT_ISR); if (!status) - return IRQ_HANDLED; + return IRQ_NONE; i2c->status = status; -- cgit v1.2.3 From 0bc7c196f3ab855da0879283ba633c4c51ddfd81 Mon Sep 17 00:00:00 2001 From: Honglei Huang Date: Wed, 1 Jul 2026 14:27:58 +0800 Subject: drm/gpusvm: free the whole IOVA reservation on unmap dma_iova_try_alloc() reserves IOVA for the entire range, but in a mixed range only the system pages are linked (their total size is state_offset) while device pages never touch the IOVA state. dma_iova_destroy() with state_offset only frees the linked part, permanently leaking the IOVA reserved for the device pages and eventually exhausting the IOVA space. Unlink the linked system-page portion and free the whole reserved IOVA instead. On the get_pages() error path state_offset is 0 (no page linked, dma_addr[0] unpopulated), so skip the unlink and just free the reservation; this also avoids reading the uninitialized dma_addr[0].dir there. Allocate the dma_addr array with the zeroing kvzalloc_objs() so every entry has a well-defined value. This issue was found by Sashiko AI review. Fixes: 37ad039fb367 ("drm/gpusvm: Use dma-map IOVA alloc, link, and sync API in GPU SVM") Cc: stable@vger.kernel.org Reviewed-by: Matthew Brost Signed-off-by: Honglei Huang Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260701062800.409248-2-honghuan@amd.com --- drivers/gpu/drm/drm_gpusvm.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 958cb605aedd..3145d55cd860 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1146,10 +1146,19 @@ static void __drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, }; bool use_iova = dma_use_iova(&svm_pages->state); - if (use_iova) - dma_iova_destroy(dev, &svm_pages->state, - svm_pages->state_offset, - svm_pages->dma_addr[0].dir, 0); + /* + * IOVA is reserved for the whole range but only the linked + * system pages (state_offset bytes) need unlinking; free the + * entire reservation to avoid leaking the device-page part. + * On the error path state_offset is 0, so just free it. + */ + if (use_iova) { + if (svm_pages->state_offset) + dma_iova_unlink(dev, &svm_pages->state, 0, + svm_pages->state_offset, + svm_pages->dma_addr[0].dir, 0); + dma_iova_free(dev, &svm_pages->state); + } for (i = 0, j = 0; i < npages; j++) { struct drm_pagemap_addr *addr = &svm_pages->dma_addr[j]; @@ -1486,7 +1495,7 @@ map_pages: /* Unlock and restart mapping to allocate memory. */ drm_gpusvm_notifier_unlock(gpusvm); svm_pages->dma_addr = - kvmalloc_objs(*svm_pages->dma_addr, npages); + kvzalloc_objs(*svm_pages->dma_addr, npages); if (!svm_pages->dma_addr) { err = -ENOMEM; goto err_free; -- cgit v1.2.3 From ea2f9985aa4adeef89e9523be295633bb3d0874a Mon Sep 17 00:00:00 2001 From: Honglei Huang Date: Wed, 1 Jul 2026 14:27:59 +0800 Subject: drm/gpusvm: do not route system pages to device_unmap() on IOVA unmap In a mixed range: ctx->allow_mixed dpagemap is not NULL while some entries are system pages. The unmap loop used: dma_unmap_page(...); else if (dpagemap && dpagemap->ops->device_unmap) dpagemap->ops->device_unmap(...); When use_iova is true the first condition is false for system pages, so they fall through to device_unmap() and a system DMA address is handed to the device specific unmap callback, risking invalid accesses or state corruption. Key the branch off addr->proto instead: system pages only need an explicit dma_unmap_page() in the non IOVA case, IOVA system pages are already torn down by the single dma_iova_destroy(), and only genuine device pages reach device_unmap(). This issue was found by Sashiko AI review. Fixes: 37ad039fb367 ("drm/gpusvm: Use dma-map IOVA alloc, link, and sync API in GPU SVM") Cc: stable@vger.kernel.org Reviewed-by: Matthew Brost Signed-off-by: Honglei Huang Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260701062800.409248-3-honghuan@amd.com --- drivers/gpu/drm/drm_gpusvm.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 3145d55cd860..44bb19658dd6 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1163,12 +1163,18 @@ static void __drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, for (i = 0, j = 0; i < npages; j++) { struct drm_pagemap_addr *addr = &svm_pages->dma_addr[j]; - if (!use_iova && addr->proto == DRM_INTERCONNECT_SYSTEM) - dma_unmap_page(dev, - addr->addr, - PAGE_SIZE << addr->order, - addr->dir); - else if (dpagemap && dpagemap->ops->device_unmap) + if (addr->proto == DRM_INTERCONNECT_SYSTEM) { + /* + * Linked IOVA pages were already torn down by + * the dma_iova_unlink()/dma_iova_free() above; + * only the non-IOVA mappings need unmap here. + */ + if (!use_iova) + dma_unmap_page(dev, + addr->addr, + PAGE_SIZE << addr->order, + addr->dir); + } else if (dpagemap && dpagemap->ops->device_unmap) dpagemap->ops->device_unmap(dpagemap, dev, addr); i += 1 << addr->order; -- cgit v1.2.3 From 7f708f51e3955bda0d77a0b67ab9bea6c97fea99 Mon Sep 17 00:00:00 2001 From: Honglei Huang Date: Wed, 1 Jul 2026 14:28:00 +0800 Subject: drm/gpusvm: publish dpagemap early to avoid device mapping leak on error drm_gpusvm_get_pages() only stored the local dpagemap into svm_pages->dpagemap on the success path. If a later page failed (e.g. -EOPNOTSUPP when ctx->allow_mixed is false) and jumped to err_unmap, svm_pages->dpagemap was still NULL, so __drm_gpusvm_unmap_pages() skipped device_unmap() and leaked the device mappings already created. Assign svm_pages->dpagemap when the first device page is mapped so the err_unmap path can device_unmap() those mappings. This issue was found by Sashiko AI review. Fixes: f70da6f99d4f ("drm/gpusvm: pull out drm_gpusvm_pages substructure") Cc: stable@vger.kernel.org Reviewed-by: Matthew Brost Signed-off-by: Honglei Huang Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260701062800.409248-4-honghuan@amd.com --- drivers/gpu/drm/drm_gpusvm.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 44bb19658dd6..9a06ff7d2608 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1544,6 +1544,16 @@ map_pages: err = -EAGAIN; goto err_unmap; } + + /* + * Set the dpagemap as soon as the first + * device page is mapped so the err_unmap path + * can device_unmap() the device mappings that + * have already been created. + */ + drm_pagemap_get(dpagemap); + drm_pagemap_put(svm_pages->dpagemap); + svm_pages->dpagemap = dpagemap; } svm_pages->dma_addr[j] = dpagemap->ops->device_map(dpagemap, @@ -1611,12 +1621,8 @@ map_pages: goto err_unmap; } - if (pagemap) { + if (pagemap) flags.has_devmem_pages = true; - drm_pagemap_get(dpagemap); - drm_pagemap_put(svm_pages->dpagemap); - svm_pages->dpagemap = dpagemap; - } /* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */ WRITE_ONCE(svm_pages->flags.__flags, flags.__flags); -- cgit v1.2.3 From 1826215eb63b57a4ac8cb973785a84d703ff23f5 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Tue, 14 Jul 2026 14:19:50 +0300 Subject: wifi: iwlwifi: mvm: verify scan id reported by firmware The scan id reported by firmware in scan complete notification is used as an index to the scan status array. Verify the reported id does not exceed the array size. Signed-off-by: Avraham Stern Link: https://patch.msgid.link/20260714141909.fdf31f494f1c.I70d01ed2023f6584fb23ea8ab344a93d222cc4c0@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 79829f775c89..42f9d9a713b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -3217,6 +3217,10 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, mvm->mei_scan_filter.is_mei_limited_scan = false; + if (IWL_FW_CHECK(mvm, uid >= ARRAY_SIZE(mvm->scan_uid_status), + "FW reports out-of-range scan UID %d\n", uid)) + return; + IWL_DEBUG_SCAN(mvm, "Scan completed: uid=%u type=%u, status=%s, EBS=%s\n", uid, mvm->scan_uid_status[uid], -- cgit v1.2.3 From d77aff138c9ec6c8562f4c2c9f262d3d9c4b4cb8 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:51 +0300 Subject: wifi: iwlwifi: mvm: fix an off-by-1 boundary check Before looking at the 11th byte, check the length is big enough. Signed-off-by: Emmanuel Grumbach Reviewed-by: Ilan Peer Link: https://patch.msgid.link/20260714141909.d22bf52a18d0.If0ef6612a67cca671428b06dbdeec68549e50ae6@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 74bd4038fd56..48cc10db7b96 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3490,7 +3490,7 @@ static void iwl_mvm_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data, ies->len); - if (!elem || elem->datalen < 10 || + if (!elem || elem->datalen < 11 || !(elem->data[10] & WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) { data->tolerated = false; -- cgit v1.2.3 From 402620cdcf4d4ee311551906dfae832b33a0cc60 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:52 +0300 Subject: wifi: iwlwifi: mld: fix an off-by-1 boundary check Before looking at the 11th byte, check the length is big enough. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.336b527e3fc6.I6fe839f4e70d673632fd7ca757e81827af87b029@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 17286b3341c0..9ac17be30400 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1708,7 +1708,7 @@ static void iwl_mld_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data, ies->len); - if (!elem || elem->datalen < 10 || + if (!elem || elem->datalen < 11 || !(elem->data[10] & WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) { *tolerated = false; -- cgit v1.2.3 From 119c353467d802865e2f5da210b64727c5e334b4 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:53 +0300 Subject: wifi: iwlwifi: mld: don't parse a notif before checking its length In order to compure the size of the iwl_mcc_update_resp which has a variable length, we need to know the number of channels. In order to read the number of channels, we must first check the payload is long enough to read at least that. Add this check. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.c2f644919011.Ic579e9935b92a674c96ccc44713140b5b4bc5d10@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/mcc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c index 8502129abe49..830c251f43af 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -18,9 +18,15 @@ static struct iwl_mcc_update_resp_v8 * iwl_mld_copy_mcc_resp(const struct iwl_rx_packet *pkt) { const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data; - int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); struct iwl_mcc_update_resp_v8 *resp_cp; - int notif_len = struct_size(resp_cp, channels, n_channels); + int n_channels; + int notif_len; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*mcc_resp_v8)) + return ERR_PTR(-EINVAL); + + n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); + notif_len = struct_size(resp_cp, channels, n_channels); if (iwl_rx_packet_payload_len(pkt) != notif_len) return ERR_PTR(-EINVAL); -- cgit v1.2.3 From 7da7162652a9e254f2c6055cb8612bbe8a49a554 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:54 +0300 Subject: wifi: iwlwifi: mvm: fix the FCS truncation logic in d3 Fix a harmless mistake in the wake packet management code in the d3 wakeup flow. If the FCS is truncated, we want to detect it, but we cleared the icvlen before updating the truncated variable that holds the number of bytes having been truncated. Fix that. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.369588f93c6b.I1a4d13f276c7e75514ab2032ae387873337470b8@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 9a74f60c9185..d7ceb385ae0b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1539,8 +1539,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, /* if truncated, FCS/ICV is (partially) gone */ if (truncated >= icvlen) { - icvlen = 0; truncated -= icvlen; + icvlen = 0; } else { icvlen -= truncated; truncated = 0; -- cgit v1.2.3 From 219292e73e40d131925a1caf413203671092dd7a Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:55 +0300 Subject: wifi: iwlwifi: mld: validate reorder BAID Reject BAIDs >= IWL_MAX_BAID before indexing fw_id_to_ba. This prevents out-of-bounds access on malformed notifications. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.07ea823b8eea.Ica915fa0cce0427bf5e3420ae933f57118fedf86@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/agg.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index e3627ad0321c..a464ebdec57f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "agg.h" #include "sta.h" @@ -222,6 +222,11 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, if (baid == IWL_RX_REORDER_DATA_INVALID_BAID) return IWL_MLD_PASS_SKB; + if (IWL_FW_CHECK(mld, baid >= ARRAY_SIZE(mld->fw_id_to_ba), + "Got out-of-range BAID %u in reorder_data=0x%x\n", + baid, reorder)) + return IWL_MLD_PASS_SKB; + /* no sta yet */ if (WARN_ONCE(!sta, "Got valid BAID without a valid station assigned\n")) -- cgit v1.2.3 From b77c6f50b1f80414cb3f542ae72e532ed90fc7f7 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:56 +0300 Subject: wifi: iwlwifi: mvm: parse beacon notif per layout The beacon TX notification can arrive in different layouts, and fields must be read only after selecting the expected format. Parse gp2 and TSF from the matching notification structure in each branch, and keep using the parsed gp2 for CSA countdown and debug output. Drop the obsolete cached gp2 field. Assisted-by: GitHub Copilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.cc8aa937f8e5.I921f8dadcb20cb73e8283e1b8546e1778205411f@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 25 ++++++++++++++--------- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 - drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 --- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index d6a8624b1ae5..b2c5be22c293 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1493,49 +1493,54 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); - struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; - struct iwl_extended_beacon_notif_v5 *beacon_v5 = (void *)pkt->data; struct ieee80211_vif *csa_vif; struct ieee80211_vif *tx_blocked_vif; struct agg_tx_status *agg_status; + u32 beacon_gp2; u16 status; lockdep_assert_held(&mvm->mutex); - mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); - if (!iwl_mvm_is_short_beacon_notif_supported(mvm)) { + struct iwl_extended_beacon_notif_v5 *beacon = (void *)pkt->data; struct iwl_tx_resp *beacon_notify_hdr = - &beacon_v5->beacon_notify_hdr; + &beacon->beacon_notify_hdr; - if (unlikely(pkt_len < sizeof(*beacon_v5))) + if (unlikely(pkt_len < sizeof(*beacon))) return; - mvm->ibss_manager = beacon_v5->ibss_mgr_status != 0; + beacon_gp2 = le32_to_cpu(beacon->gp2); + + mvm->ibss_manager = beacon->ibss_mgr_status != 0; agg_status = iwl_mvm_get_agg_status(mvm, beacon_notify_hdr); status = le16_to_cpu(agg_status->status) & TX_STATUS_MSK; IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%016llX gp2:0x%X rate:%d\n", status, beacon_notify_hdr->failure_frame, le64_to_cpu(beacon->tsf), - mvm->ap_last_beacon_gp2, + beacon_gp2, le32_to_cpu(beacon_notify_hdr->initial_rate)); } else { + const struct iwl_extended_beacon_notif *beacon = + (void *)pkt->data; + if (unlikely(pkt_len < sizeof(*beacon))) return; + beacon_gp2 = le32_to_cpu(beacon->gp2); + mvm->ibss_manager = beacon->ibss_mgr_status != 0; status = le32_to_cpu(beacon->status) & TX_STATUS_MSK; IWL_DEBUG_RX(mvm, "beacon status %#x tsf:0x%016llX gp2:0x%X\n", status, le64_to_cpu(beacon->tsf), - mvm->ap_last_beacon_gp2); + beacon_gp2); } csa_vif = rcu_dereference_protected(mvm->csa_vif, lockdep_is_held(&mvm->mutex)); if (unlikely(csa_vif && csa_vif->bss_conf.csa_active)) - iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2, + iwl_mvm_csa_count_down(mvm, csa_vif, beacon_gp2, (status == TX_STATUS_SUCCESS)); tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 48cc10db7b96..f4f4446bef54 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3061,7 +3061,6 @@ void iwl_mvm_stop_ap_ibss_common(struct iwl_mvm *mvm, } mvmvif->ap_ibss_active = false; - mvm->ap_last_beacon_gp2 = 0; if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) { iwl_mvm_vif_set_low_latency(mvmvif, false, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 683cac56822c..31912f4d0175 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1168,9 +1168,6 @@ struct iwl_mvm { struct ieee80211_vif __rcu *csa_tx_blocked_vif; u8 csa_tx_block_bcn_timeout; - /* system time of last beacon (for AP/GO interface) */ - u32 ap_last_beacon_gp2; - /* indicates that we transmitted the last beacon */ bool ibss_manager; -- cgit v1.2.3 From 77f33bed0cb49a11f03427f2fa368830c1cae3c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:57 +0300 Subject: wifi: iwlwifi: mvm: validate MCC header before n_channels MCC response parsing read n_channels from v8/v4/v3 response variants before ensuring the payload contained the fixed response header. Add a minimum payload-length check for each response version before reading n_channels, and keep the existing exact-size validation for the channels array payload. Assisted-by: GitHub Copilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.cb2cef3d3e7e.Iee7b48614289da576de842157ad3730b7589a4b1@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 43 +++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 953218f1e025..f76e57399c1f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -416,6 +416,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, int ret, resp_ver; u32 status; int resp_len, n_channels; + unsigned int pkt_len; u16 mcc; if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) @@ -431,6 +432,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, return ERR_PTR(ret); pkt = cmd.resp_pkt; + pkt_len = iwl_rx_packet_payload_len(pkt); resp_ver = iwl_fw_lookup_notif_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, MCC_UPDATE_CMD, 0); @@ -439,9 +441,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, if (resp_ver >= 8) { struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (void *)pkt->data; + if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v8), + "MCC v8 response too short: %u\n", pkt_len)) { + resp_cp = ERR_PTR(-EINVAL); + goto exit; + } + n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); - if (iwl_rx_packet_payload_len(pkt) != - struct_size(mcc_resp_v8, channels, n_channels)) { + if (IWL_FW_CHECK(mvm, + pkt_len != + struct_size(mcc_resp_v8, channels, n_channels), + "invalid MCC v8 response size: %u (n_channels=%d)\n", + pkt_len, n_channels)) { resp_cp = ERR_PTR(-EINVAL); goto exit; } @@ -464,9 +475,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT)) { struct iwl_mcc_update_resp_v4 *mcc_resp_v4 = (void *)pkt->data; + if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v4), + "MCC v4 response too short: %u\n", pkt_len)) { + resp_cp = ERR_PTR(-EINVAL); + goto exit; + } + n_channels = __le32_to_cpu(mcc_resp_v4->n_channels); - if (iwl_rx_packet_payload_len(pkt) != - struct_size(mcc_resp_v4, channels, n_channels)) { + if (IWL_FW_CHECK(mvm, + pkt_len != + struct_size(mcc_resp_v4, channels, n_channels), + "invalid MCC v4 response size: %u (n_channels=%d)\n", + pkt_len, n_channels)) { resp_cp = ERR_PTR(-EINVAL); goto exit; } @@ -489,9 +509,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, } else { struct iwl_mcc_update_resp_v3 *mcc_resp_v3 = (void *)pkt->data; + if (IWL_FW_CHECK(mvm, pkt_len < sizeof(*mcc_resp_v3), + "MCC v3 response too short: %u\n", pkt_len)) { + resp_cp = ERR_PTR(-EINVAL); + goto exit; + } + n_channels = __le32_to_cpu(mcc_resp_v3->n_channels); - if (iwl_rx_packet_payload_len(pkt) != - struct_size(mcc_resp_v3, channels, n_channels)) { + if (IWL_FW_CHECK(mvm, + pkt_len != + struct_size(mcc_resp_v3, channels, n_channels), + "invalid MCC v3 response size: %u (n_channels=%d)\n", + pkt_len, n_channels)) { resp_cp = ERR_PTR(-EINVAL); goto exit; } -- cgit v1.2.3 From 3ed8d1705d3aa5fbec918b8e241b41c483706cc2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:58 +0300 Subject: wifi: iwlwifi: mvm: validate sta_id in TLC notif TLC_MNG_UPDATE_NOTIF uses firmware-provided sta_id to index fw_id_to_link_sta[] and fw_id_to_mac_id[]. Validate sta_id before array access to avoid out-of-bounds indexing. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.1ce54794c1f8.I275fd4c1165bf42fb17516c550dd8813a2b8286e@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index 89ac4c6b3e54..e2382be8edd7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "rs.h" #include "fw-api.h" @@ -423,9 +423,14 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_lq_sta_rs_fw *lq_sta; u32 flags; + notif = (void *)pkt->data; + if (IWL_FW_CHECK(mvm, notif->sta_id >= mvm->fw->ucode_capa.num_stations, + "Invalid sta id (%d) in TLC notification\n", + notif->sta_id)) + return; + rcu_read_lock(); - notif = (void *)pkt->data; link_sta = rcu_dereference(mvm->fw_id_to_link_sta[notif->sta_id]); sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]); if (IS_ERR_OR_NULL(sta) || !link_sta) { -- cgit v1.2.3 From 6aa77efaea9efea92e3090c35ad348fd759a3cf3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:19:59 +0300 Subject: wifi: iwlwifi: mvm: validate sta_id in BA window status notif BA_WINDOW_STATUS_NOTIFICATION_ID extracts a 5-bit sta_id from the firmware notification and uses it to index fw_id_to_mac_id[] without bounds checking. Validate sta_id before array access to prevent out-of-bounds indexing. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.2e97f337f3cb.Ic3f0f404082ccdea13809a3c0b70e0f5417e1037@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 269c4b45de80..ab1eb2eb0c3c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -1227,6 +1227,11 @@ void iwl_mvm_window_status_notif(struct iwl_mvm *mvm, /* get the station */ sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK) >> BA_WINDOW_STATUS_STA_ID_POS; + if (IWL_FW_CHECK(mvm, + sta_id >= mvm->fw->ucode_capa.num_stations, + "Invalid sta id (%d) in BA window status notification\n", + sta_id)) + continue; sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); if (IS_ERR_OR_NULL(sta)) continue; -- cgit v1.2.3 From 71245daf7d58a3c407c7e1422facce13ff6a584b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:20:00 +0300 Subject: wifi: iwlwifi: mvm: validate mac_link_id in session protect notif Check the mac_id before accessing the vif_id_to_mac array. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.547ea470e686.I931445ae6f37bf0e1ef6f112c811712fc48af9c9@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 1692b6e75f57..93b46c9e2333 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -951,6 +951,11 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, struct ieee80211_vif *vif; struct iwl_mvm_vif *mvmvif; + if (IWL_FW_CHECK(mvm, id >= ARRAY_SIZE(mvm->vif_id_to_mac), + "Invalid mac_link_id (%d) in session protect notif\n", + id)) + return; + rcu_read_lock(); /* note we use link ID == MAC ID */ -- cgit v1.2.3 From 5c55827ef5c74a5d56939ed0e93df21e6f1f864e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:20:01 +0300 Subject: wifi: iwlwifi: mld: clear tzone on fail iwl_mld_thermal_zone_register() stores the thermal zone pointer in mld->tzone before calling thermal_zone_device_enable(). If enable fails, the code unregisters the zone but leaves mld->tzone stale, so iwl_mld_thermal_zone_unregister() can unregister it again. Clear mld->tzone after unregister in the error path. While at it remove a pointless if in iwl_mld_thermal_zone_unregister after we've alredy checked the tzone pointer is not NULL. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.595dcb8cb7fe.I8125e4a2eeb0390798e3f4074c62c00443eda8e8@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/thermal.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c index f8a8c35066be..e445b1d7d4b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifdef CONFIG_THERMAL #include @@ -272,6 +272,7 @@ static void iwl_mld_thermal_zone_register(struct iwl_mld *mld) if (ret) { IWL_DEBUG_TEMP(mld, "Failed to enable thermal zone\n"); thermal_zone_device_unregister(mld->tzone); + mld->tzone = NULL; } } @@ -385,10 +386,8 @@ static void iwl_mld_thermal_zone_unregister(struct iwl_mld *mld) return; IWL_DEBUG_TEMP(mld, "Thermal zone device unregister\n"); - if (mld->tzone) { - thermal_zone_device_unregister(mld->tzone); - mld->tzone = NULL; - } + thermal_zone_device_unregister(mld->tzone); + mld->tzone = NULL; } static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld) -- cgit v1.2.3 From 4f155d262b31b9b17e0f9856bdabe0968eb4930f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:20:02 +0300 Subject: wifi: iwlwifi: mvm: fix sched scan IE sizing Scheduled scan built the probe request before iwl_mvm_scan_fits(), so oversized IEs could be copied into the fixed preq buffer before length validation. Move iwl_mvm_build_scan_probe() after the fits check. Also advertise max_sched_scan_ie_len using iwl_mvm_max_scan_ie_len() so userspace limits account for driver-inserted DS/TPC bytes. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Reviewed-by: Ilan Peer Link: https://patch.msgid.link/20260714141909.53d2722c79e7.Iebb922efa6173c92f14cd8aa8b4e7f372c0a0fb7@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 +--- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index f4f4446bef54..3e73a6195fd9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -625,9 +625,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_sched_scan_reqs = 1; hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; hw->wiphy->max_match_sets = iwl_umac_scan_get_max_profiles(mvm->fw); - /* we create the 802.11 header and zero length SSID IE. */ - hw->wiphy->max_sched_scan_ie_len = - SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; + hw->wiphy->max_sched_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm); hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS; hw->wiphy->max_sched_scan_plan_interval = U16_MAX; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 42f9d9a713b8..3831b3c27e0f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -3144,8 +3144,6 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, if (ret) return ret; - iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); - /* for 6 GHZ band only PSC channels need to be added */ for (i = 0; i < params.n_channels; i++) { struct ieee80211_channel *channel = params.channels[i]; @@ -3179,6 +3177,8 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, goto out; } + iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); + uid = iwl_mvm_build_scan_cmd(mvm, vif, &hcmd, ¶ms, type); if (uid < 0) { ret = uid; -- cgit v1.2.3 From 2c79d7a7b583050c9f58041465cb46fe3483ab5d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:20:03 +0300 Subject: wifi: iwlwifi: pcie: null RX pointers after free When iwl_pcie_tx_init() fails after RX init, nic init unwinds via iwl_pcie_rx_free(). The freed RX members stayed non-NULL on the live transport object, so later teardown or retry could touch stale RX state. Set rx_pool, global_table, rxq, and alloc_page to NULL after free to make repeated cleanup and retry paths safe. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.33e8978d8b36.Ibaedd4b0ce01405b940de7b90223b6d2c5136ffd@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c index fe263cdc2e4f..4631e11f2a96 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2003-2014, 2018-2024 Intel Corporation + * Copyright (C) 2003-2014, 2018-2024, 2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1238,11 +1238,16 @@ void iwl_pcie_rx_free(struct iwl_trans *trans) } } kfree(trans_pcie->rx_pool); + trans_pcie->rx_pool = NULL; kfree(trans_pcie->global_table); + trans_pcie->global_table = NULL; kfree(trans_pcie->rxq); + trans_pcie->rxq = NULL; - if (trans_pcie->alloc_page) + if (trans_pcie->alloc_page) { __free_pages(trans_pcie->alloc_page, trans_pcie->rx_page_order); + trans_pcie->alloc_page = NULL; + } } static void iwl_pcie_rx_move_to_allocator(struct iwl_rxq *rxq, -- cgit v1.2.3 From c1a1dc162870a5447cb0fbcf81983473744772d2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 14 Jul 2026 14:20:04 +0300 Subject: wifi: iwlwifi: mvm: d3: validate D3 resume notification payloads D3 resume notification handlers read firmware notification fields before validating that the payload contains the complete fixed structure. This causes buffer underread on malformed or truncated notifications. Move payload length validation to occur before any field access in: - iwl_mvm_parse_wowlan_info_notif: validate before reading num_mlo_link_keys - iwl_mvm_wait_d3_notif D3_END handler: validate before reading flags Assisted-by: GitHub Copilot Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260714141909.762193753434.I148991b8136cc5042fa08b5faf7b57d38aa2fb47@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index d7ceb385ae0b..3429d9a10e42 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2123,16 +2123,16 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm, struct iwl_wowlan_status_data *status, u32 len) { - if (IWL_FW_CHECK(mvm, data->num_mlo_link_keys, - "MLO is not supported, shouldn't receive MLO keys\n")) - return; - if (len < sizeof(*data)) { IWL_ERR(mvm, "Invalid WoWLAN info notification!\n"); status = NULL; return; } + if (IWL_FW_CHECK(mvm, data->num_mlo_link_keys, + "MLO is not supported, shouldn't receive MLO keys\n")) + return; + if (mvm->fast_resume) return; @@ -2942,6 +2942,11 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): { struct iwl_d3_end_notif *notif = (void *)pkt->data; + if (len < sizeof(*notif)) { + IWL_ERR(mvm, "Invalid D3 end notification size\n"); + break; + } + d3_data->d3_end_flags = __le32_to_cpu(notif->flags); d3_data->notif_received |= IWL_D3_NOTIF_D3_END_NOTIF; -- cgit v1.2.3 From f1596ba3e6b390aa0fef8466afce44efecf39d8d Mon Sep 17 00:00:00 2001 From: Jaeyeong Lee Date: Sun, 12 Jul 2026 14:27:12 +0000 Subject: io_uring/kbuf: free the replaced iovec after a successful grow The provided-buffer validation fix deferred freeing a cached iovec until validation completed. However, the deferred free uses arg->iovs. After a grow, that points to the newly allocated array. Without a grow, it points to the cached array that remains in use. This leaves the caller with a dangling iovec in both cases and can result in repeated frees. Only free org_iovs when arg->iovs actually replaced it. Fixes: cd053d788c3f ("io_uring: fix dangling iovec after provided-buffer bundle grow failure") Assisted-by: Codex:gpt-5.3-codex-spark Signed-off-by: Jaeyeong Lee Link: https://patch.msgid.link/20260712142612.188695595-iostreampy@proton.me Signed-off-by: Jens Axboe --- io_uring/kbuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index b6b969b55e12..de0129bceaba 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -328,8 +328,8 @@ 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 (arg->iovs != org_iovs && (arg->mode & KBUF_MODE_FREE)) + kfree(org_iovs); if (head == tail) req->flags |= REQ_F_BL_EMPTY; -- cgit v1.2.3 From cc609376e9a43166a2fba2aef6c5f9ea262ce722 Mon Sep 17 00:00:00 2001 From: Yi Xie Date: Tue, 14 Jul 2026 11:03:06 +0800 Subject: io_uring/fs: check unused sqe fields for unlinkat Zero check unused SQE fields addr3 and pad2 for unlinkat. They're not needed now, but could be used sometime in the future. Signed-off-by: Yi Xie Reviewed-by: Gabriel Krisman Bertazi Link: https://patch.msgid.link/20260714030306.64820-1-xieyi@kylinos.cn Signed-off-by: Jens Axboe --- io_uring/fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io_uring/fs.c b/io_uring/fs.c index d0580c754bf8..26ea841a22e7 100644 --- a/io_uring/fs.c +++ b/io_uring/fs.c @@ -110,7 +110,8 @@ int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) const char __user *fname; int err; - if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in) + if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in || + sqe->addr3 || sqe->__pad2[0]) return -EINVAL; if (unlikely(req->flags & REQ_F_FIXED_FILE)) return -EBADF; -- cgit v1.2.3 From 9f86aea992568c2b4db78c80ff9508af9e050ff7 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Fri, 10 Jul 2026 12:13:33 +0900 Subject: ASoC: fsl: imx-card: Skip sysclk reset for active DAIs in shutdown In a full-duplex setup, when one direction (playback or capture) is closed while the other is still running, imx_aif_shutdown() was unconditionally calling snd_soc_dai_set_sysclk() with rate=0 for all cpu/codec DAIs, which would disable the clock still needed by the active stream. Add snd_soc_dai_active() checks before clearing sysclk so that only truly inactive DAIs have their clocks reset. Fixes: 2260bc6ea8bd ("ASoC: imx-card: Add WM8524 support") Cc: stable@vger.kernel.org Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260710031333.3491445-1-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-card.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index a4518fefad69..43438af1e1c6 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -496,11 +496,15 @@ static void imx_aif_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai; int i; - for_each_rtd_cpu_dais(rtd, i, cpu_dai) - snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + if (!snd_soc_dai_active(cpu_dai)) + snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT); + } - for_each_rtd_codec_dais(rtd, i, codec_dai) - snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (!snd_soc_dai_active(codec_dai)) + snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN); + } } static const struct snd_soc_ops imx_aif_ops = { -- cgit v1.2.3 From d39e4e27962aa1fb5b8f0df057e1079504d369eb Mon Sep 17 00:00:00 2001 From: Antonio Ignacio Campos Ruiz Date: Mon, 13 Jul 2026 18:57:09 +0200 Subject: ASoC: amd: yc: Add DMI quirk for MSI Vector A16 HX A8WIG The internal digital microphone on the MSI Vector A16 HX A8WIG is not detected: the ACP platform devices are created, but snd_soc_acp6x_mach never binds because the machine is missing from the DMI quirk table, so no capture device shows up at all. This is the same board as the already supported "Vector A16 HX A8WHG", differing only in the trailing model code. Add the corresponding entry. Signed-off-by: Antonio Ignacio Campos Ruiz Link: https://patch.msgid.link/20260713165709.19489-1-acamposruiz@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index d6df7de70b27..af1dce9c9b8a 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -521,6 +521,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WHG"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WIG"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From 9e1dc434ca708e1134332e45918935752f4410cd Mon Sep 17 00:00:00 2001 From: Adrian Ng Ho Yin Date: Tue, 14 Jul 2026 13:37:46 +0800 Subject: firmware: stratix10-svc: handle NO_RESPONSE in async poll Define INTEL_SIP_SMC_STATUS_NO_RESPONSE (0x3) and handle it in stratix10_svc_async_poll() the same way as INTEL_SIP_SMC_STATUS_BUSY, returning -EAGAIN so callers can retry instead of treating the poll as a hard failure. When the Secure Device Manager has not yet produced a response for an asynchronous transaction, ATF is expected to return INTEL_SIP_SMC_STATUS_NO_RESPONSE. Without this handling, the service layer maps the status to -EINVAL and async clients cannot distinguish "not ready yet" from a real error. Fixes: bcb9f4f07061 ("firmware: stratix10-svc: Add support for async communication") Cc: stable@vger.kernel.org Signed-off-by: Adrian Ng Ho Yin Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 5 +++-- include/linux/firmware/intel/stratix10-smc.h | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 6306b79a14a1..c346f5fd397f 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1499,8 +1499,9 @@ int stratix10_svc_async_poll(struct stratix10_svc_chan *chan, WARN_ON_ONCE(1); } return 0; - } else if (handle->res.a0 == INTEL_SIP_SMC_STATUS_BUSY) { - dev_dbg(ctrl->dev, "async message is still in progress\n"); + } else if (handle->res.a0 == INTEL_SIP_SMC_STATUS_BUSY || + handle->res.a0 == INTEL_SIP_SMC_STATUS_NO_RESPONSE) { + dev_dbg(ctrl->dev, "async message is not ready yet\n"); return -EAGAIN; } diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 2c603a24967f..9224974fffc4 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -67,6 +67,9 @@ * INTEL_SIP_SMC_STATUS_REJECTED: * Secure monitor software reject the service client's request. * + * INTEL_SIP_SMC_STATUS_NO_RESPONSE: + * Secure monitor software has no response for the request yet. + * * INTEL_SIP_SMC_STATUS_ERROR: * There is error during the process of service request. * @@ -77,6 +80,7 @@ #define INTEL_SIP_SMC_STATUS_OK 0x0 #define INTEL_SIP_SMC_STATUS_BUSY 0x1 #define INTEL_SIP_SMC_STATUS_REJECTED 0x2 +#define INTEL_SIP_SMC_STATUS_NO_RESPONSE 0x3 #define INTEL_SIP_SMC_STATUS_ERROR 0x4 #define INTEL_SIP_SMC_RSU_ERROR 0x7 -- cgit v1.2.3 From bba85375f8751d6c949036269b529cdcb2a2ed8f Mon Sep 17 00:00:00 2001 From: Adrian Ng Ho Yin Date: Tue, 14 Jul 2026 13:37:47 +0800 Subject: firmware: stratix10-svc: fix teardown order in remove to prevent race In stratix10_svc_drv_remove(), stratix10_svc_async_exit() was called before client devices were unregistered. This created a race window where child devices could still be issuing service requests through the async channels after the async infrastructure had already been torn down. Unregister client devices before tearing down the async threads and channels to ensure all in-flight service calls drain before the underlying infrastructure is destroyed. Fixes: bcb9f4f07061 ("firmware: stratix10-svc: Add support for async communication") Cc: stable@vger.kernel.org Signed-off-by: Adrian Ng Ho Yin Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index c346f5fd397f..5e20057ee344 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -2049,12 +2049,12 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev) struct stratix10_svc_controller *ctrl = platform_get_drvdata(pdev); struct stratix10_svc *svc = ctrl->svc; + platform_device_unregister(svc->stratix10_svc_rsu); + stratix10_svc_async_exit(ctrl); of_platform_depopulate(ctrl->dev); - platform_device_unregister(svc->stratix10_svc_rsu); - for (i = 0; i < SVC_NUM_CHANNEL; i++) { if (ctrl->chans[i].task) { kthread_stop(ctrl->chans[i].task); -- cgit v1.2.3 From 71356737a7a55c76fee847563e3d33f8e6dc6b6d Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Tue, 14 Jul 2026 23:08:08 +0800 Subject: i2c: mlxbf: Fix use-after-free in mlxbf_i2c_init_resource() If devm_platform_get_and_ioremap_resource() returns an error, mlxbf_i2c_init_resource() frees tmp_res before reading tmp_res->io to get the error code. This results in a use-after-free. Save the error code before freeing tmp_res. Fixes: b5b5b32081cd ("i2c: mlxbf: I2C SMBus driver for Mellanox BlueField SoC") Signed-off-by: Xuanqiang Luo Cc: # v5.10+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260714150808.85045-1-xuanqiang.luo@linux.dev --- drivers/i2c/busses/i2c-mlxbf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 6c1cfe9ec8ac..e33512b25353 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -1051,8 +1051,10 @@ static int mlxbf_i2c_init_resource(struct platform_device *pdev, tmp_res->io = devm_platform_get_and_ioremap_resource(pdev, type, &tmp_res->params); if (IS_ERR(tmp_res->io)) { + int ret = PTR_ERR(tmp_res->io); + devm_kfree(dev, tmp_res); - return PTR_ERR(tmp_res->io); + return ret; } tmp_res->type = type; -- cgit v1.2.3 From 9fe595fad54d4ac6a402edb3f60bec859d52cea6 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Tue, 14 Jul 2026 14:57:59 +0200 Subject: selinux: fix incorrect execmem checks on overlayfs The commit fixing the overlayfs mmap() and mprotect() access checks failed to skip the execmem check in __file_map_prot_check() for the case where the "mounter check" is being performed. This check should be performed only against the credentials of the task that is calling mmap()/mprotect(), since it doesn't pertain to the file itself, but rather just gates the ability of the calling task to get an executable memory mapping in general. The purpose of the "mounter check" is to guard against using an overlayfs mount to gain file access that would otherwise be denied to the mounter. For execmem this is not relevant, as there is no further file access granted based on it (notice that the file's context is not used as the target in the check), so checking it also against the mounter credentials would be incorrect. Fix this by passing a boolean to [__]file_map_prot_check() and selinux_mmap_file_common() that indicates if we are doing the "mounter check" and skiping the execmem check in that case. Since this boolean also indicates if we use current_cred() or the mounter cred as the subject, also remove the "cred" argument from these functions and determine it based on the boolean and the file struct. Cc: stable@vger.kernel.org Fixes: 82544d36b172 ("selinux: fix overlayfs mmap() and mprotect() access checks") Signed-off-by: Ondrej Mosnacek Reviewed-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/hooks.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 70a3388c047d..8d6945edae7a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3969,9 +3969,9 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, static int default_noexec __ro_after_init; -static int __file_map_prot_check(const struct cred *cred, - const struct file *file, unsigned long prot, - bool shared, bool bf_user_file) +static int __file_map_prot_check(const struct file *file, unsigned long prot, + bool shared, bool mounter_check, + bool bf_user_file) { struct inode *inode = NULL; bool prot_exec = prot & PROT_EXEC; @@ -3984,10 +3984,10 @@ static int __file_map_prot_check(const struct cred *cred, inode = file_inode(file); } - if (default_noexec && prot_exec && + if (!mounter_check && default_noexec && prot_exec && (!file || IS_PRIVATE(inode) || (!shared && prot_write))) { int rc; - u32 sid = cred_sid(cred); + u32 sid = current_sid(); /* * We are making executable an anonymous mapping or a private @@ -4000,6 +4000,8 @@ static int __file_map_prot_check(const struct cred *cred, } if (file) { + const struct cred *cred = mounter_check ? + file->f_cred : current_cred(); /* "read" always possible, "write" only if shared */ u32 av = FILE__READ; if (shared && prot_write) @@ -4013,11 +4015,11 @@ static int __file_map_prot_check(const struct cred *cred, return 0; } -static inline int file_map_prot_check(const struct cred *cred, - const struct file *file, - unsigned long prot, bool shared) +static inline int file_map_prot_check(const struct file *file, + unsigned long prot, bool shared, + bool mounter_check) { - return __file_map_prot_check(cred, file, prot, shared, false); + return __file_map_prot_check(file, prot, shared, mounter_check, false); } static int selinux_mmap_addr(unsigned long addr) @@ -4033,12 +4035,14 @@ static int selinux_mmap_addr(unsigned long addr) return rc; } -static int selinux_mmap_file_common(const struct cred *cred, struct file *file, - unsigned long prot, bool shared) +static int selinux_mmap_file_common(struct file *file, unsigned long prot, + bool shared, bool mounter_check) { if (file) { int rc; struct common_audit_data ad; + const struct cred *cred = mounter_check ? + file->f_cred : current_cred(); ad.type = LSM_AUDIT_DATA_FILE; ad.u.file = file; @@ -4047,15 +4051,16 @@ static int selinux_mmap_file_common(const struct cred *cred, struct file *file, return rc; } - return file_map_prot_check(cred, file, prot, shared); + return file_map_prot_check(file, prot, shared, mounter_check); } static int selinux_mmap_file(struct file *file, unsigned long reqprot __always_unused, unsigned long prot, unsigned long flags) { - return selinux_mmap_file_common(current_cred(), file, prot, - (flags & MAP_TYPE) == MAP_SHARED); + return selinux_mmap_file_common(file, prot, + (flags & MAP_TYPE) == MAP_SHARED, + false); } /** @@ -4087,8 +4092,9 @@ static int selinux_mmap_backing_file(struct vm_area_struct *vma, if (vma->vm_flags & VM_EXEC) prot |= PROT_EXEC; - return selinux_mmap_file_common(backing_file->f_cred, backing_file, - prot, vma->vm_flags & VM_SHARED); + return selinux_mmap_file_common(backing_file, prot, + vma->vm_flags & VM_SHARED, + true); } static int selinux_file_mprotect(struct vm_area_struct *vma, @@ -4149,11 +4155,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, } } - rc = __file_map_prot_check(cred, file, prot, shared, backing_file); + rc = __file_map_prot_check(file, prot, shared, false, backing_file); if (rc) return rc; if (backing_file) { - rc = file_map_prot_check(file->f_cred, file, prot, shared); + rc = file_map_prot_check(file, prot, shared, true); if (rc) return rc; } -- cgit v1.2.3 From deb35336b5bfed5db9231b5348bc1514db930797 Mon Sep 17 00:00:00 2001 From: Roman Vivchar Date: Thu, 9 Jul 2026 16:31:29 +0300 Subject: i2c: mediatek: fix WRRD for SoCs without auto_restart option MediaTek mt65xx family SoCs have no auto restart, however, they still support the WRRD mode in the hardware. Because auto_restart is set to 0, the WRRD mode will be never enabled, leading to read errors. Fix this by removing auto_restart check from the WRRD enable path. Fixes: b49218365280 ("i2c: mediatek: fix potential incorrect use of I2C_MASTER_WRRD") Signed-off-by: Roman Vivchar Cc: # v6.18+ Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260709-6572-6595-i2c-v2-1-b2fb8510d1d3@protonmail.com --- drivers/i2c/busses/i2c-mt65xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 126040ca05f1..307925fb78e3 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -1258,7 +1258,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, i2c->auto_restart = i2c->dev_comp->auto_restart; /* checking if we can skip restart and optimize using WRRD mode */ - if (i2c->auto_restart && num == 2) { + if (num == 2) { if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) && msgs[0].addr == msgs[1].addr) { i2c->auto_restart = 0; -- cgit v1.2.3 From 261c1fe3327ad24508f54552c6366e3e4db82c15 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Mon, 13 Jul 2026 19:30:28 +0200 Subject: accel/amdxdna: reject user command submission without a command BO amdxdna_drm_submit_execbuf() passes the user-supplied command BO handle straight into amdxdna_cmd_submit() with drv_cmd == NULL. When the handle is AMDXDNA_INVALID_BO_HANDLE (0), the block that fetches job->cmd_bo is skipped, leaving it NULL, and no check rejects it on the user path (the !job->cmd_bo guard lives inside the != INVALID branch). The job is then armed and pushed to the DRM scheduler. aie2_sched_job_run() takes the drv_cmd == NULL path and calls amdxdna_cmd_set_state(job->cmd_bo) -> amdxdna_gem_vmap(NULL) -> to_gobj(NULL)->dev, a NULL pointer dereference in the drm_sched worker. A process with access to the accel node on a system with a probed AMD NPU can trigger a kernel oops with a single AMDXDNA_EXEC_CMD ioctl (cmd_handles = 0). Only internal driver commands (SYNC_DEBUG_BO / ATTACH_DEBUG_BO) legitimately pass AMDXDNA_INVALID_BO_HANDLE, and they always set drv_cmd. Reject the invalid handle for user submissions (drv_cmd == NULL) at the submit choke point so every user path is covered. Fixes: aac243092b70 ("accel/amdxdna: Add command execution") Cc: stable@vger.kernel.org Found by 0sec automated security-research tooling (https://0sec.ai). Assisted-by: 0sec:claude-opus-4-8 Signed-off-by: Doruk Tan Ozturk Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260713173030.87541-2-doruk@0sec.ai --- drivers/accel/amdxdna/amdxdna_ctx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 8f8df9d04ec5..a5c8c2c4de6d 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -603,6 +603,16 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, ret = -EINVAL; goto free_job; } + } else if (!drv_cmd) { + /* + * Only internal driver commands (drv_cmd != NULL) may omit a + * command BO. A user command submission with the invalid handle + * would leave job->cmd_bo NULL and later fault when the scheduler + * dereferences it in amdxdna_cmd_set_state(). + */ + XDNA_DBG(xdna, "Command BO handle required for user submission"); + ret = -EINVAL; + goto free_job; } ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt); -- cgit v1.2.3 From 38953513d7313992676d4136cd425cdb70c6278e Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Mon, 13 Jul 2026 19:30:29 +0200 Subject: accel/amdxdna: reject command submission on devices without a submit op amdxdna_cmd_submit() calls xdna->dev_info->ops->cmd_submit() unconditionally, but only aie2_dev_ops defines that callback. aie4_vf_ops (the AIE4 SR-IOV virtual function) does not, so a user AMDXDNA_EXEC_CMD ioctl on an AIE4 device reaches a NULL function-pointer call and oopses the kernel. AIE4 submits work through a mapped user queue and doorbell, not this ioctl path. Reject the submission early with -EOPNOTSUPP when the device provides no cmd_submit op, so the shared EXEC ioctl is a clean no-op on such devices. Fixes: aac243092b70 ("accel/amdxdna: Add command execution") Cc: stable@vger.kernel.org Found by 0sec automated security-research tooling (https://0sec.ai). Assisted-by: 0sec:claude-opus-4-8 Signed-off-by: Doruk Tan Ozturk Reviewed-by: Lizhi Hou Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260713173030.87541-3-doruk@0sec.ai --- drivers/accel/amdxdna/amdxdna_ctx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index a5c8c2c4de6d..bdbd3db12a6c 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -590,6 +590,10 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, int ret, idx; XDNA_DBG(xdna, "Command BO hdl %d, Arg BO count %d", cmd_bo_hdl, arg_bo_cnt); + + if (!xdna->dev_info->ops->cmd_submit) + return -EOPNOTSUPP; + job = kzalloc_flex(*job, bos, arg_bo_cnt); if (!job) return -ENOMEM; -- cgit v1.2.3 From e4bf6eb4c7b61db1cf24487e14e6ae8755e61e3d Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 23 Jun 2026 22:40:57 +0200 Subject: arch/riscv: vdso: remove CFI landing pad from rt_sigreturn When CONFIG_RISCV_USER_CFI is enabled, the CFI version of the vDSO, has a CFI landing pad instruction at the start of __vdso_rt_sigreturn. This breaks libgcc's unwinding code which matches on the first two instructions. Other unwinders that rely on similar instruction matching may also be affected. Since __vdso_rt_sigreturn is reached as part of signal-return handling rather than via an indirect call/jump from userspace, it does not need a CFI landing pad. Remove it and restore the instruction sequence expected by existing unwinding code. This matches what was done on arm64 in commit 9a964285572b ("arm64: vdso: Don't prefix sigreturn trampoline with a BTI C instruction") for a similar issue. Cc: stable@vger.kernel.org Fixes: 37f57bd3faea ("arch/riscv: compile vdso with landing pad and shadow stack note") Co-authored-by: Joel Stanley Signed-off-by: Aurelien Jarno Signed-off-by: Joel Stanley Link: https://patch.msgid.link/20260623204058.498120-1-aurelien@aurel32.net [pjw@kernel.org: fixed comment style] Signed-off-by: Paul Walmsley --- arch/riscv/kernel/vdso/rt_sigreturn.S | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/vdso/rt_sigreturn.S b/arch/riscv/kernel/vdso/rt_sigreturn.S index e82987dc3739..d6f96b1abe40 100644 --- a/arch/riscv/kernel/vdso/rt_sigreturn.S +++ b/arch/riscv/kernel/vdso/rt_sigreturn.S @@ -7,11 +7,19 @@ #include #include +/* + * WARNING: Do NOT add a CFI landing pad at the start of this function. + * Unwinders such as libgcc identify the sigreturn trampoline by matching the + * instruction sequence. Adding a landing pad here would break unwinding from + * signal handlers. + * + * This trampoline is used only for signal return and not via an indirect + * call/jump from userspace, so adding CFI landing pad is unnecessary. + */ .text SYM_FUNC_START(__vdso_rt_sigreturn) .cfi_startproc .cfi_signal_frame - vdso_lpad li a7, __NR_rt_sigreturn ecall .cfi_endproc -- cgit v1.2.3 From 25957f7c3dac3265332d766b71233e3622f17e14 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 3 Jun 2026 21:33:09 -0700 Subject: 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", }, {}, }; -- cgit v1.2.3 From c1c1ffa490fc33591e90852ed0d38804dd20bc36 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Fri, 5 Jun 2026 18:13:29 +0530 Subject: 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(); } /* -- cgit v1.2.3 From d610d3ab18197d87618da11ec5fe8b3cebf32208 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Mon, 15 Jun 2026 16:37:26 -0700 Subject: 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); -- cgit v1.2.3 From bd83c98b988d2c560531084e296dbfb530aff829 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 14 Jun 2026 16:23:56 +0200 Subject: 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 @@ retry: 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; } -- cgit v1.2.3 From e4de1b9cb3b5c981e4fe9bca253a7fb9161f5acd Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Sun, 14 Jun 2026 23:04:37 +0530 Subject: 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; } } -- cgit v1.2.3 From a2c02aa0c6ca3ec9fab6f1c99912a440c7b8bfdb Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Fri, 19 Jun 2026 14:08:28 +0200 Subject: 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; } -- cgit v1.2.3 From b8de511f0e80a2ab9d23df73d147423968ebf102 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 14 Jul 2026 14:21:20 +0200 Subject: 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"); -- cgit v1.2.3 From e2e08effef2cd5d5d27b44d6239718ada614eb62 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 14 Jul 2026 15:53:33 +0200 Subject: 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"); -- cgit v1.2.3 From d7d2adcd022baade5cab65ca492ce63421ce3a6e Mon Sep 17 00:00:00 2001 From: Huiwen He Date: Tue, 14 Jul 2026 17:38:34 -0500 Subject: 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. -- cgit v1.2.3 From 5b7b3b6595ee77d01c7463757baed114786094dd Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 2 Jul 2026 14:48:15 -0700 Subject: drm/ttm: Account for NULL and handle pages in ttm_pool_backup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pages in ttm_pool_backup can be NULL or backup handles (ttm_backup_page_ptr_is_handle()), neither of which can be passed to set_pages_array_wb() or freed. Add a dedicated WB pass before the dma/purge loop that walks allocations using the same i += num_pages stride, skipping NULL and handle entries, and calls set_pages_array_wb() once per contiguous run of real pages. Apply the same NULL/handle guard to the dma/purge loop. Fixes the following oops: Oops: general protection fault, kernel NULL pointer dereference 0x0: 0000 [#1] SMP NOPTI RIP: 0010:__cpa_process_fault+0xf8/0x770 RSP: 0018:ffffc90000a87718 EFLAGS: 00010287 RAX: 0000000000000000 RBX: ffffc90000a87868 RCX: 0000000000000000 RDX: 0000000000001000 RSI: 0005088000000000 RDI: ffffffff827c5f34 RBP: 0005088000000000 R08: ffffc90000a877cb R09: ffffc90000a877d0 R10: 0000000000000000 R11: 000000000000001b R12: 000ffffffffff000 R13: ffffc90000a87868 R14: ffffc90000a87868 R15: ffff88815b882ae0 FS: 0000000000000000(0000) GS:ffff8884ec840000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f930b844000 CR3: 000000000262e003 CR4: 0000000008f70ef0 PKRU: 55555554 Call Trace: __change_page_attr_set_clr+0x989/0xe90 ? __purge_vmap_area_lazy+0x6c/0x3a0 ? _vm_unmap_aliases+0x250/0x2a0 set_pages_array_wb+0x7f/0x120 ttm_pool_backup+0x4c9/0x5b0 [ttm] ? dma_resv_wait_timeout+0x3b/0xf0 ttm_tt_backup+0x32/0x60 [ttm] ttm_bo_shrink+0x66/0x110 [ttm] xe_bo_shrink_purge+0x12b/0x1b0 [xe] xe_bo_shrink+0xbb/0x270 [xe] __xe_shrinker_walk+0xf7/0x160 [xe] xe_shrinker_walk+0x9d/0xc0 [xe] xe_shrinker_scan+0x11f/0x210 [xe] do_shrink_slab+0x13b/0x270 shrink_slab+0xf1/0x400 shrink_node+0x352/0x8a0 balance_pgdat+0x32c/0x700 kswapd+0x205/0x2f0 ? __pfx_autoremove_wake_function+0x10/0x10 ? __pfx_kswapd+0x10/0x10 kthread+0xd1/0x110 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x1b1/0x200 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Cc: Christian Koenig Cc: Huang Rui Cc: Matthew Auld Cc: Matthew Brost Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: Thomas Hellström Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org Fixes: b63d715b8090 ("drm/ttm/pool, drm/ttm/tt: Provide a helper to shrink pages") Cc: stable@vger.kernel.org Assisted-by: GitHub_Copilot:claude-opus-4.8 Signed-off-by: Matthew Brost Reviewed-by: Thomas Hellström Link: https://patch.msgid.link/20260702214815.4009271-1-matthew.brost@intel.com --- drivers/gpu/drm/ttm/ttm_pool.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 278bbe7a11ad..46983e7de7a3 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -1051,9 +1051,31 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, return -EBUSY; #ifdef CONFIG_X86 - /* Anything returned to the system needs to be cached. */ - if (tt->caching != ttm_cached) - set_pages_array_wb(tt->pages, tt->num_pages); + /* Anything returned to the system needs to be cached. Walk allocations + * skipping NULL pages and issue set_pages_array_wb() per contiguous run. + */ + if (tt->caching != ttm_cached) { + pgoff_t run_start = 0, run_count = 0; + + for (i = 0; i < tt->num_pages; i += num_pages) { + page = tt->pages[i]; + if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) { + if (run_count) { + set_pages_array_wb(&tt->pages[run_start], + run_count); + run_count = 0; + } + num_pages = 1; + continue; + } + num_pages = 1UL << ttm_pool_page_order(pool, page); + if (!run_count) + run_start = i; + run_count += num_pages; + } + if (run_count) + set_pages_array_wb(&tt->pages[run_start], run_count); + } #endif if (tt->dma_address || flags->purge) { @@ -1061,7 +1083,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, unsigned int order; page = tt->pages[i]; - if (unlikely(!page)) { + if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) { num_pages = 1; continue; } @@ -1104,6 +1126,10 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (unlikely(!page)) continue; + /* Already-handled entry from a previous attempt. */ + if (unlikely(ttm_backup_page_ptr_is_handle(page))) + continue; + ttm_pool_split_for_swap(pool, page); shandle = ttm_backup_backup_page(backup, page, flags->writeback, i, -- cgit v1.2.3 From 728d34ebf91c0e80b63a66b237dd0dfc5a8e5b4d Mon Sep 17 00:00:00 2001 From: Marcel Kłos Date: Tue, 14 Jul 2026 21:36:26 +0200 Subject: ALSA: hda/realtek: Add quirk for HP EliteBook 830 G8 (8AB8) to enable mute LEDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sound and microphone mute LEDs do not function on this newer revision of the board (8AB8) while they do on the older 880D models. I have verified this on another laptop which was manufactured before the one with the issue. Added the ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED from a G9 model, which uses the same codec, to make it work. Tested on kernel version 7.1.3 on the aforementioned newer revision notebook. Signed-off-by: Marcel Kłos Link: https://patch.msgid.link/4dab5622-9100-4730-8c99-b58da939549b@marmak.net.pl 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 443bc92c5e4b..454d8aa4f548 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7172,6 +7172,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ab8, "HP EliteBook 830 G8 Notebook PC (MB 8AB8)", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ab9, "HP EliteBook 840 G8 (MB 8AB8)", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), -- cgit v1.2.3 From 914c85fad93cd7daad30e5fb2e5bc7132ea25524 Mon Sep 17 00:00:00 2001 From: Oliver Ohrt Date: Wed, 15 Jul 2026 00:04:09 -0700 Subject: ALSA: hda/realtek: Fix speakers on Alienware x16 R2 The Alienware x16 R2 has two pairs of speakers, but the BIOS marks pin 0x17 as unused, so only the pin 0x14 pair plays and audio is very quiet/dull. Apply ALC289_FIXUP_DUAL_SPK like on other Dell machines to set up pin 0x17 and route it to DAC1. Tested on my x16 R2 with kernel 6.18.38, and now all speakers play at full volume. Signed-off-by: Oliver Ohrt Link: https://patch.msgid.link/20260715070409.42696-1-oliver@theohrts.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 454d8aa4f548..e3958310af89 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6932,6 +6932,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), SND_PCI_QUIRK(0x1028, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x1028, 0x0c90, "Alienware x16 R2", ALC289_FIXUP_DUAL_SPK), SND_PCI_QUIRK(0x1028, 0x0c94, "Dell Polaris 3 metal", ALC295_FIXUP_DELL_TAS2781_I2C), SND_PCI_QUIRK(0x1028, 0x0c96, "Dell Polaris 2in1", ALC295_FIXUP_DELL_TAS2781_I2C), SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), -- cgit v1.2.3 From d024a0a7879e6f37c0152aacf6d8e37b214a1738 Mon Sep 17 00:00:00 2001 From: Xie Bo Date: Wed, 15 Jul 2026 10:03:59 +0800 Subject: RISC-V: KVM: Serialize virtual interrupt pending state updates KVM RISC-V tracks guest local interrupt state with two bitmaps: - irqs_pending: interrupts that should be visible to the guest - irqs_pending_mask: interrupts whose pending state changed The current code updates those bitmaps with independent atomic bitops and assumes a multiple-producer, single-consumer protocol. That model does not actually hold. kvm_riscv_vcpu_sync_interrupts() is not a pure consumer. When the guest changes guest-visible HVIP state, sync_interrupts() writes both irqs_pending and irqs_pending_mask to reflect the new guest state back into KVM state. As a result, irqs_pending and irqs_pending_mask form a single logical state transition, but they are not updated atomically as a pair. This allows a race where a newly injected interrupt is lost. For example: CPU0 CPU1 ---- ---- kvm_riscv_vcpu_set_interrupt(VS_SOFT) set_bit(VS_SOFT, irqs_pending) kvm_riscv_vcpu_sync_interrupts() sees guest-cleared HVIP.VSSIP sets irqs_pending_mask clear_bit(IRQ_VS_SOFT, irqs_pending) set_bit(VS_SOFT, irqs_pending_mask) kvm_vcpu_kick() After that interleaving, a later flush can update HVIP without VSSIP even though a new virtual interrupt was injected. In practice, the guest can remain blocked in WFI with work pending. The same pending/mask protocol is shared by VS soft interrupts, PMU overflow delivery, and AIA high interrupt synchronization, so the race is not limited to one interrupt source. Fix this by serializing all updates to irqs_pending and irqs_pending_mask with a per-vCPU raw spinlock. This keeps the pending bit and the dirty mask as one state transition across: - set/unset interrupt - guest HVIP sync - interrupt flush to guest CSR state - vCPU reset - AIA CSR writes that clear dirty state Use non-atomic bitmap operations while holding the lock. Hold the lock across the AIA sync, flush, and pending checks as well, so both bitmap words share the same serialization domain. This intentionally replaces the existing lockless protocol instead of trying to repair it with additional barriers. The problem is not memory ordering on a single field; it is that two separate bitmaps encode one shared state machine while both producers and sync paths can modify them. A per-vCPU raw spinlock keeps the fix small, local, and suitable for backporting. Fixes: cce69aff689e ("RISC-V: KVM: Implement VCPU interrupts and requests handling") Cc: stable@vger.kernel.org Signed-off-by: Xie Bo Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260715020359.1521354-2-xb@ultrarisc.com Signed-off-by: Anup Patel --- arch/riscv/include/asm/kvm_host.h | 10 +++--- arch/riscv/kvm/aia.c | 35 ++++++++++++++++---- arch/riscv/kvm/vcpu.c | 68 +++++++++++++++++++++++++++------------ arch/riscv/kvm/vcpu_onereg.c | 8 +++-- 4 files changed, 87 insertions(+), 34 deletions(-) diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 60017ceec9d2..e2d5808169e4 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -209,13 +209,13 @@ struct kvm_vcpu_arch { /* * VCPU interrupts * - * We have a lockless approach for tracking pending VCPU interrupts - * implemented using atomic bitops. The irqs_pending bitmap represent - * pending interrupts whereas irqs_pending_mask represent bits changed - * in irqs_pending. Our approach is modeled around multiple producer - * and single consumer problem where the consumer is the VCPU itself. + * The irqs_pending bitmap represents pending interrupts whereas + * irqs_pending_mask represents bits changed in irqs_pending. Updates + * to these bitmaps are serialized so vcpu interrupt sync/flush cannot + * drop a newly injected interrupt while syncing guest-visible HVIP. */ #define KVM_RISCV_VCPU_NR_IRQS 64 + raw_spinlock_t irqs_pending_lock; DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS); DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index bafb009c5ce5..9a653b4ad40a 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -53,12 +53,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu) struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; unsigned long mask, val; + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (!kvm_riscv_aia_available()) return; - if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) { - mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0); - val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask; + mask = vcpu->arch.irqs_pending_mask[1]; + if (mask) { + vcpu->arch.irqs_pending_mask[1] = 0; + val = vcpu->arch.irqs_pending[1] & mask; csr->hviph &= ~mask; csr->hviph |= val; @@ -69,6 +72,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; + lockdep_assert_held(&vcpu->arch.irqs_pending_lock); + if (kvm_riscv_aia_available()) csr->vsieh = ncsr_read(CSR_VSIEH); } @@ -77,13 +82,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu) bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) { unsigned long seip; +#ifdef CONFIG_32BIT + unsigned long flags; + bool pending; +#endif if (!kvm_riscv_aia_available()) return false; #ifdef CONFIG_32BIT - if (READ_ONCE(vcpu->arch.irqs_pending[1]) & - (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask))) + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + pending = vcpu->arch.irqs_pending[1] & + (vcpu->arch.aia_context.guest_csr.vsieh & + upper_32_bits(mask)); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); + + if (pending) return true; #endif @@ -207,6 +221,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, { struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long); +#ifdef CONFIG_32BIT + unsigned long flags; +#endif if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA)) return -ENOENT; @@ -219,8 +236,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, ((unsigned long *)csr)[reg_num] = val; #ifdef CONFIG_32BIT - if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0); + if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) { + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + vcpu->arch.irqs_pending_mask[1] = 0; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, + flags); + } #endif } diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index cf6e231e76e2..977e36ab83d3 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -80,6 +80,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu *vcpu, static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) { + unsigned long flags; bool loaded; /** @@ -104,8 +105,10 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset) kvm_riscv_vcpu_aia_reset(vcpu); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS); bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); kvm_riscv_vcpu_pmu_reset(vcpu); @@ -151,6 +154,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) /* Setup VCPU hfence queue */ spin_lock_init(&vcpu->arch.hfence_lock); + raw_spin_lock_init(&vcpu->arch.irqs_pending_lock); spin_lock_init(&vcpu->arch.reset_state.lock); @@ -352,10 +356,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu) { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; unsigned long mask, val; + unsigned long flags; - if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) { - mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0); - val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask; + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + + mask = vcpu->arch.irqs_pending_mask[0]; + if (mask) { + vcpu->arch.irqs_pending_mask[0] = 0; + val = vcpu->arch.irqs_pending[0] & mask; csr->hvip &= ~mask; csr->hvip |= val; @@ -363,11 +371,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu) /* Flush AIA high interrupts */ kvm_riscv_vcpu_aia_flush_interrupts(vcpu); + + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); } void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) { unsigned long hvip; + unsigned long flags; struct kvm_vcpu_arch *v = &vcpu->arch; struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; @@ -376,34 +387,41 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu) /* Sync-up HVIP.VSSIP bit changes does by Guest */ hvip = ncsr_read(CSR_HVIP); + + raw_spin_lock_irqsave(&v->irqs_pending_lock, flags); + if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) { if (hvip & (1UL << IRQ_VS_SOFT)) { - if (!test_and_set_bit(IRQ_VS_SOFT, - v->irqs_pending_mask)) - set_bit(IRQ_VS_SOFT, v->irqs_pending); + if (!__test_and_set_bit(IRQ_VS_SOFT, + v->irqs_pending_mask)) + __set_bit(IRQ_VS_SOFT, v->irqs_pending); } else { - if (!test_and_set_bit(IRQ_VS_SOFT, - v->irqs_pending_mask)) - clear_bit(IRQ_VS_SOFT, v->irqs_pending); + if (!__test_and_set_bit(IRQ_VS_SOFT, + v->irqs_pending_mask)) + __clear_bit(IRQ_VS_SOFT, v->irqs_pending); } } /* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */ if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) { if (!(hvip & (1UL << IRQ_PMU_OVF)) && - !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask)) - clear_bit(IRQ_PMU_OVF, v->irqs_pending); + !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask)) + __clear_bit(IRQ_PMU_OVF, v->irqs_pending); } /* Sync-up AIA high interrupts */ kvm_riscv_vcpu_aia_sync_interrupts(vcpu); + raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags); + /* Sync-up timer CSRs */ kvm_riscv_vcpu_timer_sync(vcpu); } int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) { + unsigned long flags; + /* * We only allow VS-mode software, timer, and external * interrupts when irq is one of the local interrupts @@ -416,9 +434,10 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) irq != IRQ_PMU_OVF) return -EINVAL; - set_bit(irq, vcpu->arch.irqs_pending); - smp_mb__before_atomic(); - set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + __set_bit(irq, vcpu->arch.irqs_pending); + __set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); kvm_vcpu_kick(vcpu); @@ -427,6 +446,8 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) { + unsigned long flags; + /* * We only allow VS-mode software, timer, counter overflow and external * interrupts when irq is one of the local interrupts @@ -439,26 +460,33 @@ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq) irq != IRQ_PMU_OVF) return -EINVAL; - clear_bit(irq, vcpu->arch.irqs_pending); - smp_mb__before_atomic(); - set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + __clear_bit(irq, vcpu->arch.irqs_pending); + __set_bit(irq, vcpu->arch.irqs_pending_mask); + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); return 0; } bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask) { + unsigned long flags; unsigned long ie; + bool ret; + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK) << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask; ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK & (unsigned long)mask; - if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie) - return true; + ret = vcpu->arch.irqs_pending[0] & ie; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); /* Check AIA high interrupts */ - return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); + if (!ret) + ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask); + + return ret; } void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu) diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index 61988382570f..99b9107b1ac1 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -286,6 +286,7 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu, { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long); + unsigned long flags; if (reg_num >= regs_max) return -ENOENT; @@ -299,8 +300,11 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu, ((unsigned long *)csr)[reg_num] = reg_val; - if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0); + if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) { + raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags); + vcpu->arch.irqs_pending_mask[0] = 0; + raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags); + } return 0; } -- cgit v1.2.3 From fe3ff273767ef22fe8a7cb3816f264927c190e50 Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:56 +0200 Subject: selftests/bpf: Ensure UDP sockets are bound Update sockmap_basic tests to bind sockets before they are used. This accommodates the recent change in sockmap that rejects unbound UDP sockets. Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-1-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi --- tools/testing/selftests/bpf/prog_tests/sockmap_basic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c index cb3229711f93..2d22a9058a8e 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c @@ -853,7 +853,7 @@ static void test_sockmap_many_socket(void) return; } - udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); if (udp < 0) { close(dgram); close(tcp); @@ -922,7 +922,7 @@ static void test_sockmap_many_maps(void) return; } - udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); if (udp < 0) { close(dgram); close(tcp); @@ -993,7 +993,7 @@ static void test_sockmap_same_sock(void) return; } - udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK); if (udp < 0) { close(dgram); close(tcp); -- cgit v1.2.3 From 66efd3368ae10d05e08fbe6425b50fdec7186ac7 Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:57 +0200 Subject: bpf, sockmap: Reject unhashed UDP sockets on sockmap update UDP sockets get SOCK_RCU_FREE set when (auto-)bound. This means sk_is_refcounted(unbound) = true, while sk_is_refcounted(bound) = false. Because sockmap accepts unbound UDP sockets, a BPF program can increment a socket's refcount via lookup. If the socket is subsequently bound, the transition from unbound to bound causes bpf_sk_release() to skip the decrement of the refcount, causing a memory leak. unreferenced object 0xffff88810bc2eb40 (size 1984): comm "test_progs", pid 2451, jiffies 4295320596 hex dump (first 32 bytes): 7f 00 00 01 7f 00 00 01 d2 04 1b b7 04 d2 00 00 ................ 02 00 01 40 00 00 00 00 00 00 00 00 00 00 00 00 ...@............ backtrace (crc bdee079d): kmem_cache_alloc_noprof+0x557/0x660 sk_prot_alloc+0x69/0x240 sk_alloc+0x30/0x460 inet_create+0x2ce/0xf80 __sock_create+0x25b/0x5c0 __sys_socket+0x119/0x1d0 __x64_sys_socket+0x72/0xd0 do_syscall_64+0xa1/0x5f0 entry_SYSCALL_64_after_hwframe+0x76/0x7e Instead of special-casing for refcounted sockets, reject unhashed UDP sockets during sockmap updates, as there is no benefit to supporting those. This effectively reverts the commit under Fixes, with two exceptions: 1. sock_map_sk_state_allowed() maintains a fall-through `return true`. 2. In the spirit of commit b8b8315e39ff ("bpf, sockmap: Remove unhash handler for BPF sockmap usage"), the proto::unhash BPF handler is not reintroduced. Historical note: this issue is related to commit 67312adc96b5 ("bpf: reject unhashed sockets in bpf_sk_assign"). Fixes: 0c48eefae712 ("sock_map: Lift socket state restriction for datagram sockets") Suggested-by: Kuniyuki Iwashima Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Reviewed-by: John Fastabend Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-2-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi --- net/core/sock_map.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/sock_map.c b/net/core/sock_map.c index c60ba6d292f9..9efbd8ca7db8 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -542,6 +542,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk) { if (sk_is_tcp(sk)) return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN); + if (sk_is_udp(sk)) + return sk_hashed(sk); if (sk_is_stream_unix(sk)) return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED; if (sk_is_vsock(sk) && -- cgit v1.2.3 From 30581eda4a07ff15db623612cac578e81869e96f Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:58 +0200 Subject: selftests/bpf: Adapt sockmap update error handling Update sockmap_listen to accommodate the recent change in sockmap that rejects unbound UDP sockets. TCP: Reject unbound and bound (unless established or listening). UDP: Accept only bound sockets. While at it, migrate to ASSERT_* and enforce reverse xmas tree. Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-3-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi --- .../selftests/bpf/prog_tests/sockmap_listen.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index cc0c68bab907..1c96a3cf4b97 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, int family, int sotype, int mapfd) { u32 key = 0; - u64 value; int err, s; + u64 value; s = xsocket(family, sotype, 0); if (s == -1) @@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused, errno = 0; value = s; err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); - if (sotype == SOCK_STREAM) { - if (!err || errno != EOPNOTSUPP) - FAIL_ERRNO("map_update: expected EOPNOTSUPP"); - } else if (err) - FAIL_ERRNO("map_update: expected success"); + ASSERT_ERR(err, "map_update"); + ASSERT_EQ(errno, EOPNOTSUPP, "errno"); xclose(s); } @@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, struct sockaddr_storage addr; socklen_t len = 0; u32 key = 0; - u64 value; int err, s; + u64 value; init_addr_loopback(family, &addr, &len); @@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused, errno = 0; value = s; err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST); - if (!err || errno != EOPNOTSUPP) - FAIL_ERRNO("map_update: expected EOPNOTSUPP"); + if (sotype == SOCK_STREAM) { + ASSERT_ERR(err, "map_update"); + ASSERT_EQ(errno, EOPNOTSUPP, "errno"); + } else { + ASSERT_OK(err, "map_update"); + } close: xclose(s); } @@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map, /* insert */ TEST(test_insert_invalid), TEST(test_insert_opened), - TEST(test_insert_bound, SOCK_STREAM), + TEST(test_insert_bound), TEST(test_insert), /* delete */ TEST(test_delete_after_insert), -- cgit v1.2.3 From 203b06932777b9ad5085319389dea566f5c2ca63 Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Tue, 7 Jul 2026 06:23:59 +0200 Subject: selftests/bpf: Fail unbound UDP on sockmap update sockmap now rejects unbound UDP sockets. Adjust test_maps. While at it, check socket()'s return value. This effectively reverts commit c39aa2159974 ("bpf, selftests: Fix test_maps now that sockmap supports UDP"). Signed-off-by: Michal Luczaj Reviewed-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/20260707-sockmap-lookup-udp-leak-v4-4-f878346f27ab@rbox.co Signed-off-by: Kumar Kartikeya Dwivedi --- tools/testing/selftests/bpf/test_maps.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index c32da7bd8be2..6a2641ee7897 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -759,16 +759,15 @@ static void test_sockmap(unsigned int tasks, void *data) goto out_sockmap; } - /* Test update with unsupported UDP socket */ + /* Test update with unsupported unbound UDP socket */ udp = socket(AF_INET, SOCK_DGRAM, 0); - i = 0; - err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY); - if (err) { - printf("Failed socket update SOCK_DGRAM '%i:%i'\n", - i, udp); + CHECK(udp < 0, "socket(AF_INET, SOCK_DGRAM)", "errno:%d\n", errno); + err = bpf_map_update_elem(fd, &(int){0}, &udp, BPF_ANY); + close(udp); + if (!err) { + printf("Unexpectedly succeeded unbound UDP update '0:%i'\n", udp); goto out_sockmap; } - close(udp); /* Test update without programs */ for (i = 0; i < 6; i++) { -- cgit v1.2.3 From 899160e2774d9952e9f2770b38f701ff1906c0b2 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:11 +0800 Subject: 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); -- cgit v1.2.3 From 2439becd91bad6883b135044f85f83a0538b96a6 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:12 +0800 Subject: 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; -- cgit v1.2.3 From 5adc14cd4b905629d5b9163b3a416dcab24c7ce2 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:13 +0800 Subject: 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: -- cgit v1.2.3 From 1db87818bde3d2295613660879378b43a70d31f8 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:14 +0800 Subject: 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); -- cgit v1.2.3 From 9d87eaf985cef9581b6ed99b461b38e8cd666480 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:15 +0800 Subject: 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 -- cgit v1.2.3 From 8da5930144712412d85e7f868693d96ec5c2018c Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:16 +0800 Subject: 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)) { -- cgit v1.2.3 From 6aa00a43bbd3d994558a55586351757cebbff236 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:17 +0800 Subject: 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) -- cgit v1.2.3 From e27c946b589c53520409a0956b33d52ef7a0898f Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 15 Jul 2026 15:18:18 +0800 Subject: 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) -- cgit v1.2.3 From 04af4efde58a4a4ef4feab7360c46af9ec0b83a0 Mon Sep 17 00:00:00 2001 From: Mattia Meleleo Date: Wed, 8 Jul 2026 18:55:00 +0200 Subject: bpf, sockmap: Account for receive queue in FIONREAD without a verdict program tcp_bpf_ioctl() answers SIOCINQ from psock->msg_tot_len, which only counts bytes in ingress_msg. Without a stream/skb verdict program nothing is diverted there: data stays in sk_receive_queue, so FIONREAD returns 0 even though read() returns data. Add tcp_inq() to the reported value when the psock has no verdict program. The two queues are disjoint, so bytes redirected into ingress_msg from other sockets stay correctly accounted through msg_tot_len. Remove unused sk_psock_msg_inq(). Fixes: 929e30f93125 ("bpf, sockmap: Fix FIONREAD for sockmap") Signed-off-by: Mattia Meleleo Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Reviewed-by: John Fastabend Link: https://lore.kernel.org/bpf/20260708-fionread-no-verdict-v3-1-b4ee31b3af53@coralogix.com Signed-off-by: Kumar Kartikeya Dwivedi --- include/linux/skmsg.h | 14 -------------- net/ipv4/tcp_bpf.c | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index a8553401b1c9..d5e35f24738d 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -551,20 +551,6 @@ static inline void psock_progs_drop(struct sk_psock_progs *progs) psock_set_prog(&progs->skb_verdict, NULL); } -/* for tcp only, sk is locked */ -static inline ssize_t sk_psock_msg_inq(struct sock *sk) -{ - struct sk_psock *psock; - ssize_t inq = 0; - - psock = sk_psock_get(sk); - if (likely(psock)) { - inq = sk_psock_get_msg_len_nolock(psock); - sk_psock_put(sk, psock); - } - return inq; -} - /* for udp only, sk is not locked */ static inline ssize_t sk_msg_first_len(struct sock *sk) { diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index cc0bd73f36b6..8e905b50dead 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -334,6 +334,7 @@ unlock: static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg) { + struct sk_psock *psock; bool slow; if (cmd != SIOCINQ) @@ -344,7 +345,21 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg) return -EINVAL; slow = lock_sock_fast(sk); - *karg = sk_psock_msg_inq(sk); + psock = sk_psock_get(sk); + if (unlikely(!psock)) { + unlock_sock_fast(sk, slow); + return tcp_ioctl(sk, cmd, karg); + } + *karg = sk_psock_get_msg_len_nolock(psock); + /* Without a verdict program, ingress data is never diverted to + * ingress_msg: it stays in sk_receive_queue and is read through + * the fallback to tcp_recvmsg(), so account for it like + * tcp_ioctl() does. + */ + if (!READ_ONCE(psock->progs.stream_verdict) && + !READ_ONCE(psock->progs.skb_verdict)) + *karg += tcp_inq(sk); + sk_psock_put(sk, psock); unlock_sock_fast(sk, slow); return 0; -- cgit v1.2.3 From a42f05cc4c1d49e27bbc6f65415aa76274ef7736 Mon Sep 17 00:00:00 2001 From: Mattia Meleleo Date: Wed, 8 Jul 2026 18:55:01 +0200 Subject: selftests/bpf: Test FIONREAD on a sockmap socket without a verdict program Add a test validating that FIONREAD on a TCP socket in a sockmap without a verdict program reports data pending in sk_receive_queue. Signed-off-by: Mattia Meleleo Reviewed-by: Jiayuan Chen Reviewed-by: Emil Tsalapatis Reviewed-by: John Fastabend Link: https://lore.kernel.org/bpf/20260708-fionread-no-verdict-v3-2-b4ee31b3af53@coralogix.com Signed-off-by: Kumar Kartikeya Dwivedi --- .../selftests/bpf/prog_tests/sockmap_basic.c | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c index 2d22a9058a8e..e5fc038d747b 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c @@ -1373,6 +1373,43 @@ end: test_sockmap_pass_prog__destroy(skel); } +/* A socket in a sockmap without a verdict program keeps its ingress data + * in sk_receive_queue: FIONREAD must account for it. + */ +static void test_sockmap_no_verdict_fionread(void) +{ + int err, map, zero = 0, sent, avail; + int c0 = -1, c1 = -1, p0 = -1, p1 = -1; + struct test_sockmap_pass_prog *skel; + char buf[256] = "0123456789"; + + skel = test_sockmap_pass_prog__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + map = bpf_map__fd(skel->maps.sock_map_rx); + + err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1); + if (!ASSERT_OK(err, "create_socket_pairs()")) + goto out; + + err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST); + if (!ASSERT_OK(err, "bpf_map_update_elem(c1)")) + goto out_close; + + sent = xsend(p1, &buf, sizeof(buf), 0); + ASSERT_EQ(sent, sizeof(buf), "xsend(p1)"); + avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC); + ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)"); + +out_close: + close(c0); + close(p0); + close(c1); + close(p1); +out: + test_sockmap_pass_prog__destroy(skel); +} + void test_sockmap_basic(void) { if (test__start_subtest("sockmap create_update_free")) @@ -1415,6 +1452,8 @@ void test_sockmap_basic(void) test_sockmap_skb_verdict_shutdown(); if (test__start_subtest("sockmap skb_verdict fionread")) test_sockmap_skb_verdict_fionread(true); + if (test__start_subtest("sockmap no_verdict fionread")) + test_sockmap_no_verdict_fionread(); if (test__start_subtest("sockmap skb_verdict fionread on drop")) test_sockmap_skb_verdict_fionread(false); if (test__start_subtest("sockmap skb_verdict change tail")) -- cgit v1.2.3 From fd4cfa8c8f9a17cdec0539334d28754bc1d8a5d9 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Tue, 14 Jul 2026 02:38:45 -0700 Subject: bpf: Reject negative const offsets for buffer pointers The verifier rejects variable offsets for PTR_TO_TP_BUFFER and PTR_TO_BUF accesses, but it currently accepts a constant negative offset produced by pointer arithmetic. Commit 022ac0750883 ("bpf: use reg->var_off instead of reg->off for pointers") moved constant pointer offsets from reg->off to reg->var_off. However, __check_buffer_access() continued to check only the instruction offset. An access with reg->var_off equal to -8 and an instruction offset of zero therefore passes verification. For writable raw tracepoints, the access end is also calculated from the unsigned reg->var_off.value. An eight-byte access starting at -8 wraps the calculated end to zero, allowing the program to load and attach without increasing max_tp_access. After ensuring that reg->var_off is constant, calculate the effective access start using signed arithmetic and reject it when it is negative. Use the validated start to calculate the access end for both PTR_TO_TP_BUFFER and PTR_TO_BUF. Fixes: 022ac0750883 ("bpf: use reg->var_off instead of reg->off for pointers") Signed-off-by: Sun Jian Acked-by: Shung-Hsi Yu Cc: stable@vger.kernel.org # 5.2.0 Link: https://patch.msgid.link/20260714093846.18159-2-sun.jian.kdev@gmail.com Signed-off-by: Eduard Zingerman --- kernel/bpf/verifier.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6515d4d3c003..9f1333676365 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5326,14 +5326,11 @@ static int check_max_stack_depth(struct bpf_verifier_env *env) static int __check_buffer_access(struct bpf_verifier_env *env, const char *buf_info, const struct bpf_reg_state *reg, - argno_t argno, int off, int size) + argno_t argno, int off, int size, + u32 *access_end) { - if (off < 0) { - verbose(env, - "%s invalid %s buffer access: off=%d, size=%d\n", - reg_arg_name(env, argno), buf_info, off, size); - return -EACCES; - } + s64 start; + if (!tnum_is_const(reg->var_off)) { char tn_buf[48]; @@ -5344,6 +5341,15 @@ static int __check_buffer_access(struct bpf_verifier_env *env, return -EACCES; } + start = (s64)reg->var_off.value + off; + if (start < 0) { + verbose(env, + "%s invalid negative %s buffer offset: off=%d, var_off=%lld\n", + reg_arg_name(env, argno), buf_info, off, (s64)reg->var_off.value); + return -EACCES; + } + + *access_end = start + size; return 0; } @@ -5351,14 +5357,14 @@ static int check_tp_buffer_access(struct bpf_verifier_env *env, const struct bpf_reg_state *reg, argno_t argno, int off, int size) { + u32 access_end; int err; - err = __check_buffer_access(env, "tracepoint", reg, argno, off, size); + err = __check_buffer_access(env, "tracepoint", reg, argno, off, size, &access_end); if (err) return err; - env->prog->aux->max_tp_access = max(reg->var_off.value + off + size, - env->prog->aux->max_tp_access); + env->prog->aux->max_tp_access = max(access_end, env->prog->aux->max_tp_access); return 0; } @@ -5370,13 +5376,14 @@ static int check_buffer_access(struct bpf_verifier_env *env, u32 *max_access) { const char *buf_info = type_is_rdonly_mem(reg->type) ? "rdonly" : "rdwr"; + u32 access_end; int err; - err = __check_buffer_access(env, buf_info, reg, argno, off, size); + err = __check_buffer_access(env, buf_info, reg, argno, off, size, &access_end); if (err) return err; - *max_access = max(reg->var_off.value + off + size, *max_access); + *max_access = max(access_end, *max_access); return 0; } -- cgit v1.2.3 From 6f59deb32efa4673fc3b1fef9f3a0da48e9d8494 Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Tue, 14 Jul 2026 02:38:46 -0700 Subject: selftests/bpf: Cover negative buffer pointer offsets Add verifier coverage for constant negative offsets on PTR_TO_TP_BUFFER and PTR_TO_BUF pointers. Both programs adjust the buffer pointer by -8 and access it at offset zero, so the negative effective start must be rejected at load time. Switch the raw tracepoint writable attach checks from nbd_send_request to bpf_testmod_test_writable_bare_tp, avoiding a dependency on the NBD tracepoint. Keep the existing past-end case and add a case with a negative var_off compensated by a positive instruction offset. The effective start remains non-negative, so the program loads, but its access end exceeds the writable context size and bpf_raw_tracepoint_open() must return -EINVAL. Cc: stable@vger.kernel.org # 5.2.0 Signed-off-by: Sun Jian Acked-by: Shung-Hsi Yu Link: https://patch.msgid.link/20260714093846.18159-3-sun.jian.kdev@gmail.com Signed-off-by: Eduard Zingerman --- .../prog_tests/raw_tp_writable_reject_bad_access.c | 57 ++++++++++++++++++++++ .../raw_tp_writable_reject_nbd_invalid.c | 43 ---------------- tools/testing/selftests/bpf/prog_tests/verifier.c | 2 + .../selftests/bpf/progs/verifier_ptr_to_buf.c | 27 ++++++++++ .../selftests/bpf/progs/verifier_raw_tp_writable.c | 16 ++++++ 5 files changed, 102 insertions(+), 43 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c delete mode 100644 tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c create mode 100644 tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c new file mode 100644 index 000000000000..b8538fc4fc3f --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include "test_kmods/bpf_testmod.h" +#include "bpf_util.h" + +static void check_attach_reject(const struct bpf_insn *program, size_t prog_len) +{ + LIBBPF_OPTS(bpf_prog_load_opts, opts); + char error[4096]; + int bpf_fd, tp_fd; + + opts.log_level = 2; + opts.log_buf = error; + opts.log_size = sizeof(error); + + bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2", + program, prog_len, &opts); + if (!ASSERT_GE(bpf_fd, 0, "prog_load")) + return; + + tp_fd = bpf_raw_tracepoint_open("bpf_testmod_test_writable_bare_tp", bpf_fd); + ASSERT_EQ(tp_fd, -EINVAL, "bpf_raw_tracepoint_open"); + if (tp_fd >= 0) + close(tp_fd); + + close(bpf_fd); +} + +void test_raw_tp_writable_reject_bad_access(void) +{ + const struct bpf_insn program[] = { + /* r6 is our tp buffer */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + /* one byte beyond the end of the writable context */ + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, + sizeof(struct bpf_testmod_test_writable_ctx)), + BPF_EXIT_INSN(), + }; + + const struct bpf_insn negative_var_off_program[] = { + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), + /* make var_off negative, but keep the effective access offset non-negative */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8), + /* one byte beyond the end of the writable context */ + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, + sizeof(struct bpf_testmod_test_writable_ctx) + 8), + BPF_EXIT_INSN(), + }; + + if (test__start_subtest("past_end")) + check_attach_reject(program, ARRAY_SIZE(program)); + + if (test__start_subtest("negative_var_off_past_end")) + check_attach_reject(negative_var_off_program, + ARRAY_SIZE(negative_var_off_program)); +} diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c deleted file mode 100644 index 216b0dfac0fe..000000000000 --- a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include "bpf_util.h" - -void test_raw_tp_writable_reject_nbd_invalid(void) -{ - __u32 duration = 0; - char error[4096]; - int bpf_fd = -1, tp_fd = -1; - - const struct bpf_insn program[] = { - /* r6 is our tp buffer */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0), - /* one byte beyond the end of the nbd_request struct */ - BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, - sizeof(struct nbd_request)), - BPF_EXIT_INSN(), - }; - - LIBBPF_OPTS(bpf_prog_load_opts, opts, - .log_level = 2, - .log_buf = error, - .log_size = sizeof(error), - ); - - bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2", - program, ARRAY_SIZE(program), - &opts); - if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable load", - "failed: %d errno %d\n", bpf_fd, errno)) - return; - - tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd); - if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable open", - "erroneously succeeded\n")) - goto out_bpffd; - - close(tp_fd); -out_bpffd: - close(bpf_fd); -} diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index 8a3d69e2453c..be97f6887f0e 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -78,6 +78,7 @@ #include "verifier_precision.skel.h" #include "verifier_prevent_map_lookup.skel.h" #include "verifier_private_stack.skel.h" +#include "verifier_ptr_to_buf.skel.h" #include "verifier_raw_stack.skel.h" #include "verifier_raw_tp_writable.skel.h" #include "verifier_reg_equal.skel.h" @@ -230,6 +231,7 @@ void test_verifier_or_jmp32_k(void) { RUN(verifier_or_jmp32_k); } void test_verifier_precision(void) { RUN(verifier_precision); } void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup); } void test_verifier_private_stack(void) { RUN(verifier_private_stack); } +void test_verifier_ptr_to_buf(void) { RUN(verifier_ptr_to_buf); } void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); } void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); } void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); } diff --git a/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c new file mode 100644 index 000000000000..12cf24db46a0 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "bpf_misc.h" + +SEC("iter/bpf_map_elem") +__description("PTR_TO_BUF: reject negative const offset") +__failure +__msg("invalid negative rdwr buffer offset") +__naked void ptr_to_buf_reject_negative_const_offset(void) +{ + asm volatile ("r0 = 0; \ + r2 = *(u64 *)(r1 + %[value_off]); \ + if r2 == 0 goto l0_%=; \ + r2 += -8; \ + r0 = *(u64 *)(r2 + 0); \ +l0_%=: \ + exit; \ + " + : + : __imm_const(value_off, + offsetof(struct bpf_iter__bpf_map_elem, value)) + : __clobber_all); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c index 14a0172e2141..4055a6443bc2 100644 --- a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c +++ b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c @@ -47,4 +47,20 @@ l0_%=: /* shift the buffer pointer to a variable location */\ : __clobber_all); } +SEC("raw_tracepoint.w") +__description("raw_tracepoint_writable: reject negative const offset") +__failure +__msg("invalid negative tracepoint buffer offset") +__naked void tracepoint_writable_reject_negative_const_offset(void) +{ + asm volatile (" \ + r6 = *(u64 *)(r1 + 0); \ + r6 += -8; \ + r0 = *(u64 *)(r6 + 0); \ + exit; \ +" : + : + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; -- cgit v1.2.3 From 2f2312c422fd2695da772cecb30c69994b795964 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Tue, 14 Jul 2026 09:03:06 -0700 Subject: 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 @@ vmentry_fail_vmexit: 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)) -- cgit v1.2.3 From 0db3a430d9681fdb29890bef6934cd89cd1745d0 Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Thu, 9 Jul 2026 13:57:35 -0600 Subject: iommu/amd: Fix IRQ unsafe locking in gdom allocation Lockdep complains: [ 259.410489] ===================================================== [ 259.417287] WARNING: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected [ 259.424667] 7.0.0-g51db1d8d2113 #54 Not tainted [ 259.429718] ----------------------------------------------------- [ 259.436516] qemu-system-x86/10143 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire: [ 259.444670] ff3b2b1c60305170 (&xa->xa_lock#25){+.+.}-{3:3}, at: __domain_flush_pages+0x17c/0x4b0 [ 259.454485] and this task is already holding: [ 259.460991] ff3b2b1c98504cc0 (&domain->lock){-.-.}-{3:3}, at: amd_iommu_iotlb_sync+0x25/0x60 [ 259.470408] which would create a new lock dependency: [ 259.476041] (&domain->lock){-.-.}-{3:3} -> (&xa->xa_lock#25){+.+.}-{3:3} [ 259.483615] but this new dependency connects a HARDIRQ-irq-safe lock: [ 259.492447] (&domain->lock){-.-.}-{3:3} [ 259.492449] ... which became HARDIRQ-irq-safe at: [ 259.503705] lock_acquire+0xb6/0x2e0 [ 259.507790] _raw_spin_lock_irqsave+0x3e/0x60 [ 259.512748] amd_iommu_flush_iotlb_all+0x20/0x50 [ 259.517996] iommu_dma_free_iova.isra.0+0x1b8/0x1e0 [ 259.523534] __iommu_dma_unmap+0xc2/0x140 [ 259.528100] iommu_dma_unmap_phys+0x55/0xc0 [ 259.532863] dma_unmap_phys+0x274/0x2e0 [ 259.537238] dma_unmap_page_attrs+0x17/0x30 [ 259.542000] nvme_unmap_data+0x13e/0x280 [ 259.546473] nvme_pci_complete_batch+0x45/0x70 [ 259.551524] nvme_irq+0x83/0x90 [ 259.555123] __handle_irq_event_percpu+0x92/0x360 [ 259.560466] handle_irq_event+0x39/0x80 [ 259.564841] handle_edge_irq+0xb2/0x1a0 [ 259.569214] __common_interrupt+0x4e/0x130 [ 259.573882] common_interrupt+0x88/0xa0 [ 259.578256] asm_common_interrupt+0x27/0x40 [ 259.583019] cpuidle_enter_state+0x119/0x5d0 [ 259.587877] cpuidle_enter+0x2e/0x50 [ 259.591962] do_idle+0x153/0x2c0 [ 259.595657] cpu_startup_entry+0x29/0x30 [ 259.600128] start_secondary+0x118/0x150 [ 259.604601] common_startup_64+0x13e/0x141 [ 259.609266] to a HARDIRQ-irq-unsafe lock: [ 259.615384] (&xa->xa_lock#25){+.+.}-{3:3} [ 259.615386] ... which became HARDIRQ-irq-unsafe at: [ 259.627039] ... [ 259.627039] lock_acquire+0xb6/0x2e0 [ 259.633071] _raw_spin_lock+0x2f/0x50 [ 259.637250] amd_iommu_alloc_domain_nested+0x140/0x3c0 [ 259.643078] iommufd_hwpt_alloc+0x272/0x800 [iommufd] [ 259.648813] iommufd_fops_ioctl+0x14e/0x200 [iommufd] [ 259.654547] __x64_sys_ioctl+0x9d/0xf0 ... Since amd_iommu_domain_flush_pages() necessarily holds domain->lock to do the flush, switch the allocation side in gdom_info_load_or_alloc_locked() to HARDIRQ-safe allocation. The IOMMU_DESTROY->free path has the same issue, so switch that path to HARDIRQ-safe locking as well. Fixes: 757d2b1fdf5b ("iommu/amd: Introduce gDomID-to-hDomID Mapping and handle parent domain invalidation") Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Ankit Soni Signed-off-by: Will Deacon --- drivers/iommu/amd/nested.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c index 5b902598e68a..5c9405223f91 100644 --- a/drivers/iommu/amd/nested.c +++ b/drivers/iommu/amd/nested.c @@ -59,7 +59,9 @@ static int validate_gdte_nested(struct iommu_hwpt_amd_guest *gdte) return 0; } -static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long index) +static void *gdom_info_load_or_alloc_locked(struct xarray *xa, + unsigned long index, + unsigned long *flags) { struct guest_domain_mapping_info *elm, *res; @@ -67,13 +69,13 @@ static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long ind if (elm) return elm; - xa_unlock(xa); + xa_unlock_irqrestore(xa, *flags); elm = kzalloc_obj(struct guest_domain_mapping_info); - xa_lock(xa); + xa_lock_irqsave(xa, *flags); if (!elm) return ERR_PTR(-ENOMEM); - res = __xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL); + res = __xa_cmpxchg(xa, index, NULL, elm, GFP_ATOMIC); if (xa_is_err(res)) res = ERR_PTR(xa_err(res)); @@ -95,6 +97,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, const struct iommu_user_data *user_data) { int ret; + unsigned long irqflags; struct nested_domain *ndom; struct guest_domain_mapping_info *gdom_info; struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core); @@ -136,11 +139,12 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, * keep track of the gDomID mapping. When the S2 is changed, the INVALIDATE_IOMMU_PAGES * command must be issued for each hDomID in the xarray. */ - xa_lock(&aviommu->gdomid_array); + xa_lock_irqsave(&aviommu->gdomid_array, irqflags); - gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, ndom->gdom_id); + gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, + ndom->gdom_id, &irqflags); if (IS_ERR(gdom_info)) { - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); ret = PTR_ERR(gdom_info); goto out_err; } @@ -148,7 +152,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, /* Check if gDomID exist */ if (refcount_inc_not_zero(&gdom_info->users)) { ndom->gdom_info = gdom_info; - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); pr_debug("%s: Found gdom_id=%#x, hdom_id=%#x\n", __func__, ndom->gdom_id, gdom_info->hdom_id); @@ -161,7 +165,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, if (gdom_info->hdom_id <= 0) { __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC); - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); ret = -ENOSPC; goto out_err_gdom_info; } @@ -169,7 +173,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, ndom->gdom_info = gdom_info; refcount_set(&gdom_info->users, 1); - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); pr_debug("%s: Allocate gdom_id=%#x, hdom_id=%#x\n", __func__, ndom->gdom_id, gdom_info->hdom_id); @@ -257,14 +261,15 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev, static void nested_domain_free(struct iommu_domain *dom) { + unsigned long irqflags; struct guest_domain_mapping_info *curr; struct nested_domain *ndom = to_ndomain(dom); struct amd_iommu_viommu *aviommu = ndom->viommu; - xa_lock(&aviommu->gdomid_array); + xa_lock_irqsave(&aviommu->gdomid_array, irqflags); if (!refcount_dec_and_test(&ndom->gdom_info->users)) { - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); return; } @@ -275,7 +280,7 @@ static void nested_domain_free(struct iommu_domain *dom) curr = __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id, ndom->gdom_info, NULL, GFP_ATOMIC); - xa_unlock(&aviommu->gdomid_array); + xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); if (WARN_ON(!curr || xa_err(curr))) return; -- cgit v1.2.3 From 283c5c4c34b4c8d1ebd038d8f360c5ba7fcc767b Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Thu, 9 Jul 2026 13:57:36 -0600 Subject: iommu/amd: Fix nested domain leak A couple of runs of different AI tools have generated something like the following bug report: In nested_domain_free(), when refcount_dec_and_test() returns false (other nested domains still reference the same gdom_info), the function returns without calling kfree(ndom), leaking the nested_domain structure. This problem wasn't introduced by this patch, but exists in the code from commit 757d2b1fdf5b that the patch modifies. Each nested_domain (ndom) is allocated individually in amd_iommu_alloc_domain_nested() via kzalloc_obj(*ndom). The .free callback is the sole point responsible for freeing this domain. When the refcount is > 0, only the xa_unlock_irqrestore is performed and the function returns, leaving ndom permanently allocated. This leak occurs every time a nested domain sharing a gDomID is destroyed while other domains still use that gDomID. There is a similar leak later in this function in the WARN_ON() test when the mapping is already NULL. Switch to a RAII-based cleanup for ndom, since it should always be freed in this function. Fixes: 757d2b1fdf5b ("iommu/amd: Introduce gDomID-to-hDomID Mapping and handle parent domain invalidation") Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Ankit Soni Signed-off-by: Will Deacon --- drivers/iommu/amd/nested.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c index 5c9405223f91..63b53b29e029 100644 --- a/drivers/iommu/amd/nested.c +++ b/drivers/iommu/amd/nested.c @@ -263,7 +263,7 @@ static void nested_domain_free(struct iommu_domain *dom) { unsigned long irqflags; struct guest_domain_mapping_info *curr; - struct nested_domain *ndom = to_ndomain(dom); + struct nested_domain *ndom __free(kfree) = to_ndomain(dom); struct amd_iommu_viommu *aviommu = ndom->viommu; xa_lock_irqsave(&aviommu->gdomid_array, irqflags); @@ -290,7 +290,6 @@ static void nested_domain_free(struct iommu_domain *dom) amd_iommu_pdom_id_free(ndom->gdom_info->hdom_id); kfree(curr); - kfree(ndom); } static const struct iommu_domain_ops nested_domain_ops = { -- cgit v1.2.3 From 7a7baebd9f23ba4f24796775472b2fd00dcd95d9 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Wed, 15 Jul 2026 13:23:08 +0530 Subject: gpio: sloppy-logic-analyzer: Fix memory leak in gpio_la_poll_probe() The memory allocated for priv->blob.data is not freed in the error paths that follow the fops_buf_size_set() call in gpio_la_poll_probe(), as well as in the remove function. Fix that by using device managed action to free the memory on remove. Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling") Signed-off-by: Abdun Nihaal Reviewed-by: Wolfram Sang Link: https://patch.msgid.link/20260715075311.527753-1-nihaal@cse.iitm.ac.in Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sloppy-logic-analyzer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 2bbd308ca08e..aa23b5779802 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -160,6 +160,13 @@ static int fops_buf_size_get(void *data, u64 *val) return 0; } +static void fops_buf_release(void *data) +{ + struct gpio_la_poll_priv *priv = data; + + vfree(priv->blob.data); +} + static int fops_buf_size_set(void *data, u64 val) { struct gpio_la_poll_priv *priv = data; @@ -238,6 +245,9 @@ static int gpio_la_poll_probe(struct platform_device *pdev) return ret; fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); + ret = devm_add_action_or_reset(dev, fops_buf_release, priv); + if (ret) + return ret; priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN); if (IS_ERR(priv->descs)) -- cgit v1.2.3 From e1cc8fa0fb9e5112d15f2c310b68ac316981c06c Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 14 Jul 2026 00:30:53 +0100 Subject: gpiolib: tolerate gpio-hogs lacking a hogging state Commit d1d564ec4992 ("gpio: move hogs into GPIO core") made gpiochip_add_hog() return -EINVAL for hog nodes lacking any of the 'input', 'output-low' or 'output-high' properties. The error is propagated by gpiochip_hog_lines() and fails registration of the whole GPIO chip. The previous OF-specific implementation tolerated such nodes: of_parse_own_gpio() warned "no hogging state specified, bailing out" and of_gpiochip_add_hog() stopped processing the node without failing chip registration. Some boards deliberately ship hog nodes without a hogging state in their base devicetree and supply the state via overlay, e.g. the PCIe slot key selection hogs on the BananaPi R4 Pro added in commit e309fa232d12 ("arm64: dts: mediatek: mt7988a-bpi-r4pro: rework pcie gpio-hog handling"), as the polarity set in the base devicetree could not be overridden from an overlay. Booting such a board without an overlay applied now fails to register the gpiochip. On the BananaPi R4 Pro this means the MT7988A pinctrl device fails to probe, all peripherals including the console UART defer forever, and the board finally hangs when clk_disable_unused() gates the clocks of the UART still in use by earlycon: gpiochip_add_data_with_key: GPIOs 512..595 (pinctrl_moore) failed to register, -22 mt7988-pinctrl 1001f000.pinctrl: error -EINVAL: Failed to add gpio_chip ... clk: Disabling unused clocks (hangs) Restore the previous behaviour by warning about hog nodes lacking a hogging state and skipping them instead of failing the registration of the whole GPIO chip. Fixes: d1d564ec4992 ("gpio: move hogs into GPIO core") Cc: stable@vger.kernel.org Signed-off-by: Daniel Golle Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/4c67cf0839ccf57db35a826df6d8fc779531509a.1783974733.git.daniel@makrotopia.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e5fb60111151..c433a095907f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -999,14 +999,17 @@ int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode) if (ret < 0) return ret; - if (fwnode_property_present(fwnode, "input")) + if (fwnode_property_present(fwnode, "input")) { dflags |= GPIOD_IN; - else if (fwnode_property_present(fwnode, "output-low")) + } else if (fwnode_property_present(fwnode, "output-low")) { dflags |= GPIOD_OUT_LOW; - else if (fwnode_property_present(fwnode, "output-high")) + } else if (fwnode_property_present(fwnode, "output-high")) { dflags |= GPIOD_OUT_HIGH; - else - return -EINVAL; + } else { + gpiochip_warn(gc, "%pfwP: no hogging state specified, bailing out\n", + fwnode); + return 0; + } fwnode_property_read_string(fwnode, "line-name", &name); -- cgit v1.2.3 From 4a2c8cbe9bcba170706fdf08b1c84b6cbcf5b044 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Tue, 14 Jul 2026 18:30:55 +0200 Subject: drm/panthor: return error on truncated firmware panthor_fw_load() detects truncated firmware images, but jumps to the common cleanup path without setting ret. If no previous error was recorded, the function can return 0 and treat the invalid firmware as successfully loaded. Set ret to -EINVAL before leaving the truncated-image path. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Liviu Dudau Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260714163056.22329-1-osama.abdelkader@gmail.com Signed-off-by: Liviu Dudau --- drivers/gpu/drm/panthor/panthor_fw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index de8e6689a869..90f59d782a80 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -829,6 +829,7 @@ static int panthor_fw_load(struct panthor_device *ptdev) } if (hdr.size > iter.size) { + ret = -EINVAL; drm_err(&ptdev->base, "Firmware image is truncated\n"); goto out; } -- cgit v1.2.3 From 022e901333c3054656a640794e842bab7af5a75c Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 13 Jul 2026 16:29:12 +0800 Subject: drm/panthor: Check debugfs GEM lock initialization drmm_mutex_init() can fail while registering the managed cleanup action. When that happens, drmm_add_action_or_reset() destroys the mutex before returning the error. Continuing initialization would therefore leave the debugfs GEM object list with an unusable lock. Propagate the error as is already done for the other managed mutexes in panthor_device_init(). Fixes: a3707f53eb3f ("drm/panthor: show device-wide list of DRM GEM objects over DebugFS") Signed-off-by: Linmao Li Reviewed-by: Liviu Dudau Link: https://patch.msgid.link/20260713082912.321021-1-lilinmao@kylinos.cn Signed-off-by: Liviu Dudau --- drivers/gpu/drm/panthor/panthor_device.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index 0b25abebb803..9687c59de350 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -182,7 +182,10 @@ int panthor_device_init(struct panthor_device *ptdev) return ret; #ifdef CONFIG_DEBUG_FS - drmm_mutex_init(&ptdev->base, &ptdev->gems.lock); + ret = drmm_mutex_init(&ptdev->base, &ptdev->gems.lock); + if (ret) + return ret; + INIT_LIST_HEAD(&ptdev->gems.node); #endif -- cgit v1.2.3 From 8d9c9b135b5c23de9811a8426257cbd2fa024a99 Mon Sep 17 00:00:00 2001 From: Zongmin Zhou Date: Wed, 15 Jul 2026 11:08:18 +0800 Subject: KVM: riscv: Fix Spectre-v1 in vector register access User-controlled register indices from the ONE_REG ioctl are used to index into the vector register buffer (v0..v31). Sanitize the calculated offset with array_index_nospec() to prevent speculative out-of-bounds access. Signed-off-by: Zongmin Zhou Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260715030818.75657-1-min_halo@163.com Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_vector.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c index 62d2fb77bb9b..3708616e2c32 100644 --- a/arch/riscv/kvm/vcpu_vector.c +++ b/arch/riscv/kvm/vcpu_vector.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -129,11 +130,20 @@ static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu, return -ENOENT; } } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) { + unsigned long reg_offset; + if (reg_size != vlenb) return -EINVAL; WARN_ON(!cntx->vector.datap); - *reg_addr = cntx->vector.datap + - (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb; + /* + * The reg_num is derived from the userspace-provided ONE_REG + * id. Sanitize it with array_index_nospec() to prevent + * speculative out-of-bounds access to the vector register + * buffer (32 vector registers: v0..v31). + */ + reg_offset = array_index_nospec( + reg_num - KVM_REG_RISCV_VECTOR_REG(0), 32); + *reg_addr = cntx->vector.datap + reg_offset * vlenb; } else { return -ENOENT; } -- cgit v1.2.3 From ddb44baed257560f192b145ed36cf8c0a412de47 Mon Sep 17 00:00:00 2001 From: Jhonraushan Date: Wed, 15 Jul 2026 13:12:06 +0530 Subject: accel/ivpu: Reject firmware log with size smaller than header fw_log_from_bo() validates the tracing buffer header_size and that the log fits within the BO, but never checks that log->size is at least log->header_size. fw_log_print_buffer() then computes: u32 data_size = log->size - log->header_size; which underflows to a near-U32_MAX value when firmware reports a log whose size is smaller than its header. That huge data_size defeats the log_start/log_end bounds clamps added by commit dd1311bcf0e6 ("accel/ivpu: Add bounds checks for firmware log indices"), so fw_log_print_lines() reads far past the small real data region of the BO. A size of 0 also makes fw_log_from_bo() advance the offset by 0, causing the callers to loop forever on the same header. Reject logs whose size is smaller than the header (which also rejects size == 0). Fixes: d4e4257afa6e ("accel/ivpu: Add firmware tracing support") Cc: stable@vger.kernel.org Signed-off-by: Jhonraushan Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260715074206.867712-1-raushan.jhon@gmail.com --- drivers/accel/ivpu/ivpu_fw_log.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c index 275baf844b56..716467aa3156 100644 --- a/drivers/accel/ivpu/ivpu_fw_log.c +++ b/drivers/accel/ivpu/ivpu_fw_log.c @@ -43,6 +43,10 @@ static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *off ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size); return -EINVAL; } + if (log->size < log->header_size) { + ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); + return -EINVAL; + } if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) { ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); return -EINVAL; -- cgit v1.2.3 From 3b597d24dc0455ae926f1053f97c2725038fc3cd Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:36:25 +0800 Subject: ALSA: hda: cs35l41: validate and free ACPI mute object cs35l41_get_acpi_mute_state() evaluates a _DSM method to get the ACPI mute state and reads the first byte from the returned object. However, the returned ACPI object is owned by the caller and is never freed after use, so each successful query leaks the _DSM result object. The code also assumes that the returned object is a buffer with at least one byte. A malformed firmware response can return a different object type or an empty buffer, and the direct ret->buffer.pointer dereference can then access an invalid pointer. Use the typed _DSM helper, validate that the returned buffer contains at least one byte, and free the ACPI object after reading it. Fixes: 447106e92a0c ("ALSA: hda: cs35l41: Support mute notifications for CS35L41 HDA") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260708113625.752913-1-lgs201920130244@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index 64a5bd895fd1..237059ef22f5 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1434,10 +1434,19 @@ static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle guid_parse(CS35L41_UUID, &guid); if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) { - ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL); + ret = acpi_evaluate_dsm_typed(handle, &guid, 0, + CS35L41_DSM_GET_MUTE, NULL, + ACPI_TYPE_BUFFER); + if (!ret) return -EINVAL; + if (!ret->buffer.length || !ret->buffer.pointer) { + ACPI_FREE(ret); + return -EINVAL; + } + mute = *ret->buffer.pointer; + ACPI_FREE(ret); dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute); } -- cgit v1.2.3 From fadeedd7cfc5d73d33fa3d7ac54b9b27aabd09d2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 12 Jul 2026 07:23:55 -1000 Subject: sched/psi: Create the psimon kthread outside of cgroup_mutex a5b98009f16d ("sched/psi: fix race between file release and pressure write") made pressure_write() hold cgroup_mutex across psi_trigger_create(), which forks the psimon kthread for the first rtpoll trigger. As kthread creation depends on the whole fork path, the commit inadvertently created a lot of unwanted locking dependencies from cgroup_mutex. sched_ext got hit by one: its enable path blocks forks and then grabs cgroup_mutex, so a pressure write racing a scheduler enable deadlocks, with every other fork piling up behind. Fix it by splitting trigger creation so that the worker is forked with cgroup_mutex dropped and the kernfs active reference left broken. The latter matters because rmdir and cgroup.pressure writes drain active references under cgroup_mutex. Publishing the trigger last keeps error reporting synchronous and preserves the of->priv lifetime rules. The trigger registered in the first stage pins the group's rtpoll machinery across the unlocked window, leaving only creation races to resolve. The catch-up poll on installation covers scheduling attempts dropped while there was no worker. v2: Retagged sched/psi (was cgroup). Fixes: a5b98009f16d ("sched/psi: fix race between file release and pressure write") Cc: stable@vger.kernel.org Cc: Edward Adam Davis Cc: Chen Ridong Reported-by: Matt Fleming Closes: https://lore.kernel.org/all/20260710100441.2653477-1-matt@readmodwrite.com/ Signed-off-by: Tejun Heo Acked-by: Johannes Weiner Tested-by: Matt Fleming Acked-by: Suren Baghdasaryan --- include/linux/psi.h | 4 ++- kernel/cgroup/cgroup.c | 23 ++++++++++++++++- kernel/sched/psi.c | 69 ++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/include/linux/psi.h b/include/linux/psi.h index e0745873e3f2..7966e3ac03b9 100644 --- a/include/linux/psi.h +++ b/include/linux/psi.h @@ -25,7 +25,9 @@ void psi_memstall_leave(unsigned long *flags); int psi_show(struct seq_file *s, struct psi_group *group, enum psi_res res); struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, enum psi_res res, struct file *file, - struct kernfs_open_file *of); + struct kernfs_open_file *of, + bool *need_rtpoll_worker); +int psi_trigger_create_rtpoll_worker(struct psi_group *group); void psi_trigger_destroy(struct psi_trigger *t); __poll_t psi_trigger_poll(void **trigger_ptr, struct file *file, diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 38f8d9df8fbc..b5b461d4418b 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -3996,6 +3996,7 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf, struct psi_trigger *new; struct cgroup *cgrp; struct psi_group *psi; + bool need_rtpoll_worker; ssize_t ret = 0; cgrp = cgroup_kn_lock_live(of->kn, false); @@ -4015,12 +4016,32 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf, } psi = cgroup_psi(cgrp); - new = psi_trigger_create(psi, buf, res, of->file, of); + new = psi_trigger_create(psi, buf, res, of->file, of, + &need_rtpoll_worker); if (IS_ERR(new)) { ret = PTR_ERR(new); goto out_unlock; } + /* + * The worker fork must run with neither cgroup_mutex nor the file's + * kernfs active reference held. The latter is broken since + * cgroup_kn_lock_live(). @of->priv may be released while unlocked, so + * recheck before publishing @new. + */ + if (need_rtpoll_worker) { + cgroup_unlock(); + ret = psi_trigger_create_rtpoll_worker(psi); + cgroup_lock(); + + if (!ret && !of->priv) + ret = -ENODEV; + if (ret) { + psi_trigger_destroy(new); + goto out_unlock; + } + } + smp_store_release(&ctx->psi.trigger, new); out_unlock: diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c index d9c9d9480a45..565ec7b80743 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -1292,9 +1292,44 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res) return 0; } +/* + * Create @group's rtpoll worker after psi_trigger_create() reported the need + * for one. kthread creation depends on the whole fork path and we don't want + * all of that nested inside cgroup_mutex, so the caller must drop it and any + * other lock that forks can wait behind. If two callers race, the loser stops + * its never-woken kthread. + */ +int psi_trigger_create_rtpoll_worker(struct psi_group *group) +{ + struct task_struct *task; + + task = kthread_create(psi_rtpoll_worker, group, "psimon"); + if (IS_ERR(task)) + return PTR_ERR(task); + + scoped_guard(mutex, &group->rtpoll_trigger_lock) { + if (!rcu_access_pointer(group->rtpoll_task)) { + atomic_set(&group->rtpoll_wakeup, 0); + wake_up_process(task); + rcu_assign_pointer(group->rtpoll_task, task); + + /* + * Poll once to catch up on scheduling attempts dropped + * while there was no rtpoll worker. + */ + psi_schedule_rtpoll_work(group, 1, true); + return 0; + } + } + + kthread_stop(task); + return 0; +} + struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, enum psi_res res, struct file *file, - struct kernfs_open_file *of) + struct kernfs_open_file *of, + bool *need_rtpoll_worker) { struct psi_trigger *t; enum psi_states state; @@ -1302,6 +1337,8 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, bool privileged; u32 window_us; + *need_rtpoll_worker = false; + if (static_branch_likely(&psi_disabled)) return ERR_PTR(-EOPNOTSUPP); @@ -1362,26 +1399,14 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, if (privileged) { mutex_lock(&group->rtpoll_trigger_lock); - if (!rcu_access_pointer(group->rtpoll_task)) { - struct task_struct *task; - - task = kthread_create(psi_rtpoll_worker, group, "psimon"); - if (IS_ERR(task)) { - kfree(t); - mutex_unlock(&group->rtpoll_trigger_lock); - return ERR_CAST(task); - } - atomic_set(&group->rtpoll_wakeup, 0); - wake_up_process(task); - rcu_assign_pointer(group->rtpoll_task, task); - } - list_add(&t->node, &group->rtpoll_triggers); group->rtpoll_min_period = min(group->rtpoll_min_period, div_u64(t->win.size, UPDATES_PER_WINDOW)); group->rtpoll_nr_triggers[t->state]++; group->rtpoll_states |= (1 << t->state); + *need_rtpoll_worker = !rcu_access_pointer(group->rtpoll_task); + mutex_unlock(&group->rtpoll_trigger_lock); } else { mutex_lock(&group->avgs_lock); @@ -1541,6 +1566,8 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf, size_t buf_size; struct seq_file *seq; struct psi_trigger *new; + bool need_rtpoll_worker; + int ret; if (static_branch_likely(&psi_disabled)) return -EOPNOTSUPP; @@ -1565,12 +1592,22 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf, return -EBUSY; } - new = psi_trigger_create(&psi_system, buf, res, file, NULL); + new = psi_trigger_create(&psi_system, buf, res, file, NULL, + &need_rtpoll_worker); if (IS_ERR(new)) { mutex_unlock(&seq->lock); return PTR_ERR(new); } + if (need_rtpoll_worker) { + ret = psi_trigger_create_rtpoll_worker(&psi_system); + if (ret) { + psi_trigger_destroy(new); + mutex_unlock(&seq->lock); + return ret; + } + } + smp_store_release(&seq->private, new); mutex_unlock(&seq->lock); -- cgit v1.2.3 From 5457025fa8ca3c0d2732109513de839e3e797190 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 12 Jul 2026 07:23:55 -1000 Subject: sched/psi: Shut down rtpoll_timer in psi_cgroup_free() psi_schedule_rtpoll_work() is called locklessly from the scheduler hotpath and can race psi_trigger_destroy() taking down the last rtpoll trigger under rtpoll_trigger_lock: psi_schedule_rtpoll_work() psi_trigger_destroy() rcu_read_lock(); task = rcu_dereference(rtpoll_task); rcu_assign_pointer(rtpoll_task, NULL); timer_delete(&rtpoll_timer); mod_timer(&rtpoll_timer, ...); rcu_read_unlock(); synchronize_rcu(); kthread_stop(task_to_destroy); The group can then be freed with the re-armed timer still pending, and poll_timer_fn() runs on freed memory. 461daba06bdc ("psi: eliminate kthread_worker from psi trigger scheduling mechanism") deleted the timer synchronously after the synchronize_rcu(), which prevented this but raced trigger creation instead: the deletion could cancel the timer that a new trigger set armed during the grace period and, as creation also reinitialized the timer at the time, corrupt it. 8f91efd870ea ("psi: Fix race between psi_trigger_create/destroy") moved the initialization into group_init() and the deletion into the locked section, trading the creation races for the window above. Neither placement in the destruction path works. A pending timer firing while the group is alive is harmless though. poll_timer_fn() just wakes the rtpoll waitqueue and doesn't re-arm itself. Bind the timer to the group's lifetime instead and shut it down in psi_cgroup_free(). Nothing can arm it by then. timer_shutdown_sync() because the timer is never armed again. Fixes: 8f91efd870ea ("psi: Fix race between psi_trigger_create/destroy") Cc: stable@vger.kernel.org # v5.10+ Reported-by: Sashiko AI Closes: https://lore.kernel.org/all/20260711000434.36C4A1F000E9@smtp.kernel.org/ Signed-off-by: Tejun Heo Acked-by: Johannes Weiner Tested-by: Matt Fleming Acked-by: Suren Baghdasaryan --- kernel/sched/psi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c index 565ec7b80743..e2e825dcd088 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -1134,6 +1134,12 @@ void psi_cgroup_free(struct cgroup *cgroup) return; cancel_delayed_work_sync(&cgroup->psi->avgs_work); + /* + * A psi_schedule_rtpoll_work() call racing the last trigger's + * destruction may have re-armed the timer after psi_trigger_destroy() + * deleted it. Spurious firing while the group is alive is harmless. + */ + timer_shutdown_sync(&cgroup->psi->rtpoll_timer); free_percpu(cgroup->psi->pcpu); /* All triggers must be removed by now */ WARN_ONCE(cgroup->psi->rtpoll_states, "psi: trigger leak\n"); -- cgit v1.2.3 From 4bb06b60d982355e22647b3d12d6619419f8c1fa Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Wed, 8 Jul 2026 12:02:14 +0200 Subject: s390/checksum: Fix csum_partial() without vector facility Currently csum_partial() calls csum_copy() with copy=false and dst=NULL. On machines without the vector facility, csum_copy() falls back to cksm(dst, ...), causing the checksum to be calculated from address zero instead of the source buffer. The VX implementation already checksums data loaded from src. Make the fallback do the same by passing src to cksm(). Fixes: dcd3e1de9d17 ("s390/checksum: provide csum_partial_copy_nocheck()") Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/lib/csum-partial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/lib/csum-partial.c b/arch/s390/lib/csum-partial.c index 458abd9bac70..9d74ceff136c 100644 --- a/arch/s390/lib/csum-partial.c +++ b/arch/s390/lib/csum-partial.c @@ -23,7 +23,7 @@ static __always_inline __wsum csum_copy(void *dst, const void *src, int len, __w if (!cpu_has_vx()) { if (copy) memcpy(dst, src, len); - return cksm(dst, len, sum); + return cksm(src, len, sum); } kernel_fpu_begin(&vxstate, KERNEL_VXR_V16V23); fpu_vlvgf(16, (__force u32)sum, 1); -- cgit v1.2.3 From 49145bce539117db4b6e9e83c0e5ef528e361050 Mon Sep 17 00:00:00 2001 From: Sumanth Korikkar Date: Mon, 6 Jul 2026 12:46:31 +0200 Subject: s390/perf_cpum_cf: Add missing array_index_nospec() to __hw_perf_event_init() ev variable is userspace controlled via event->attr.config and used as an array index after bounds checking, but without speculation barriers. Add the missing array_index_nospec() call to prevent speculative execution. Cc: stable@vger.kernel.org Fixes: 212188a596d1 ("[S390] perf: add support for s390x CPU counters") Signed-off-by: Sumanth Korikkar Reviewed-by: Ilya Leoshkevich Acked-by: Thomas Richter Signed-off-by: Vasily Gorbik --- arch/s390/kernel/perf_cpum_cf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 7aa655664ecc..2076ac22e2c4 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -768,6 +769,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) if (!is_userspace_event(ev)) { if (ev >= ARRAY_SIZE(cpumf_generic_events_user)) return -EOPNOTSUPP; + ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_user)); ev = cpumf_generic_events_user[ev]; } } else if (!attr->exclude_kernel && attr->exclude_user) { @@ -778,6 +780,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) if (!is_userspace_event(ev)) { if (ev >= ARRAY_SIZE(cpumf_generic_events_basic)) return -EOPNOTSUPP; + ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_basic)); ev = cpumf_generic_events_basic[ev]; } } -- cgit v1.2.3 From 0b604e886ece11b71c4daaeccc512c784b89b014 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Jul 2026 18:06:20 +0800 Subject: ASoC: bt-sco: fix duplicate DAPM widget names for wideband DAI The bt-sco-pcm-wb DAI uses the same stream_name strings as bt-sco-pcm ("Playback" and "Capture"). This causes duplicate DAPM AIF widget names within the same component, leading to debugfs warnings: debugfs: 'Playback' already exists in 'dapm' debugfs: 'Capture' already exists in 'dapm' Give the wideband DAI distinct stream names ("WB Playback" and "WB Capture") and add corresponding DAPM AIF widgets and routes for them. Fixes: 5947e1b4992e ("ASoC: bt-sco: extend rate and add a general compatible string") Assisted-by: VeroCoder:claude-sonnet-4-5 Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260715100620.1387159-1-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/bt-sco.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index 3afcef2dfa35..c0bf45b76cb8 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -17,11 +17,17 @@ static const struct snd_soc_dapm_widget bt_sco_widgets[] = { SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("BT_SCO_TX", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("BT_SCO_RX_WB", "WB Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("BT_SCO_TX_WB", "WB Capture", 0, + SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_dapm_route bt_sco_routes[] = { { "BT_SCO_TX", NULL, "RX" }, { "TX", NULL, "BT_SCO_RX" }, + { "BT_SCO_TX_WB", NULL, "RX" }, + { "TX", NULL, "BT_SCO_RX_WB" }, }; static struct snd_soc_dai_driver bt_sco_dai[] = { @@ -45,14 +51,14 @@ static struct snd_soc_dai_driver bt_sco_dai[] = { { .name = "bt-sco-pcm-wb", .playback = { - .stream_name = "Playback", + .stream_name = "WB Playback", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { - .stream_name = "Capture", + .stream_name = "WB Capture", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, -- cgit v1.2.3 From c1f3e770eec26d6f96dd6d2ea30555ba7c09a244 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 23 Jun 2026 13:23:46 -0700 Subject: fortify: Disable -Wstringop-overread in tests clang recently added support for -Wstringop-overread [1], which is on by default like -Wfortify-source. This breaks the usage of -Werror in the fortify tests, resulting in the following false positive warnings in the kernel build: warning: unsafe memcmp() usage lacked '__read_overflow2' warning in lib/test_fortify/read_overflow2-memcmp.c warning: unsafe memcmp() usage lacked '__read_overflow' warning in lib/test_fortify/read_overflow-memcmp.c warning: unsafe memchr() usage lacked '__read_overflow' warning in lib/test_fortify/read_overflow-memchr.c Examining the fortify test logs shows a warning like the following in each of the failed logs: In file included from lib/test_fortify/read_overflow2-memcmp.c:5: lib/test_fortify/test_fortify.h:34:2: error: 'memcmp' reading 17 bytes from a region of size 16 [-Werror,-Wstringop-overread] 34 | TEST; | ^ lib/test_fortify/read_overflow2-memcmp.c:3:2: note: expanded from macro 'TEST' 3 | memcmp(large, small, sizeof(small) + 1) | ^ 1 error generated. Disable -Wstringop-overread for the fortify tests, as it defeats the purpose of testing the Linux specific implementation of fortify, like -Wfortify-source. Cc: stable@vger.kernel.org Closes: https://github.com/ClangBuiltLinux/linux/issues/2168 Link: https://github.com/llvm/llvm-project/commit/86f2e71cb8d165b59ad31a442b2391e23826133e [1] Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20260623-fix-test_fortify-for-clang-stringop-overread-v1-1-15ee8342a953@kernel.org Signed-off-by: Kees Cook --- lib/test_fortify/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/test_fortify/Makefile b/lib/test_fortify/Makefile index 399cae880e1d..44cd5df41a81 100644 --- a/lib/test_fortify/Makefile +++ b/lib/test_fortify/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 ccflags-y := $(call cc-disable-warning,fortify-source) +ccflags-y += $(call cc-disable-warning,stringop-overread) quiet_cmd_test_fortify = TEST $@ cmd_test_fortify = $(CONFIG_SHELL) $(src)/test_fortify.sh \ -- cgit v1.2.3 From 5caae1deee89a6582c761d5dcd4b924b744426cc Mon Sep 17 00:00:00 2001 From: Mark Harris Date: Mon, 13 Jul 2026 17:30:56 -0700 Subject: riscv: hwprobe: Avoid uninitialized read in hwprobe_get_cpus() When cpusetsize < cpumask_size(), hwprobe_get_cpus() did not fully initialize its copy of the cpu mask, which could cause non-deterministic results from the riscv_hwprobe syscall on a system with more than 8 CPUs when the supplied cpu mask is empty. Address this by fully initializing the cpu mask. Fixes: e178bf146e4b ("RISC-V: hwprobe: Introduce which-cpus flag") Signed-off-by: Mark Harris Reviewed-by: Nam Cao Reviewed-by: Michael Ellerman Link: https://patch.msgid.link/20260714003056.73707-1-mark.hsj@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/sys_hwprobe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c index 1659d31fd288..caf6762427c8 100644 --- a/arch/riscv/kernel/sys_hwprobe.c +++ b/arch/riscv/kernel/sys_hwprobe.c @@ -450,6 +450,7 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs, if (cpusetsize > cpumask_size()) cpusetsize = cpumask_size(); + cpumask_clear(&cpus); ret = copy_from_user(&cpus, cpus_user, cpusetsize); if (ret) return -EFAULT; -- cgit v1.2.3 From 691b052139c94ee6640ac39e0b764dd3867897c0 Mon Sep 17 00:00:00 2001 From: Mike Waychison Date: Wed, 15 Jul 2026 15:29:50 -0400 Subject: block: fix race in blk_time_get_ns() returning 0 blk_time_get_ns() populates the per-plug cached timestamp and then returns it by re-reading the field: if (!plug->cur_ktime) { plug->cur_ktime = ktime_get_ns(); current->flags |= PF_BLOCK_TS; } return plug->cur_ktime; This is problematic when the compiler emits the final "return plug->cur_ktime" as a reload from memory, after PF_BLOCK_TS has already been set. Since the cached timestamp is now invalidated from finish_task_switch() (fad156c2af22 "block: invalidate cached plug timestamp after task switch"), a task preempted between setting PF_BLOCK_TS and that reload has plug->cur_ktime zeroed by blk_plug_invalidate_ts() when it is scheduled back in. The reload then returns 0. A 0 handed back here is stored as a start timestamp -- e.g. blk_account_io_start() writes it to rq->start_time_ns -- and later subtracted from "now". blk_account_io_done() then adds (now - 0), i.e. roughly the system uptime, to the per-group nsecs[] counters. On an otherwise idle, healthy device this appears as sudden ~uptime-sized jumps in the diskstats time fields (write_ticks/discard_ticks/time_in_queue). The solution is to be explicit in our reads and writes to this field that is preemption volatile. We also add a barrier() to ensure that any setting of PF_BLOCK_TS is ordered to happen after the cur_ktime update. This issue was discovered using AI-assisted kprobes looking for paths that were leaking zeroed timestamps in a live system, based on the observation that we were sometimes seeing uptime-sized jumps in kernel exported counters. This was flagged by NodeDiskIOSaturation prometheus alerts that started firing on all hosts post 7.1.3 kernel upgrade, due to node-exporter now exporting a nonsensical node_disk_io_time_weighted_seconds_total. Fixes: fad156c2af22 ("block: invalidate cached plug timestamp after task switch") Cc: stable@vger.kernel.org Signed-off-by: Mike Waychison Assisted-by: Claude:claude-opus-4.8 Link: https://patch.msgid.link/20260715192950.2488921-1-mike@waychison.com Signed-off-by: Jens Axboe --- block/blk.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/block/blk.h b/block/blk.h index fb95d3c58950..eaac05815cb0 100644 --- a/block/blk.h +++ b/block/blk.h @@ -717,6 +717,7 @@ static inline int req_ref_read(struct request *req) static inline u64 blk_time_get_ns(void) { struct blk_plug *plug = current->plug; + u64 now; if (!plug || !in_task()) return ktime_get_ns(); @@ -725,12 +726,18 @@ static inline u64 blk_time_get_ns(void) * 0 could very well be a valid time, but rather than flag "this is * a valid timestamp" separately, just accept that we'll do an extra * ktime_get_ns() if we just happen to get 0 as the current time. + * + * cur_ktime can be zeroed by pre-emption the moment PF_BLOCK_TS is set. */ - if (!plug->cur_ktime) { - plug->cur_ktime = ktime_get_ns(); + now = READ_ONCE(plug->cur_ktime); + if (!now) { + now = ktime_get_ns(); + WRITE_ONCE(plug->cur_ktime, now); + /* Ensure PF_BLOCK_TS is set after cur_ktime. */ + barrier(); current->flags |= PF_BLOCK_TS; } - return plug->cur_ktime; + return now; } static inline ktime_t blk_time_get(void) -- cgit v1.2.3 From c4f4c0fc551cbcdccbbbc2d874d7d6440c7b4983 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 15 Jul 2026 21:39:57 +0200 Subject: loop: remove manually added partitions on detach Commit 267ec4d7223a ("loop: fix partition scan race between udev and loop_reread_partitions()") stopped disk_force_media_change() from setting GD_NEED_PART_SCAN because loop devices with LO_FLAGS_PARTSCAN rescan partitions explicitly. However, partitions can also be added manually with BLKPG while LO_FLAGS_PARTSCAN is clear. When such a loop device is detached, __loop_clr_fd() skips bdev_disk_changed(). Without GD_NEED_PART_SCAN, reopening the unbound device no longer performs the previous lazy cleanup, leaving dead partition devices behind. A subsequent LOOP_CONFIGURE can then fail its partition scan with -EBUSY, as seen in blktests loop/009 after loop/008. Call bdev_disk_changed() unconditionally during __loop_clr_fd(). The disk capacity is already zero and the release path holds open_mutex, so this drops all partitions without rescanning the detached backing file. The new blktests loop/013 case covers this sequence by adding a partition with BLKPG without LO_FLAGS_PARTSCAN, detaching the loop device, and checking that the partition is gone when the device is reopened. Fixes: 267ec4d7223a ("loop: fix partition scan race between udev and loop_reread_partitions()") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202607150754.b660f5b9-lkp@intel.com Signed-off-by: Daan De Meyer Link: https://patch.msgid.link/20260715-b4-loop-partition-cleanup-v1-1-b9f59910cd1e@amutable.com Signed-off-by: Jens Axboe --- drivers/block/loop.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 310de0463beb..1faecef33009 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1113,6 +1113,7 @@ static void __loop_clr_fd(struct loop_device *lo) struct queue_limits lim; struct file *filp; gfp_t gfp = lo->old_gfp_mask; + int err; spin_lock_irq(&lo->lo_lock); filp = lo->lo_backing_file; @@ -1146,26 +1147,21 @@ static void __loop_clr_fd(struct loop_device *lo) disk_force_media_change(lo->lo_disk); - if (lo->lo_flags & LO_FLAGS_PARTSCAN) { - int err; - - /* - * open_mutex has been held already in release path, so don't - * acquire it if this function is called in such case. - * - * If the reread partition isn't from release path, lo_refcnt - * must be at least one and it can only become zero when the - * current holder is released. - */ - err = bdev_disk_changed(lo->lo_disk, false); - if (err) - pr_warn("%s: partition scan of loop%d failed (rc=%d)\n", - __func__, lo->lo_number, err); - /* Device is gone, no point in returning error */ - } + /* + * Remove all partitions, including partitions added manually with + * BLKPG, which may exist even if LO_FLAGS_PARTSCAN is not set. + * + * open_mutex has been held already in release path, so don't acquire + * it here. + */ + err = bdev_disk_changed(lo->lo_disk, false); + if (err) + pr_warn("%s: partition scan of loop%d failed (rc=%d)\n", + __func__, lo->lo_number, err); + /* Device is gone, no point in returning error */ /* - * lo->lo_state is set to Lo_unbound here after above partscan has + * lo->lo_state is set to Lo_unbound here after removing partitions has * finished. There cannot be anybody else entering __loop_clr_fd() as * Lo_rundown state protects us from all the other places trying to * change the 'lo' device. -- cgit v1.2.3 From dbbca20764382b4d411ec2918f4e278ffe547acc Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Wed, 15 Jul 2026 21:24:07 +0800 Subject: blk-cgroup: fix leaks and online flag on radix_tree_insert failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When radix_tree_insert() fails in blkg_create(), the error path has two issues: 1. blkg->online is set to true unconditionally, even when the blkg was never fully inserted. Move the assignment inside the success block. 2. The error path calls blkg_put() without first calling percpu_ref_kill(). Because the refcount is still in percpu mode, percpu_ref_put() only does this_cpu_sub() without checking for zero, so blkg_release() is never triggered. This permanently leaks the blkg memory, its percpu iostat, policy data, the parent blkg reference, and the cgroup css reference — the latter preventing the cgroup from ever being destroyed. Fix by replacing blkg_put() with percpu_ref_kill(), matching the pattern used in blkg_destroy(). Acked-by: Tejun Heo Signed-off-by: Tao Cui Link: https://patch.msgid.link/20260715132407.1469777-1-cui.tao@linux.dev Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index d2a1f5903f24..d9676126c5b5 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -434,15 +434,15 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk, blkg->pd[i]->online = true; } } + blkg->online = true; } - blkg->online = true; spin_unlock(&blkcg->lock); if (!ret) return blkg; /* @blkg failed fully initialized, use the usual release path */ - blkg_put(blkg); + percpu_ref_kill(&blkg->refcnt); return ERR_PTR(ret); err_free_blkg: -- cgit v1.2.3 From d35e236282992e583ed9c1b7ab52e9ef1b873585 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 14 Jul 2026 07:48:06 -0400 Subject: partitions: aix: bound the lvd scan to one sector aix_partition() reads the logical-volume descriptor array as a single sector and then scans it: if (numlvs && (d = read_part_sector(state, vgda_sector + 1, §))) { struct lvd *p = (struct lvd *)d; ... for (i = 0; foundlvs < numlvs && i < state->limit; i++) { lvip[i].pps_per_lv = be16_to_cpu(p[i].num_lps); p points at a single 512-byte sector, which holds SECTOR_SIZE / sizeof(struct lvd) = 16 entries, but the loop runs until foundlvs reaches the on-disk numlvs or i reaches state->limit (DISK_MAX_PARTS, 256). numlvs is an on-disk __be16 read straight from the volume group descriptor and is not validated, so a crafted AIX image with numlvs larger than 16 and lvd entries whose num_lps fields are zero (so foundlvs never advances) drives the loop to read p[i] well past the end of the read sector buffer. Commit d97a86c170b4 ("partitions: aix.c: off by one bug") hardened the matching write of lvip[lv_ix] in 2014 but left this read loop unbounded. Bound the scan to the number of struct lvd entries that fit in the sector that was actually read. Fixes: 6ceea22bbbc8 ("partitions: add aix lvm partition support files") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260714114806.3761553-1-michael.bommarito@gmail.com Signed-off-by: Jens Axboe --- block/partitions/aix.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/block/partitions/aix.c b/block/partitions/aix.c index f3c4174e003e..689837deba27 100644 --- a/block/partitions/aix.c +++ b/block/partitions/aix.c @@ -208,7 +208,14 @@ int aix_partition(struct parsed_partitions *state) if (n) { int foundlvs = 0; - for (i = 0; foundlvs < numlvs && i < state->limit; i += 1) { + /* + * The lvd array was read as a single sector; only the + * struct lvd entries that fit in it are valid. Bound the + * scan so an on-disk numlvs larger than that cannot walk + * the read buffer out of bounds. + */ + for (i = 0; foundlvs < numlvs && i < state->limit && + i < SECTOR_SIZE / (int)sizeof(struct lvd); i++) { lvip[i].pps_per_lv = be16_to_cpu(p[i].num_lps); if (lvip[i].pps_per_lv) foundlvs += 1; -- cgit v1.2.3 From daff723f2d4c618d5f9186a990f47bf75878abeb Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 14 Jul 2026 18:37:39 +0930 Subject: block: do not warn when doing greedy allocation in folio_alloc_greedy() During one of my local btrfs fstests runs, folio_alloc() inside folio_alloc_greedy() triggered an allocation failure report when trying to allocate an order-4 folio. The kernel is from the latest development branch, which is utilizing the IOMAP_DIO_BOUNCE flag for direct writes when the inode requires checksum. Unfortunately I didn't save the full log, only the function and the order. When the IOMAP_DIO_BOUNCE flag is utilized, we will hit the following call chain: bio_iov_iter_bounce_write() |- folio_alloc_greedy() |- folio_alloc(gfp | __GFP_NORETRY, get_order(*size)); However __GFP_NORETRY will still emit an allocation failure report when it fails. And folio_alloc_greedy() will retry with a smaller order anyway, there is no point in emitting that allocation failure report. Append the __GFP_NOWARN flag to folio_alloc() for the larger-order folio attempts. Fixes: 8dd5e7c75d7b ("block: add helpers to bounce buffer an iov_iter into bios") Signed-off-by: Qu Wenruo Link: https://patch.msgid.link/d10571445ee505d95ba6eaad7558fc1f556d2921.1784020005.git.wqu@suse.com Signed-off-by: Jens Axboe --- block/bio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/bio.c b/block/bio.c index f2a5f4d0a967..5ac954c70dd0 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1285,7 +1285,8 @@ static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size, struct folio *folio; while (*size > minsize) { - folio = folio_alloc(gfp | __GFP_NORETRY, get_order(*size)); + folio = folio_alloc(gfp | __GFP_NORETRY | __GFP_NOWARN, + get_order(*size)); if (folio) return folio; *size = rounddown_pow_of_two(*size - 1); -- cgit v1.2.3 From b3a7aa9c0020ae549a0d4964867ff66d2bd61709 Mon Sep 17 00:00:00 2001 From: Haofeng Li Date: Wed, 15 Jul 2026 16:02:10 +0800 Subject: selftests/lkdtm: rename STACKLEAK_ERASING to KSTACK_ERASE Commit 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE") renamed the LKDTM crash type and selftest configuration but missed the entry in tests.txt. As a result, the selftest generates STACKLEAK_ERASING.sh, which run.sh skips because the LKDTM DIRECT trigger only exposes KSTACK_ERASE. Rename the test entry so the generated runner uses the registered crash type. Fixes: 57fbad15c2ee ("stackleak: Rename STACKLEAK to KSTACK_ERASE") Signed-off-by: Haofeng Li Link: https://patch.msgid.link/tencent_CD80B5F746B6AABD68AF3F1097AD02C96F05@qq.com Signed-off-by: Kees Cook --- tools/testing/selftests/lkdtm/tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index d8180bbe31e8..bec57a02913a 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -78,7 +78,7 @@ USERCOPY_STACK_FRAME_TO USERCOPY_STACK_FRAME_FROM USERCOPY_STACK_BEYOND USERCOPY_KERNEL -STACKLEAK_ERASING OK: the rest of the thread stack is properly erased +KSTACK_ERASE OK: the rest of the thread stack is properly erased CFI_FORWARD_PROTO CFI_BACKWARD call trace:|ok: control flow unchanged FORTIFY_STRSCPY detected buffer overflow -- cgit v1.2.3 From 3421b9b056a6576d0ebac1030eafb48ad0544092 Mon Sep 17 00:00:00 2001 From: Kuan-Ying Lee Date: Wed, 15 Jul 2026 13:35:52 +0800 Subject: selftests/seccomp: Fix pointer type mismatch build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We hit the following build error while running the seccomp selftests in our testing. CC seccomp_bpf seccomp_bpf.c: In function ‘UPROBE_setup’: seccomp_bpf.c:5175:74: error: pointer type mismatch in conditional expression [-Wincompatible-pointer-types] 5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); | ^ seccomp_bpf.c:5175:57: note: first expression has type ‘int (*)(void)’ 5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); | ^~~~~~~~~~~~~~~~ seccomp_bpf.c:5175:76: note: second expression has type ‘int (__attribute__((nocf_check)) *)(void)’ 5175 | offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); | ^~~~~~~~~~~~~ get_uprobe_offset() takes a 'const void *' argument, so cast both operands to 'void *'. Fixes: 9ffc7a635c35 ("selftests/seccomp: validate uprobe syscall passes through seccomp") Signed-off-by: Kuan-Ying Lee Acked-by: Jiri Olsa Link: https://patch.msgid.link/20260715053559.28535-1-kuan-ying.lee@canonical.com Signed-off-by: Kees Cook --- tools/testing/selftests/seccomp/seccomp_bpf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 358b6c65e120..0622bc2acad4 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -5178,7 +5178,8 @@ FIXTURE_SETUP(UPROBE) ASSERT_GE(bit, 0); } - offset = get_uprobe_offset(variant->uretprobe ? probed_uretprobe : probed_uprobe); + offset = get_uprobe_offset(variant->uretprobe ? (void *)probed_uretprobe + : (void *)probed_uprobe); ASSERT_GE(offset, 0); if (variant->uretprobe) -- cgit v1.2.3 From 4416f8a9ed5f49256dc69c664f1386b496ca16a1 Mon Sep 17 00:00:00 2001 From: Jackie Liu Date: Thu, 16 Jul 2026 09:26:06 +0800 Subject: liveupdate: fix GET_NAME ioctl argument validation LIVEUPDATE_SESSION_GET_NAME was developed in the liveupdate/next branch while the session type validation change was carried in liveupdate-fixes. When the conflict between the two branches was resolved, the GET_NAME operation descriptor picked up the structure and last member from RETRIEVE_FD. This makes both its known size and minimum size 16 bytes rather than 72. A zero-initialized request still succeeds because luo_session_get_name() writes the full name before luo_ucmd_respond() copies the full GET_NAME response to userspace. However, copy_struct_from_user() treats the output-only name field as unknown trailing data and rejects the request with -E2BIG if any byte in that field is nonzero. Use the GET_NAME structure and its name field in the descriptor. Link: https://lore.kernel.org/all/ahWlYXNjGUbkKoHy@sirena.org.uk/ Assisted-by: Codex:gpt-5.6-sol Reviewed-by: Pratyush Yadav (Google) Signed-off-by: Jackie Liu Link: https://patch.msgid.link/20260716012607.22020-1-liu.yun@linux.dev Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/luo_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index b79b2a488974..f38b5b18f3f8 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -378,7 +378,7 @@ static const struct luo_ioctl_op luo_session_ioctl_ops[] = { IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd, struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_INCOMING), IOCTL_OP(LIVEUPDATE_SESSION_GET_NAME, luo_session_get_name, - struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_ALL), + struct liveupdate_session_get_name, name, LUO_IOCTL_ALL), }; static bool luo_ioctl_type_valid(struct luo_session *session, -- cgit v1.2.3 From a3d6d3cedfe87bbd5a677d52b22ac20d28e59cf8 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 15 Jul 2026 21:06:10 +0300 Subject: ALSA: hda: codecs: hdmi: disable keep-alive before audio format change When a keep-alive (KAE) silent stream is active on an Intel HDMI/DP codec, opening a real PCM stream reprograms the converter format and the audio infoframe in snd_hda_hdmi_generic_pcm_prepare(). Part of that reprogramming - the converter channel count and the channel mapping in snd_hda_hdmi_setup_audio_infoframe() - is not safe to do while a keep-alive stream is active. This is most visible when switching to a multichannel PCM configuration, where the active channel count actually changes. In that case the newly opened PCM stream plays no sound. Add an optional hdmi_ops .prepare hook, called at the start of the PCM prepare sequence (before the format and infoframe are touched), and implement it for HSW+ to release keep-alive. Keep-alive is then re-enabled as before once the new stream has been set up, in the setup_stream op. Fixes: 15175a4f2bbb ("ALSA: hda/hdmi: add keep-alive support for ADL-P and DG2") Reported-by: Alexander Kaplan Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8412 Tested-by: Alexander Kaplan Cc: Signed-off-by: Kai Vehmanen Link: https://patch.msgid.link/20260715180610.1371243-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/hdmi/hdmi.c | 3 +++ sound/hda/codecs/hdmi/hdmi_local.h | 9 +++++++++ sound/hda/codecs/hdmi/intelhdmi.c | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 1f4d646724ed..a4a2f2f0540c 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -1688,6 +1688,9 @@ int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo, per_pin->channels = substream->runtime->channels; per_pin->setup = true; + if (spec->ops.prepare) + spec->ops.prepare(codec, per_pin); + if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) { stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core, substream); diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h index 548241ad3fa9..dce9a335cff2 100644 --- a/sound/hda/codecs/hdmi/hdmi_local.h +++ b/sound/hda/codecs/hdmi/hdmi_local.h @@ -74,6 +74,15 @@ struct hdmi_ops { hda_nid_t pin_nid, int dev_id, u32 stream_tag, int format); + /* + * Optional hook invoked at the beginning of the PCM prepare + * sequence, before the audio infoframe and stream format are + * (re)programmed. Used to disable keep-alive / silent stream so + * that the format change is not done while keep-alive is active. + */ + void (*prepare)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin); + void (*pin_cvt_fixup)(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid); diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c index 6a7882544ab7..73141046d502 100644 --- a/sound/hda/codecs/hdmi/intelhdmi.c +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -418,6 +418,28 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx); } +/* + * prepare ops override for HSW+ + * + * Disable keep-alive before the converter format and audio infoframe are + * reprogrammed by the PCM prepare sequence. Changing the audio format (e.g. + * the channel count when switching to multichannel PCM) while a keep-alive + * stream is active is not safe, so release keep-alive here, early in the + * sequence. It is re-enabled once the new stream has been set up, in + * i915_hsw_setup_stream(). + */ +static void i915_hsw_prepare(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin->silent_stream) { + silent_stream_set_kae(codec, per_pin, false); + /* wait for pending transfers in codec to clear */ + usleep_range(100, 200); + } +} + /* setup_stream ops override for HSW+ */ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, int dev_id, u32 stream_tag, @@ -435,15 +457,16 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, haswell_verify_D0(codec, cvt_nid, pin_nid); - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - silent_stream_set_kae(codec, per_pin, false); - /* wait for pending transfers in codec to clear */ - usleep_range(100, 200); - } - res = snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, stream_tag, format); + /* + * Keep-alive was disabled in i915_hsw_prepare(), re-enable it now. + * The pin lookup above resolves to the same per_pin that prepare + * used (pin_nid comes from that per_pin), so this stays balanced; a + * NULL per_pin only occurs on a lookup failure that also implies no + * active keep-alive stream to restore. + */ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { usleep_range(100, 200); silent_stream_set_kae(codec, per_pin, true); @@ -607,6 +630,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, codec->depop_delay = 0; codec->auto_runtime_pm = 1; + spec->ops.prepare = i915_hsw_prepare; spec->ops.setup_stream = i915_hsw_setup_stream; spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; spec->ops.silent_stream = i915_set_silent_stream; -- cgit v1.2.3 From ac1e8d01f4ea0d4a5739c6f40b07484c6385cfbb Mon Sep 17 00:00:00 2001 From: Chris Chiu Date: Thu, 16 Jul 2026 10:38:25 +0800 Subject: ALSA: hda/realtek: Add inverted LED quirk for HP ZBook 8 G2a HP ZBook 8 G2a 14 and 16 (SSIDs 0x103c:0x8f94, 0x103c:0x8f95) use the Realtek ALC245 codec with a TAS2781 amplifier via I2C. They share the same hardware configuration as the existing 0x8f40/0x8f41/0x8f42/0x8f62 models but have inverted speaker mute LED polarity, so the existing ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED quirk drives the LED backwards: off when muted and on when unmuted. Add a dedicated quirk with inverted COEF values. These are speaker-only models without an HP pin, so the LED is driven directly through the vmaster_mute hook; there is no need to probe the HP pin at runtime since the configuration is static and known per SSID. Signed-off-by: Chris Chiu Link: https://patch.msgid.link/20260716023825.387532-1-chris.chiu@canonical.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index e3958310af89..92feb2df0007 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3773,6 +3773,15 @@ static void alc245_hp_spk_mute_led_update(void *private_data, int enabled) alc_update_coef_idx(codec, 0x0b, 0x0c, val); } +static void alc245_hp_spk_mute_led_update_inverted(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + unsigned int val; + + val = enabled ? 0x04 : 0x08; /* inverted: 0x04 led off, 0x08 led on */ + alc_update_coef_idx(codec, 0x0b, 0x0c, val); +} + /* JD2: mute led GPIO3: micmute led */ static void alc245_tas2781_i2c_hp_fixup_muteled(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -3801,6 +3810,33 @@ static void alc245_tas2781_i2c_hp_fixup_muteled(struct hda_codec *codec, alc245_fixup_hp_mute_led_coefbit(codec, fix, action); alc285_fixup_hp_coef_micmute_led(codec, fix, action); } + +/* Same as alc245_tas2781_i2c_hp_fixup_muteled(), but with inverted speaker + * mute LED polarity. The HP ZBook 8 G2a 14/16 (0x103c:0x8f94, 0x103c:0x8f95) + * are speaker-only models without an HP pin, so the LED is driven directly + * through the vmaster_mute hook. + */ +static void alc245_tas2781_i2c_hp_fixup_muteled_inverted(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const hda_nid_t conn[] = { 0x02 }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.vmaster_mute.hook = alc245_hp_spk_mute_led_update_inverted; + spec->gen.vmaster_mute_led = 1; + spec->gen.auto_mute_via_amp = 1; + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + case HDA_FIXUP_ACT_INIT: + alc245_hp_spk_mute_led_update_inverted(codec, !spec->gen.master_mute); + break; + } + + tas2781_fixup_txnw_i2c(codec, fix, action); + alc285_fixup_hp_coef_micmute_led(codec, fix, action); +} /* * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly * at PM resume @@ -4163,6 +4199,7 @@ enum { ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE, ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED, ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED, + ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED, ALC288_FIXUP_SURFACE_SWAP_DACS, ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO, ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY, @@ -6734,6 +6771,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc245_tas2781_i2c_hp_fixup_muteled, }, + [ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_tas2781_i2c_hp_fixup_muteled_inverted, + }, [ALC288_FIXUP_SURFACE_SWAP_DACS] = { .type = HDA_FIXUP_FUNC, .v.func = alc288_fixup_surface_swap_dacs, @@ -7384,6 +7425,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f57, "HP Trekker G7JC", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8f94, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), + SND_PCI_QUIRK(0x103c, 0x8f95, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x1043, 0x1024, "ASUS Zephyrus G14 2025", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1032, "ASUS VivoBook X513EA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), -- cgit v1.2.3 From 25f744ffa0c8e799e06250ce2e618367b166b0d4 Mon Sep 17 00:00:00 2001 From: Nikunj A Dadhania Date: Wed, 15 Jul 2026 06:35:06 +0000 Subject: 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; -- cgit v1.2.3 From e4e8af62adab2fdcca230006f829407a953070cd Mon Sep 17 00:00:00 2001 From: Shuhao Fu Date: Thu, 7 May 2026 10:22:26 +0200 Subject: 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 ... */ -- cgit v1.2.3 From d83762005c13269fffa51620399fc8d09095c4f7 Mon Sep 17 00:00:00 2001 From: Stéphane Grosjean Date: Thu, 7 May 2026 10:22:27 +0200 Subject: 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 -- cgit v1.2.3 From 68973f9db76144825e4f35dfdc80fb8279eb2d57 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2026 18:55:23 +0200 Subject: 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 @@ register_proto_failed: 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); -- cgit v1.2.3 From d9b091d9d22fee81ec53fb55d2032951993ceadb Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:24 +0200 Subject: 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); -- cgit v1.2.3 From 749179c2e25b95d22499ed29096b3e02d6dfd2b4 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:25 +0200 Subject: 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 | 176 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 133 insertions(+), 43 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 @@ free_op: 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, + /* get new CAN frames content before locking */ + new_frames = kmalloc(msg_head->nframes * op->cfsiz, + GFP_KERNEL); + if (!new_frames) + return -ENOMEM; + + err = memcpy_from_msg(new_frames, msg, msg_head->nframes * op->cfsiz); - if (err < 0) + if (err < 0) { + kfree(new_frames); return err; + } - /* clear last_frames to indicate 'nothing received' */ - memset(op->last_frames, 0, msg_head->nframes * op->cfsiz); + 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) -- cgit v1.2.3 From e6c24ba95fc3f1b5e1dcd28b1c6e59ef61a9daa5 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:26 +0200 Subject: 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 | 69 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 23 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) + 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); + 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, &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, -- cgit v1.2.3 From 7b2c3eabc4dafc062a25e10711154f2107526a78 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:27 +0200 Subject: 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) { -- cgit v1.2.3 From 12ce799f7ab1e05bd8fbf79e46f403bfe5597ebc Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:28 +0200 Subject: 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 | 104 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 29 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,36 +1102,38 @@ 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); + op->flags & TX_RESET_MULTI_IDX) { + /* potentially update changed nframes */ + op->nframes = msg_head->nframes; + /* restart multiple frame transmission */ + op->currframe = 0; + } - 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; - if (op->flags & SETTIMER) - op->count = msg_head->count; + spin_unlock_bh(&op->bcm_tx_lock); - 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) -- cgit v1.2.3 From 62ec41f364648be79d54d94d0d240ee326948afd Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:29 +0200 Subject: 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 @@ free_op: 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; } /* -- cgit v1.2.3 From d59948293ea34b6337ce2b5febab8510de70048c Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:30 +0200 Subject: 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), -- cgit v1.2.3 From 3b762c0d950383ab7a002686c9136b9aa55d2d70 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:31 +0200 Subject: 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) -- cgit v1.2.3 From 58fd6cbc8541216af1d7ed272ea7ac2b66d50fd8 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:32 +0200 Subject: 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, -- cgit v1.2.3 From 2f5976f54a04e9f18b25283036ac3136be453b17 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 14 Jul 2026 18:55:33 +0200 Subject: 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. -- cgit v1.2.3 From 9b1a02e0d980ac6b0e36a90378f847062f81d7e4 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:41 +0200 Subject: 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); -- cgit v1.2.3 From 20bab8b88baac140ca3701116e1d486c7f51e311 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:42 +0200 Subject: 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 | 87 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 28 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; - - 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); - } - } + /* 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); + + 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; -- cgit v1.2.3 From cf070fe33bfbd1a4c21236078fadb35dd223a157 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Sun, 12 Jul 2026 19:59:43 +0200 Subject: 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); -- cgit v1.2.3 From 7dcf091da4b4596f7dbfb403d946ee4aac423af3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 15 Jul 2026 21:16:48 +0100 Subject: ASoC: fs210x: Make cache write through again during resume The fs210x driver currently puts the regmap cache into cache only mode during suspend but at no point does it make it writable again, meaning that device can't work after suspend other than basic powerup which bypasses the cache. Mark the cache as writable again when we resync the registers, it's not clear to me that the cache only mode is needed at all but this is a fairly minimal change. Since this is setting a flag it's safe to do even if the cache was not previously in cache only mode. Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-asoc-fs210x-fix-resume-v1-1-dd81d9c62c05@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/fs210x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c index 5f381fe063e8..d794ff918b6f 100644 --- a/sound/soc/codecs/fs210x.c +++ b/sound/soc/codecs/fs210x.c @@ -607,6 +607,7 @@ tag_power_down: regcache_cache_bypass(fs210x->regmap, false); if (!ret) { + regcache_cache_only(fs210x->regmap, false); regcache_mark_dirty(fs210x->regmap); regcache_sync(fs210x->regmap); fs210x->is_inited = true; -- cgit v1.2.3 From bd2df8dc72201f626d66ca97f2afc8b7f2740713 Mon Sep 17 00:00:00 2001 From: Jackie Liu Date: Wed, 15 Jul 2026 15:35:18 +0800 Subject: block: free copied pages when blk_rq_map_kern() fails bio_copy_kern() allocates pages that are normally freed by the bio completion callback. If blk_rq_append_bio() rejects the bio, however, blk_rq_map_kern() only drops the bio reference. Since bio_put() does not free pages referenced by the bio vectors, those pages leak. This can happen when the bio exceeds the queue segment constraints or when a later mapping cannot be merged into a request built by earlier calls. Track whether the buffer was copied and free those pages before dropping the rejected bio. Fixes: 3a5a39276d2a ("block: allow blk_rq_map_kern to append to requests") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Jackie Liu Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260715073518.96042-1-liu.yun@linux.dev Signed-off-by: Jens Axboe --- block/blk-map.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/block/blk-map.c b/block/blk-map.c index 768549f19f97..d1d6bbe0ecf1 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -653,6 +653,7 @@ int blk_rq_map_kern(struct request *rq, void *kbuf, unsigned int len, gfp_t gfp_mask) { unsigned long addr = (unsigned long) kbuf; + bool do_copy; struct bio *bio; int ret; @@ -661,7 +662,8 @@ int blk_rq_map_kern(struct request *rq, void *kbuf, unsigned int len, if (!len || !kbuf) return -EINVAL; - if (!blk_rq_aligned(rq->q, addr, len) || object_is_on_stack(kbuf)) + do_copy = !blk_rq_aligned(rq->q, addr, len) || object_is_on_stack(kbuf); + if (do_copy) bio = bio_copy_kern(rq, kbuf, len, gfp_mask); else bio = bio_map_kern(rq, kbuf, len, gfp_mask); @@ -670,8 +672,11 @@ int blk_rq_map_kern(struct request *rq, void *kbuf, unsigned int len, return PTR_ERR(bio); ret = blk_rq_append_bio(rq, bio); - if (unlikely(ret)) + if (unlikely(ret)) { + if (do_copy) + bio_free_pages(bio); blk_mq_map_bio_put(bio); + } return ret; } EXPORT_SYMBOL(blk_rq_map_kern); -- cgit v1.2.3 From 4ff58d6bc9dd4c7fe6092d0ab5677a79ebc788af Mon Sep 17 00:00:00 2001 From: Shin'ichiro Kawasaki Date: Thu, 16 Jul 2026 18:22:37 +0900 Subject: block: serialize elevator changes for the same queue using a writer lock When elevator_change() is called concurrently for the same queue, the elevator_change_done() function runs concurrently as well. This function adds or deletes kobjects for the debugfs entry of the queue. Then the concurrent calls cause memory corruption of the kobjects and result in a process hang. The core part of the elevator switch is protected by queue freeze and q->elevator_lock. However, since the commit 559dc11143eb ("block: move elv_register[unregister]_queue out of elevator_lock"), the elevator_change_done() is not serialized. Hence the memory corruption and the hang. The failures are observed when udev-worker writes to a sysfs queue/scheduler attribute file while the blktests test case block/005 writes to the same attribute file. The failure also can be recreated by running two processes that write to the same queue/scheduler file concurrently. The failure is observed since another commit 370ac285f23a ("block: avoid cpu_hotplug_lock depedency on freeze_lock"). This commit changed the behavior of queue freeze and it unveiled the failure. Fix the failure by changing elv_iosched_store() to acquire update_nr_hwq_lock as the writer lock instead of the reader lock. This serializes the whole elevator switch steps, including the elevator_change_done() call. Fixes: 559dc11143eb ("block: move elv_register[unregister]_queue out of elevator_lock") Signed-off-by: Shin'ichiro Kawasaki Reviewed-by: Nilay Shroff Reviewed-by: Ming Lei Link: https://patch.msgid.link/20260716092237.1305030-1-shinichiro.kawasaki@wdc.com Signed-off-by: Jens Axboe --- block/elevator.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/block/elevator.c b/block/elevator.c index 3bcd37c2aa34..2161b6eea680 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -812,8 +812,13 @@ ssize_t elv_iosched_store(struct gendisk *disk, const char *buf, * reference during concurrent disk deletion: * update_nr_hwq_lock -> kn->active (via del_gendisk -> kobject_del) * kn->active -> update_nr_hwq_lock (via this sysfs write path) + * + * Use the writer lock instead of the reader lock of update_nr_hwq_lock + * to serialize the two-stage elevator switch steps in + * elevator_change(): the core switch step under the elevator lock and + * the elevator_change_done() step outside the elevator lock. */ - if (!down_read_trylock(&set->update_nr_hwq_lock)) { + if (!down_write_trylock(&set->update_nr_hwq_lock)) { ret = -EBUSY; goto out; } @@ -824,7 +829,7 @@ ssize_t elv_iosched_store(struct gendisk *disk, const char *buf, } else { ret = -ENOENT; } - up_read(&set->update_nr_hwq_lock); + up_write(&set->update_nr_hwq_lock); out: if (ctx.type) -- cgit v1.2.3 From f94de432646ebec7857ba2624fe082e779b52572 Mon Sep 17 00:00:00 2001 From: Jackie Liu Date: Wed, 15 Jul 2026 15:33:41 +0800 Subject: block: show operation in error injection rules Rules listed through the error_injection debugfs file omit the block operation they match. As a result, rules that differ only in operation are indistinguishable even though op is mandatory when adding a rule. Signed-off-by: Jackie Liu Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260715073341.95129-1-liu.yun@linux.dev Signed-off-by: Jens Axboe --- block/error-injection.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/block/error-injection.c b/block/error-injection.c index cfb83138960c..e14bc4b723ef 100644 --- a/block/error-injection.c +++ b/block/error-injection.c @@ -276,9 +276,10 @@ static int blk_error_injection_show(struct seq_file *s, void *private) rcu_read_lock(); list_for_each_entry_rcu(inj, &disk->error_injection_list, entry) { - seq_printf(s, "%llu:%llu status=%s,chance=%u", - inj->start, inj->end, - blk_status_to_tag(inj->status), inj->chance); + seq_printf(s, "%llu:%llu op=%s,status=%s,chance=%u", + inj->start, inj->end, + blk_op_str(inj->op), + blk_status_to_tag(inj->status), inj->chance); seq_putc(s, '\n'); } rcu_read_unlock(); -- cgit v1.2.3 From 447cfed6d700bfbd5a7120f8ea5821a0e1191667 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Thu, 9 Jul 2026 10:01:45 +0800 Subject: block: try slab allocation in bio_alloc_bioset() before mempool When the per-CPU bio cache is enabled but empty, bio_alloc_percpu_cache() returns NULL and bio_alloc_bioset() falls straight through to the mempool fallback: if (unlikely(!bio)) { if (!(saved_gfp & __GFP_DIRECT_RECLAIM)) return NULL; ... } For non-sleeping allocations (no __GFP_DIRECT_RECLAIM) this returns NULL without ever attempting a slab allocation, even when there is plenty of free memory. Commit b520c4eef83d ("block: split bio_alloc_bioset more clearly into a fast and slowpath") introduced this. Before it, a percpu cache miss fell through to mempool_alloc(), which attempted the underlying slab allocation first and only failed when that slab allocation failed. The restructuring dropped the slab attempt that non-sleeping callers of a cache-enabled bioset (such as the default fs_bio_set used by bio_alloc()) relied on. Try a slab allocation with optimistic GFP_ flags before falling back to the mempool whenever the bio is still NULL, so both the cache-empty and non-cache paths share the same slab attempt. This restores the previous behavior for non-sleeping allocations. Fixes: b520c4eef83d ("block: split bio_alloc_bioset more clearly into a fast and slowpath") Suggested-by: Christoph Hellwig Signed-off-by: Joseph Qi Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260709020145.4011533-1-joseph.qi@linux.alibaba.com Signed-off-by: Jens Axboe --- block/bio.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/block/bio.c b/block/bio.c index 5ac954c70dd0..de56d3f10c56 100644 --- a/block/bio.c +++ b/block/bio.c @@ -555,6 +555,14 @@ struct bio *bio_alloc_bioset(struct block_device *bdev, unsigned short nr_vecs, bio = bio_alloc_percpu_cache(bs); } else { opf &= ~REQ_ALLOC_CACHE; + } + + /* + * For a bioset without a percpu cache, or when the percpu cache was + * empty, try a slab allocation with optimistic GFP_ flags before + * falling back to the mempool. + */ + if (!bio) { p = kmem_cache_alloc(bs->bio_slab, gfp); if (p) bio = p + bs->front_pad; -- cgit v1.2.3 From 4f221efff9b32fb4206f8db240a2b35a7d27696d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 16 Jul 2026 11:12:45 +0200 Subject: block: handle huge zero folios in bio_free_folios When CONFIG_PERSISTENT_HUGE_ZERO_FOLIO is enabled, iomap_dio_zero() can add a huge zero folio to a zeroing bio, which needs special treatment in bio_free_folios by also checking is_huge_zero_folio() in addition to is_zero_folio(). Fixes: 8dd5e7c75d7b ("block: add helpers to bounce buffer an iov_iter into bios") Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260716091306.316625-2-hch@lst.de Signed-off-by: Jens Axboe --- block/bio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/bio.c b/block/bio.c index de56d3f10c56..ebb6d3e53ac3 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1311,7 +1311,7 @@ static void bio_free_folios(struct bio *bio) bio_for_each_bvec_all(bv, bio, i) { struct folio *folio = bvec_folio(bv); - if (!is_zero_folio(folio)) + if (!is_zero_folio(folio) && !is_huge_zero_folio(folio)) folio_put(folio); } } -- cgit v1.2.3 From df308a14585649f7683a560f5c94978ab4f8224d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 16 Jul 2026 11:12:46 +0200 Subject: block: fix aligning of bounced dio read bios bio_iov_iter_align_down expects the "normal" biovec layout from vector 0, while bio_iov_iter_bounce_read abuses vector 0 for a bounce buffer allocation. Pass an explicit bvec to bio_iov_iter_align_down to deal with this case to avoid a double unpin. Additionally we need to free the folio if no bio_vec could be added, and adjust the size of the first bio_vec that contains the bounce buffer when the I/O size is aligned down. Fixes: e7b8b3c5b2a6 ("block: align down bounces bios") Reported-by: 0wnerD1ed Signed-off-by: Christoph Hellwig Tested-by: 0wnerD1ed Reviewed-by: Keith Busch Link: https://patch.msgid.link/20260716091306.316625-3-hch@lst.de Signed-off-by: Jens Axboe --- block/bio.c | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/block/bio.c b/block/bio.c index ebb6d3e53ac3..6a2f6fc3413e 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1199,7 +1199,7 @@ void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter) * for the next iteration. */ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter, - unsigned len_align_mask) + struct bio_vec *bv, unsigned len_align_mask) { size_t nbytes = bio->bi_iter.bi_size & len_align_mask; @@ -1208,23 +1208,16 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter, iov_iter_revert(iter, nbytes); bio->bi_iter.bi_size -= nbytes; - do { - struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; - - if (nbytes < bv->bv_len) { - bv->bv_len -= nbytes; - break; - } - + while (nbytes >= bv->bv_len) { if (bio_flagged(bio, BIO_PAGE_PINNED)) unpin_user_page(bv->bv_page); - bio->bi_vcnt--; + if (!--bio->bi_vcnt) + return -EFAULT; nbytes -= bv->bv_len; - } while (nbytes); - - if (!bio->bi_vcnt) - return -EFAULT; + bv--; + } + bv->bv_len -= nbytes; return 0; } @@ -1284,7 +1277,8 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, if (is_pci_p2pdma_page(bio->bi_io_vec->bv_page)) bio->bi_opf |= REQ_NOMERGE; - return bio_iov_iter_align_down(bio, iter, len_align_mask); + return bio_iov_iter_align_down(bio, iter, + &bio->bi_io_vec[bio->bi_vcnt - 1], len_align_mask); } static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size, @@ -1369,7 +1363,8 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter, if (!bio->bi_iter.bi_size) return -ENOMEM; - return bio_iov_iter_align_down(bio, iter, minsize - 1); + return bio_iov_iter_align_down(bio, iter, + &bio->bi_io_vec[bio->bi_vcnt - 1], minsize - 1); } static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, @@ -1377,21 +1372,18 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, { size_t len = min3(iov_iter_count(iter), maxlen, SZ_1M); struct folio *folio; + ssize_t ret; folio = folio_alloc_greedy(GFP_KERNEL, &len, minsize); if (!folio) return -ENOMEM; do { - ssize_t ret; - ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len, &bio->bi_vcnt, bio->bi_max_vecs - 1, 0); if (ret <= 0) { - if (!bio->bi_vcnt) { - folio_put(folio); - return ret; - } + if (!bio->bi_vcnt) + goto out_folio_put; break; } len -= ret; @@ -1407,7 +1399,20 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, bvec_set_folio(&bio->bi_io_vec[0], folio, bio->bi_iter.bi_size, 0); if (iov_iter_extract_will_pin(iter)) bio_set_flag(bio, BIO_PAGE_PINNED); - return bio_iov_iter_align_down(bio, iter, minsize - 1); + + /* The first vec stores the bounce buffer, so do not subtract 1 here. */ + ret = bio_iov_iter_align_down(bio, iter, + &bio->bi_io_vec[bio->bi_vcnt], minsize - 1); + if (ret) + goto out_folio_put; + + /* Update the bounc buffer bv_len to the aligned down size. */ + bio->bi_io_vec[0].bv_len = bio->bi_iter.bi_size; + return 0; + +out_folio_put: + folio_put(folio); + return ret; } /** -- cgit v1.2.3 From e057b94772328221405b067c3a85fe479b915dc8 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 16 Jul 2026 13:06:39 +0100 Subject: arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates When seccomp support was originally added to arm64 in a1ae65b21941 ("arm64: add seccomp support"), seccomp was erroneously called _before_ the ptrace syscall-enter-stop and therefore the tracer could trivially manipulate the syscall register state after the seccomp check had passed. This was subsequently fixed in a5cd110cb836 ("arm64/ptrace: run seccomp after ptrace") by moving the seccomp check after the tracer has run. Unfortunately, a decade later, that fix has been reported to be incomplete. On arm64, both the first argument to a syscall and its eventual return value are allocated to register x0. In order to facilitate syscall restarting and querying of syscall arguments on the syscall exit path, the original value of x0 is stashed in 'struct pt_regs::orig_x0' early during the syscall entry path and is returned for the first argument by syscall_get_arguments(). Unlike 32-bit Arm, this stashed value is not directly exposed via ptrace() and so changes to register x0 made by the tracer on a syscall-enter-stop are not reflected in 'orig_x0'. This means that seccomp, syscall tracepoints and audit can observe a stale value for the register compared to the argument that will be observed by the actual syscall. Re-sync 'orig_x0' from x0 on the syscall entry path following a potential ptrace stop (i.e. PTRACE_EVENTMSG_SYSCALL_ENTRY or SECCOMP_RET_TRACE). This behaviour is limited to native tasks (because compat tasks expose 'orig_r0' to ptrace) where the syscall is not being skipped (because x0 is updated to hold the return value of -ENOSYS in that case). Cc: Kees Cook Cc: Jinjie Ruan Cc: Mark Rutland Cc: stable@vger.kernel.org Reported-by: Yiqi Sun Link: https://lore.kernel.org/all/20260529065444.1336608-1-sunyiqixm@gmail.com/ Suggested-by: Catalin Marinas Fixes: a5cd110cb836 ("arm64/ptrace: run seccomp after ptrace") Reviewed-by: Jinjie Ruan Tested-by: Jinjie Ruan Signed-off-by: Will Deacon --- arch/arm64/kernel/ptrace.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 4d08598e2891..390c9b2bd966 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -2408,6 +2408,21 @@ static void report_syscall_exit(struct pt_regs *regs) } } +static void update_syscall_orig_x0_after_ptrace(struct pt_regs *regs) +{ + /* + * Keep orig_x0 authoritative so that seccomp (via + * syscall_get_arguments()), audit and the restart path all see the same + * first argument the syscall is dispatched with, even if it has been + * updated by a tracer. Skip this for NO_SYSCALL (set either by the user + * or the tracer), as regs[0] holds the return value (see the comment in + * el0_svc_common()) and can be unwound using syscall_rollback(). + * For compat tasks, orig_r0 is provided directly through GPR index 17. + */ + if (!is_compat_task() && regs->syscallno != NO_SYSCALL) + regs->orig_x0 = regs->regs[0]; +} + int syscall_trace_enter(struct pt_regs *regs) { unsigned long flags = read_thread_flags(); @@ -2417,12 +2432,26 @@ int syscall_trace_enter(struct pt_regs *regs) ret = report_syscall_entry(regs); if (ret || (flags & _TIF_SYSCALL_EMU)) return NO_SYSCALL; + + /* + * Ensure ptrace changes to x0 during a regular + * syscall-enter-stop (PTRACE_SYSCALL) are visible to + * subsequent seccomp checks, tracepoints and audit. + */ + update_syscall_orig_x0_after_ptrace(regs); } /* Do the secure computing after ptrace; failures should be fast. */ if (secure_computing() == -1) return NO_SYSCALL; + /* + * Ensure tracer changes to x0 during seccomp ptrace exit + * processing (SECCOMP_RET_TRACE) are visible to tracepoints and + * audit. + */ + update_syscall_orig_x0_after_ptrace(regs); + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) trace_sys_enter(regs, regs->syscallno); -- cgit v1.2.3 From 9469764292e0d6825c9bf51d75682e3a623b9b6b Mon Sep 17 00:00:00 2001 From: Ben Horgan Date: Fri, 15 May 2026 09:58:25 +0100 Subject: arm_mpam: Fix software reset values of MPAMCFG_PRI Priority partitioning is not supported other than to set the per-PARTID defaults in MPAMCFG_PRI, INTPRI and DSPRI, to the highest priority. When 0 is the lowest priority, all ones is the highest priority. However, these values are calculated with an extra higher bit set. Luckily, there is still no chance of setting functional bits incorrectly. When the priority widths are maximal, this is ensured as the fields have width 16 and a u16 holds the value for each field. When the widths are smaller, the higher order bits beyond the advertised widths, MPAMF_PRI_IDR.DSPRI_WD and MPAMF_PRI_IDR.INTPRI_WD, in the priority fields INTPRI and DSPRI are not used to calculate the priority. It is not specified whether these higher order bits are RAZ/WI or Res0 and so it is desirable not to set them to avoid the chance of misleading reads. Correct the priority reset values. Fixes: 880df85d8673 ("arm_mpam: Probe and reset the rest of the features") Signed-off-by: Ben Horgan Signed-off-by: Will Deacon --- drivers/resctrl/mpam_devices.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index b69f99488111..37ce11d7db92 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1552,12 +1552,9 @@ static u16 mpam_wa_t241_calc_min_from_max(struct mpam_props *props, static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, struct mpam_config *cfg) { - u32 pri_val = 0; u16 cmax = MPAMCFG_CMAX_CMAX; struct mpam_msc *msc = ris->vmsc->msc; struct mpam_props *rprops = &ris->props; - u16 dspri = GENMASK(rprops->dspri_wd, 0); - u16 intpri = GENMASK(rprops->intpri_wd, 0); mutex_lock(&msc->part_sel_lock); __mpam_part_sel(ris->ris_idx, partid, msc); @@ -1622,16 +1619,25 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, if (mpam_has_feature(mpam_feat_intpri_part, rprops) || mpam_has_feature(mpam_feat_dspri_part, rprops)) { - /* aces high? */ - if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops)) - intpri = 0; - if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops)) - dspri = 0; + u32 pri_val = 0; + + if (mpam_has_feature(mpam_feat_intpri_part, rprops)) { + u16 intpri = GENMASK(rprops->intpri_wd - 1, 0); + + /* aces high? */ + if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops)) + intpri = 0; - if (mpam_has_feature(mpam_feat_intpri_part, rprops)) pri_val |= FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri); - if (mpam_has_feature(mpam_feat_dspri_part, rprops)) + } + if (mpam_has_feature(mpam_feat_dspri_part, rprops)) { + u16 dspri = GENMASK(rprops->dspri_wd - 1, 0); + + if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops)) + dspri = 0; + pri_val |= FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri); + } mpam_write_partsel_reg(msc, PRI, pri_val); } -- cgit v1.2.3 From 021118ce5ea954ec316d7e30bcf4506e12eb5222 Mon Sep 17 00:00:00 2001 From: Fenghua Yu Date: Sat, 6 Jun 2026 22:09:25 -0700 Subject: arm_mpam: Fix MPAMCFG_MBW_PBM register setting MPAMCFG_MBW_PBM is written from cfg if cfg has the MBW partition feature. It is reset when cfg does not have the MBW partition feature. But the register handling is reversed. This may cause an incorrect register setting. For example, during an MPAM reset, reset_cfg is empty (no MBW partition feature set), and cfg->mbw_pbm is 0. Instead of resetting MPAMCFG_MBW_PBM to all 1's, the current logic will set it to cfg->mbw_pbm, which is 0. Fix the issue by swapping the if/else branches. Fixes: a1cb6577f575 ("arm_mpam: Reset when feature configuration bit unset") Reported-by: Matt Ochs Signed-off-by: Fenghua Yu Reviewed-by: Gavin Shan Reviewed-by: Ben Horgan Signed-off-by: Will Deacon --- drivers/resctrl/mpam_devices.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 37ce11d7db92..8e48b918ae54 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1580,9 +1580,9 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, if (mpam_has_feature(mpam_feat_mbw_part, rprops)) { if (mpam_has_feature(mpam_feat_mbw_part, cfg)) - mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); - else mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm); + else + mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); } if (mpam_has_feature(mpam_feat_mbw_min, rprops)) { -- cgit v1.2.3 From 977f52909c624210178a1247fab0b02b110c1106 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 16:23:23 +0800 Subject: arm_mpam: guard MBWU state before adding it to garbage __destroy_component_cfg() adds each RIS mbwu_state object to the MPAM garbage list when destroying component configuration. However, mbwu_state is allocated per RIS and only for RISes with MBWU monitors. A component can therefore have comp->cfg allocated while some RISes still have ris->mbwu_state set to NULL. Passing a NULL mbwu_state to add_to_garbage() dereferences the NULL pointer inside the macro. Skip RISes that do not have an mbwu_state object before adding them to the garbage list. Fixes: 41e8a14950e1 ("arm_mpam: Track bandwidth counter state for power management") Signed-off-by: Guangshuo Li Reviewed-by: Ben Horgan Signed-off-by: Will Deacon --- drivers/resctrl/mpam_devices.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 8e48b918ae54..2f09f4b78bd3 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -2612,8 +2612,10 @@ static void __destroy_component_cfg(struct mpam_component *comp) msc = vmsc->msc; if (mpam_mon_sel_lock(msc)) { - list_for_each_entry(ris, &vmsc->ris, vmsc_list) - add_to_garbage(ris->mbwu_state); + list_for_each_entry(ris, &vmsc->ris, vmsc_list) { + if (ris->mbwu_state) + add_to_garbage(ris->mbwu_state); + } mpam_mon_sel_unlock(msc); } } -- cgit v1.2.3 From 21fc7ec93f8b633b60d5bddef2f1529ff6b36185 Mon Sep 17 00:00:00 2001 From: Yu Peng Date: Wed, 8 Jul 2026 10:35:14 +0800 Subject: arm64: fixmap: Allow 256K early_ioremap() at any offset NR_FIX_BTMAPS is the per-slot page limit for early_ioremap(). Since __early_ioremap() maps the page-aligned physical range, a 256K request can require one extra page when the physical address is not page-aligned. Reserve one extra page per slot so the 256K mapping budget is usable regardless of the initial page offset. Link: https://lore.kernel.org/r/08fd96fa-ee3a-4904-bd11-bb08bd90436f@kylinos.cn Signed-off-by: Yu Peng Signed-off-by: Will Deacon --- arch/arm64/include/asm/fixmap.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h index 65555284446e..7075a3bd2c61 100644 --- a/arch/arm64/include/asm/fixmap.h +++ b/arch/arm64/include/asm/fixmap.h @@ -78,8 +78,12 @@ enum fixed_addresses { /* * Temporary boot-time mappings, used by early_ioremap(), * before ioremap() is functional. + * + * Reserve one extra page so a 256K mapping may start at any + * offset within a page. early_ioremap() maps the page-aligned + * physical range, so the initial offset can consume an extra page. */ -#define NR_FIX_BTMAPS (SZ_256K / PAGE_SIZE) +#define NR_FIX_BTMAPS ((SZ_256K / PAGE_SIZE) + 1) #define FIX_BTMAPS_SLOTS 7 #define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS) -- cgit v1.2.3 From 9dc325327babe7f159e84cbe9380a45342da0585 Mon Sep 17 00:00:00 2001 From: Mark Tomlinson Date: Thu, 9 Jul 2026 16:51:16 +1200 Subject: gpio: pca953x: fix pca953x_irq_bus_sync_unlock regmap lock Locking is disabled in the regmap config as this driver uses its own lock. This means that all calls to regmap functions (read or write) must hold the i2c_lock. The function pca953x_irq_bus_sync_unlock() did not do this, and it was therefore possible that multiple threads could cause an incorrect register to be read/written. A previous patch partly fixed this, but only protected the write to the interrupt mask register, and not the read from the direction register. Fixes: bfc6444b57dc ("gpio: pca953x: fix pca953x_irq_bus_sync_unlock race") Cc: stable@vger.kernel.org Signed-off-by: Mark Tomlinson Link: https://patch.msgid.link/20260709045116.2304246-1-mark.tomlinson@alliedtelesis.co.nz Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index f6b870b7b352..e03bf1b12091 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -604,20 +604,28 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long * return 0; } -static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +static int pca953x_gpio_direction_input_unlocked(struct gpio_chip *gc, + unsigned int off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); u8 bit = pca953x_get_bit_mask(chip, off); - guard(mutex)(&chip->i2c_lock); - if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) return regmap_update_bits(chip->regmap, dirreg, bit, 0); return regmap_update_bits(chip->regmap, dirreg, bit, bit); } +static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned int off) +{ + struct pca953x_chip *chip = gpiochip_get_data(gc); + + guard(mutex)(&chip->i2c_lock); + + return pca953x_gpio_direction_input_unlocked(gc, off); +} + static int pca953x_gpio_direction_output(struct gpio_chip *gc, unsigned off, int val) { @@ -855,9 +863,10 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) DECLARE_BITMAP(reg_direction, MAX_LINE); int level; + guard(mutex)(&chip->i2c_lock); + if (chip->driver_data & PCA_PCAL) { DECLARE_BITMAP(latched_inputs, MAX_LINE); - guard(mutex)(&chip->i2c_lock); /* Enable latch on edge-triggered interrupt-enabled inputs */ bitmap_or(latched_inputs, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); @@ -879,7 +888,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) /* Look for any newly setup interrupt */ for_each_andnot_bit(level, irq_mask, reg_direction, gc->ngpio) - pca953x_gpio_direction_input(&chip->gpio_chip, level); + pca953x_gpio_direction_input_unlocked(&chip->gpio_chip, level); mutex_unlock(&chip->irq_lock); } -- cgit v1.2.3 From 93c2a8ea2454b7b14eb378a58cad8a83c0ffc903 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 16 Jul 2026 14:20:44 +0100 Subject: ASoC: cs35l56: Fix potential probe() deadlock On I2C/SPI call cs35l56_init() before calling snd_soc_register_component() to prevent the potential for a deadlock on init_completion. For most buses all the hardware would be ready when probe() returns, but on SoundWire, probe() must return before the SoundWire bus driver will enumerate the device. All access to the registers must be deferred until the driver receives an ATTACHED notification. But anything that could return -EPROBE_DEFER must be called during probe, and that includes snd_soc_register_component(). Because of that, on SoundWire the ASoC component can be created before the registers are accssible, so cs35l56_component_probe() waits for init_completion to signal that the registers are accessible. On I2C/SPI this 2-stage startup isn't required so their probe() functions simply called cs35l56_common_probe() and then cs35l56_init(). The problem with this was that snd_soc_register_component() was still called early. If this triggered ASoC to create the card, ASoC would call cs35l56_component_probe() which waits on init_completion - but this would be running inside the cs35l56 driver probe() so blocking it from reaching the code that signals init_completion, causing a deadlock. Fixes: e496112529006 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Reported-by: Salman S. Tahir Closes: https://lore.kernel.org/linux-sound/95c21574-97d5-4311-9263-9e174d22d22c@opensource.cirrus.com/T/#u Tested-by: Salman S. Tahir Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260716132045.1469156-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-i2c.c | 4 +--- sound/soc/codecs/cs35l56-spi.c | 4 +--- sound/soc/codecs/cs35l56.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 0f64ab628b03..4f6ddf1c5a3f 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -55,9 +55,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client) if (ret != 0) return ret; - ret = cs35l56_init(cs35l56); - if (ret == 0) - ret = cs35l56_irq_request(&cs35l56->base, client->irq); + ret = cs35l56_irq_request(&cs35l56->base, client->irq); if (ret < 0) cs35l56_remove(cs35l56); diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c index 9bc9b7c98390..b1eb924a5b6c 100644 --- a/sound/soc/codecs/cs35l56-spi.c +++ b/sound/soc/codecs/cs35l56-spi.c @@ -44,9 +44,7 @@ static int cs35l56_spi_probe(struct spi_device *spi) if (ret != 0) return ret; - ret = cs35l56_init(cs35l56); - if (ret == 0) - ret = cs35l56_irq_request(&cs35l56->base, spi->irq); + ret = cs35l56_irq_request(&cs35l56->base, spi->irq); if (ret < 0) cs35l56_remove(cs35l56); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 570a68829ccd..fea4362e74cd 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -2008,6 +2008,16 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) goto err; } + /* + * On SoundWire the cs35l56_init() cannot be run until after the + * device has been enumerated by the SoundWire core. + */ + if (!cs35l56->sdw_peripheral) { + ret = cs35l56_init(cs35l56); + if (ret) + goto err_remove_wm_adsp; + } + ret = snd_soc_register_component(cs35l56->base.dev, &soc_component_dev_cs35l56, cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); @@ -2022,6 +2032,11 @@ err_remove_wm_adsp: wm_adsp2_remove(&cs35l56->dsp); err: + if (pm_runtime_enabled(cs35l56->base.dev)) { + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); + pm_runtime_disable(cs35l56->base.dev); + } + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); -- cgit v1.2.3 From e0bffb63a2eda0af82ed7e6357ac67c2db990c21 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 16 Jul 2026 14:20:45 +0100 Subject: ASoC: cs35l56: Use complete_all() to signal init_completion In cs35l56_init() use complete_all() to signal init_completion instead of complete(). cs35l56_init() was signaling init_completion using the complete() function. This only releases ONE waiter. If cs35l56_component_probe() was called multiple times the first time would consume that one signal, then future calls would timeout waiting for the completion. This could happen if: - The component is probed, removed, then probed again without the cs35l56 module being removed. - A call to component_probe() returns an error and ASoC calls it again later. It should use complete_all() so that after it has been signaled it will allow any code that waits on it to continue immediately. The one case where the driver must wait for initialization to run again is when waiting for a reboot after firmware download, and here the code correctly calls reinit_completion() first. Fixes: e496112529006 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20260716132045.1469156-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index fea4362e74cd..063ef7a70de0 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -2123,7 +2123,7 @@ post_soft_reset: return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n"); cs35l56->base.init_done = true; - complete(&cs35l56->init_completion); + complete_all(&cs35l56->init_completion); return 0; } -- cgit v1.2.3 From 7bc597ce74bab4153b2009c92eccf889e9d74044 Mon Sep 17 00:00:00 2001 From: Himal Prasad Ghimiray Date: Wed, 24 Jun 2026 23:19:44 +0530 Subject: drm/xe/vm: Fix BO prefetch with CONSULT_MEM_ADVISE_PREF_LOC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When prefetch region is DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC for a BO VMA, the code used it as an index into region_to_mem_type[], causing an out-of-bounds access since the value is -1. Resolve the preferred location for BO VMAs directly: local VRAM on dGFX (using the BO's tile placement) or system memory on iGPU. Discovered using AI-assisted static analysis confirmed by Intel Product Security. v2: -Fix null dereference Reported-by: Martin Hodo Fixes: c1bb69a2e8e2 ("drm/xe/svm: Consult madvise preferred location in prefetch") Cc: Matthew Brost Cc: stable@vger.kernel.org Reviewed-by: Matthew Brost Link: https://patchwork.freedesktop.org/patch/msgid/20260624174943.2808767-2-himal.prasad.ghimiray@intel.com Signed-off-by: Himal Prasad Ghimiray (cherry picked from commit d9a4906ac03be9f6ed3f3b45c56c866b867fd75b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_vm.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 080c2fff0e95..32ded13491ca 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -3255,11 +3255,26 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm, .request_decompress = false, .check_purged = true, }); - if (!err && !xe_vma_has_no_bo(vma)) - err = xe_bo_migrate(xe_vma_bo(vma), - region_to_mem_type[region], - NULL, - exec); + if (!err && !xe_vma_has_no_bo(vma)) { + struct xe_bo *bo = xe_vma_bo(vma); + u32 mem_type; + + if (region == DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC) { + unsigned int i; + + mem_type = XE_PL_TT; + for (i = 0; i < bo->placement.num_placement; i++) { + if (mem_type_is_vram(bo->placements[i].mem_type)) { + mem_type = bo->placements[i].mem_type; + break; + } + } + } else { + mem_type = region_to_mem_type[region]; + } + + err = xe_bo_migrate(bo, mem_type, NULL, exec); + } break; } default: -- cgit v1.2.3 From 62775525a27c3b0d56382e08ba81ee2d322058b6 Mon Sep 17 00:00:00 2001 From: Nitin Gote Date: Sat, 11 Jul 2026 00:40:28 +0530 Subject: drm/xe: Hold a dma-buf reference for imported BOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An imported dma-buf BO is created as a ttm_bo_type_sg BO whose reservation object is the exporter's dma_buf->resv. The importer, however, only takes a dma-buf reference after a successful dma_buf_dynamic_attach(). Until then nothing keeps the exporter alive, so if the exporter is freed while the BO still references its resv, a later access to that resv is a use-after-free: Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6b9c Workqueue: ttm ttm_bo_delayed_delete [ttm] RIP: 0010:mutex_can_spin_on_owner+0x3f/0xc0 This can be reached on two paths: - dma_buf_dynamic_attach() fails, or - ttm_bo_init_reserved() fails during BO creation. In both cases the BO already has bo->base.resv pointing at the exporter resv, and sg BOs are always torn down via ttm_bo_delayed_delete(), which locks bo->base.resv asynchronously - potentially after the exporter has been freed. Take the dma-buf reference in xe_bo_init_locked(), before ttm_bo_init_reserved(), so it also covers a creation failure there, and release it in xe_ttm_bo_destroy(). The reference is held for the whole BO lifetime, keeping the shared resv alive on every path. v2: - Reworked the fix to avoid creating the imported sg BO before dma_buf_dynamic_attach() succeeds. - Attach with importer_priv == NULL and make invalidate_mappings ignore incomplete imports. v3: - Dropped the xe-side reordering approach since importer_priv must be valid when dma_buf_dynamic_attach() publishes the attachment. - Per Christian's suggestion on the v1 thread, keyed the check on import_attach rather than removing the sg guard entirely. - Fixes both xe and amdgpu in a single TTM patch. v4: - Moved import_attach check to after dma_resv_copy_fences() so fences are copied before returning for successful imports (Thomas). - Removed exporter-alive claim from commit message (Thomas). v5: - Add drm/xe patch to keep imported sg BOs off the LRU before attach succeeds; the TTM fix alone is not sufficient for xe if the BO is already LRU-visible. (Thomas) v4 patch: https://patchwork.freedesktop.org/patch/736663/?series=169129&rev=2 - Patch 1 (drm/ttm) carries Christian's Reviewed-by from v4. v6: - Reworked the fix based on Thomas' suggestion. Instead of the TTM resv individualization (v1-v5) plus the xe off-LRU/placement handling (v5), just hold a dma-buf reference for the imported BO lifetime so the shared resv can never be freed while the BO still references it. Single xe patch, no TTM change. (Thomas) - Take the reference in xe_bo_init_locked() before ttm_bo_init_reserved() so a TTM creation failure is covered too (Thomas). - Dropped the v5 series (drm/ttm + drm/xe off-LRU); the off-LRU approach also regressed in CI BAT via ttm_bo_pipeline_gutting() creating a ghost BO that outlived the exporter. Link to v5: https://patchwork.freedesktop.org/series/169984/ v7: - Move changelog above --- so it stays in the commit message. - Reorder changelog entries oldest-to-newest. (Thomas) Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8023 Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: stable@vger.kernel.org Cc: Thomas Hellstrom Cc: Christian Konig Cc: Matthew Auld Suggested-by: Thomas Hellstrom Assisted-by: GitHub_Copilot:claude-sonnet-4.6 Reviewed-by: Thomas Hellström Signed-off-by: Nitin Gote Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260710191027.260160-2-nitin.r.gote@intel.com (cherry picked from commit 3516f3fae6be35642f8f06f8a218da6425c0306a) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_bo.c | 24 ++++++++++++++++++++---- drivers/gpu/drm/xe/xe_bo.h | 3 ++- drivers/gpu/drm/xe/xe_bo_types.h | 2 ++ drivers/gpu/drm/xe/xe_dma_buf.c | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 4c80bac67622..ddbaf4242c79 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -1349,7 +1349,7 @@ int xe_bo_notifier_prepare_pinned(struct xe_bo *bo) backup = xe_bo_init_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, xe_bo_size(bo), DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel, XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS | - XE_BO_FLAG_PINNED, &exec); + XE_BO_FLAG_PINNED, NULL, &exec); if (IS_ERR(backup)) { drm_exec_retry_on_contention(&exec); ret = PTR_ERR(backup); @@ -1490,7 +1490,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo) xe_bo_size(bo), DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel, XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS | - XE_BO_FLAG_PINNED, &exec); + XE_BO_FLAG_PINNED, NULL, &exec); if (IS_ERR(backup)) { drm_exec_retry_on_contention(&exec); ret = PTR_ERR(backup); @@ -1826,6 +1826,8 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo) if (bo->ttm.base.import_attach) drm_prime_gem_destroy(&bo->ttm.base, NULL); + if (bo->dma_buf) + dma_buf_put(bo->dma_buf); drm_gem_object_release(&bo->ttm.base); xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list)); @@ -2283,6 +2285,8 @@ void xe_bo_free(struct xe_bo *bo) * @cpu_caching: The cpu caching used for system memory backing store. * @type: The TTM buffer object type. * @flags: XE_BO_FLAG_ flags. + * @dma_buf: The dma-buf to reference for the BO lifetime (imported BOs), + * or NULL. * @exec: The drm_exec transaction to use for exhaustive eviction. * * Initialize or create an xe buffer object. On failure, any allocated buffer @@ -2294,7 +2298,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, struct xe_tile *tile, struct dma_resv *resv, struct ttm_lru_bulk_move *bulk, size_t size, u16 cpu_caching, enum ttm_bo_type type, - u32 flags, struct drm_exec *exec) + u32 flags, struct dma_buf *dma_buf, + struct drm_exec *exec) { struct ttm_operation_ctx ctx = { .interruptible = true, @@ -2383,6 +2388,17 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, placement = (type == ttm_bo_type_sg || bo->flags & XE_BO_FLAG_DEFER_BACKING) ? &sys_placement : &bo->placement; + + /* + * For imported BOs, keep the exporter dma-buf alive for the BO + * lifetime. Taken before ttm_bo_init_reserved() to also cover a + * creation failure there. Released in xe_ttm_bo_destroy(). + */ + if (dma_buf) { + get_dma_buf(dma_buf); + bo->dma_buf = dma_buf; + } + err = ttm_bo_init_reserved(&xe->ttm, &bo->ttm, type, placement, alignment, &ctx, NULL, resv, xe_ttm_bo_destroy); @@ -2500,7 +2516,7 @@ __xe_bo_create_locked(struct xe_device *xe, vm && !xe_vm_in_fault_mode(vm) && flags & XE_BO_FLAG_USER ? &vm->lru_bulk_move : NULL, size, - cpu_caching, type, flags, exec); + cpu_caching, type, flags, NULL, exec); if (IS_ERR(bo)) return bo; diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index 6340317f7d2e..7ae1d9ac0574 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -118,7 +118,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo, struct xe_tile *tile, struct dma_resv *resv, struct ttm_lru_bulk_move *bulk, size_t size, u16 cpu_caching, enum ttm_bo_type type, - u32 flags, struct drm_exec *exec); + u32 flags, struct dma_buf *dma_buf, + struct drm_exec *exec); struct xe_bo *xe_bo_create_locked(struct xe_device *xe, struct xe_tile *tile, struct xe_vm *vm, size_t size, enum ttm_bo_type type, u32 flags, diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h index fcc63ae3f455..e45f24301050 100644 --- a/drivers/gpu/drm/xe/xe_bo_types.h +++ b/drivers/gpu/drm/xe/xe_bo_types.h @@ -36,6 +36,8 @@ struct xe_bo { struct xe_bo *backup_obj; /** @parent_obj: Ref to parent bo if this a backup_obj */ struct xe_bo *parent_obj; + /** @dma_buf: Imported dma-buf ref to keep its resv alive. */ + struct dma_buf *dma_buf; /** @flags: flags for this buffer object */ u32 flags; /** @vm: VM this BO is attached to, for extobj this will be NULL */ diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 8a920e58245c..bf0728838ead 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -302,7 +302,7 @@ xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf) bo = xe_bo_init_locked(xe, NULL, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ - ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); + ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, dma_buf, &exec); drm_exec_retry_on_contention(&exec); if (IS_ERR(bo)) { ret = PTR_ERR(bo); -- cgit v1.2.3 From c473761f8178760b915633332908409c73bfdb9e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 14 Jul 2026 08:54:17 +0300 Subject: drm/xe/nvm: fix writable override for CRI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The witable override should be set when FDO_MODE bit is enabled. Fix the comparison to distingush this case from legacy systems where bit should be disabled to have override. Cc: stable@vger.kernel.org Fixes: 9dde74fd9e65 ("drm/xe/nvm: enable cri platform") Signed-off-by: Alexander Usyskin Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260714-cri_nvm_fdo_flip-v2-1-14580e71b58e@intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 2007be18d2318a59748da5da1b8968042213d5f1) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_nvm.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_nvm.c b/drivers/gpu/drm/xe/xe_nvm.c index 33487e91f366..1ea67eaeae24 100644 --- a/drivers/gpu/drm/xe/xe_nvm.c +++ b/drivers/gpu/drm/xe/xe_nvm.c @@ -60,35 +60,40 @@ static bool xe_nvm_writable_override(struct xe_device *xe) struct xe_mmio *mmio = xe_root_tile_mmio(xe); bool writable_override; struct xe_reg reg; - u32 test_bit; + u32 test_bit, test_val; switch (xe->info.platform) { case XE_CRESCENTISLAND: reg = PCODE_SCRATCH(0); test_bit = FDO_MODE; + test_val = FDO_MODE; break; case XE_BATTLEMAGE: reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE); test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE; + test_val = 0; break; case XE_PVC: reg = HECI_FWSTS2(PVC_GSC_HECI2_BASE); test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE; + test_val = 0; break; case XE_DG2: reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE); test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE; + test_val = 0; break; case XE_DG1: reg = HECI_FWSTS2(DG1_GSC_HECI2_BASE); test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE; + test_val = 0; break; default: drm_err(&xe->drm, "Unknown platform\n"); return true; } - writable_override = !(xe_mmio_read32(mmio, reg) & test_bit); + writable_override = (xe_mmio_read32(mmio, reg) & test_bit) == test_val; if (writable_override) drm_info(&xe->drm, "NVM access overridden by jumper\n"); return writable_override; -- cgit v1.2.3 From 56441f9e08ad68697295b8835266d2bc48ab59b5 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Mon, 13 Jul 2026 23:24:40 -0700 Subject: drm/xe/vf: Fix VF CCS attach/detach race with in-flight BO moves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xe_bo_move() attaches VF CCS read/write batch buffers (BBs) to a BO after it transitions NULL/SYSTEM -> TT, and detaches them after it transitions TT -> SYSTEM. Both operations were done synchronously on the CPU immediately after building the move's copy/clear fence, without waiting for that fence to signal. This creates two races with VF migration: - Attach happens too late relative to the copy job it is meant to protect. If the copy job is submitted before the CCS BBs are attached, a VF migration event that pauses execution mid-copy can observe partially copied CCS metadata without the attach state needed to correctly save/restore it. - Detach happens too early relative to the copy job that moves data out of TT. The CCS BBs are torn down right after the copy fence is obtained, while the actual blit may still be in flight. A VF migration event that pauses execution mid-copy can then race the save/restore path against the still-running blit, and the CCS BBs it would need to make sense of the paused state have already been removed. Fix both races: - Move the attach call to before the copy/clear job is submitted, so the CCS BBs are already registered by the time the copy runs. On attach failure, unwind and bail out of the move. xe_migrate_ccs_rw_copy() now takes the destination resource explicitly, since bo->ttm.resource is not updated to the new resource until after the move commits. - Detach only after explicitly waiting for the copy fence to signal, instead of tearing down the CCS BBs immediately after obtaining it. While here, also fix xe_sriov_vf_ccs_attach_bo() to properly unwind and propagate errors: the per-context loop previously never broke out on error, silently discarding earlier failures. Unwind by clearing each attached context directly via xe_migrate_ccs_rw_copy_clear() instead of reusing xe_sriov_vf_ccs_detach_bo(), which requires both contexts to be attached before it will clean up either one. Fixes: 864690cf4dd6 ("drm/xe/vf: Attach and detach CCS copy commands with BO") Cc: Michal Wajdeczko Cc: Matthew Auld Cc: Michał Winiarski Cc: Satyanarayana K V P Assisted-by: GitHub_Copilot:claude-sonnet-5 Signed-off-by: Matthew Brost Acked-by: Satyanarayana K V P Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260714062440.3421225-1-matthew.brost@intel.com (cherry picked from commit d45ad0aa7a1eb5d7288b5ed948b05695611dc39e) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_bo.c | 34 +++++++++++++++++++++++----------- drivers/gpu/drm/xe/xe_migrate.c | 5 ++++- drivers/gpu/drm/xe/xe_migrate.h | 1 + drivers/gpu/drm/xe/xe_sriov_vf_ccs.c | 20 ++++++++++++++++++-- drivers/gpu/drm/xe/xe_sriov_vf_ccs.h | 3 ++- 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index ddbaf4242c79..7ed76349075f 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -1102,6 +1102,21 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict, xe_pm_runtime_get_noresume(xe); } + /* + * Attach CCS BBs before submitting the copy job below so a VF + * migration racing the copy sees valid, up to date attach state. + */ + if (IS_VF_CCS_READY(xe) && + ((move_lacks_source && new_mem->mem_type == XE_PL_TT) || + (old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) && + handle_system_ccs) { + ret = xe_sriov_vf_ccs_attach_bo(bo, new_mem); + if (ret) { + xe_pm_runtime_put(xe); + goto out; + } + } + if (move_lacks_source) { u32 flags = 0; @@ -1139,22 +1154,19 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict, ttm_bo_move_null(ttm_bo, new_mem); } - dma_fence_put(fence); - xe_pm_runtime_put(xe); - /* - * CCS meta data is migrated from TT -> SMEM. So, let us detach the - * BBs from BO as it is no longer needed. + * Detach must wait for the copy above to complete: a VF migration + * racing an in-flight copy must still see valid CCS BBs, so don't + * tear them down until the copy fence has signaled. */ if (IS_VF_CCS_READY(xe) && old_mem_type == XE_PL_TT && - new_mem->mem_type == XE_PL_SYSTEM) + new_mem->mem_type == XE_PL_SYSTEM) { + dma_fence_wait(fence, false); xe_sriov_vf_ccs_detach_bo(bo); + } - if (IS_VF_CCS_READY(xe) && - ((move_lacks_source && new_mem->mem_type == XE_PL_TT) || - (old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) && - handle_system_ccs) - ret = xe_sriov_vf_ccs_attach_bo(bo); + dma_fence_put(fence); + xe_pm_runtime_put(xe); out: if ((!ttm_bo->resource || ttm_bo->resource->mem_type == XE_PL_SYSTEM) && diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index 9428dd5e7760..7d28290e7d1c 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -1166,6 +1166,8 @@ static int emit_flush_invalidate(u32 *dw, int i, u32 flags) * @tile: Tile whose migration context to be used. * @q : Execution to be used along with migration context. * @src_bo: The buffer object @src is currently bound to. + * @new_mem: The (not yet committed) destination resource @src_bo is being + * moved into; src_bo->ttm.resource is still the old resource. * @read_write : Creates BB commands for CCS read/write. * * Creates batch buffer instructions to copy CCS metadata from CCS pool to @@ -1177,12 +1179,13 @@ static int emit_flush_invalidate(u32 *dw, int i, u32 flags) */ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, struct xe_bo *src_bo, + struct ttm_resource *new_mem, enum xe_sriov_vf_ccs_rw_ctxs read_write) { bool src_is_pltt = read_write == XE_SRIOV_VF_CCS_READ_CTX; bool dst_is_pltt = read_write == XE_SRIOV_VF_CCS_WRITE_CTX; - struct ttm_resource *src = src_bo->ttm.resource; + struct ttm_resource *src = new_mem; struct xe_migrate *m = tile->migrate; struct xe_gt *gt = tile->primary_gt; u32 batch_size, batch_size_allocated; diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index 965c45889c72..78e5b63f3ebe 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -138,6 +138,7 @@ struct dma_fence *xe_migrate_resolve(struct xe_migrate *m, int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, struct xe_bo *src_bo, + struct ttm_resource *new_mem, enum xe_sriov_vf_ccs_rw_ctxs read_write); void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index 09b99fb2608b..6787564629c6 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -404,6 +404,8 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx) /** * xe_sriov_vf_ccs_attach_bo - Insert CCS read write commands in the BO. * @bo: the &buffer object to which batch buffer commands will be added. + * @new_mem: the (not yet committed) destination resource @bo is being moved + * into; bo->ttm.resource is still the old resource at this point. * * This function shall be called only by VF. It inserts the PTEs and copy * command instructions in the BO by calling xe_migrate_ccs_rw_copy() @@ -411,7 +413,7 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx) * * Returns: 0 if successful, negative error code on failure. */ -int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo) +int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem) { struct xe_device *xe = xe_bo_device(bo); enum xe_sriov_vf_ccs_rw_ctxs ctx_id; @@ -430,7 +432,21 @@ int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo) xe_assert(xe, !bb); ctx = &xe->sriov.vf.ccs.contexts[ctx_id]; - err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, ctx_id); + err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, new_mem, ctx_id); + if (err) + goto err_unwind; + } + return 0; + +err_unwind: + /* + * Clean up any contexts already attached. Can't reuse + * xe_sriov_vf_ccs_detach_bo() here as it requires both contexts + * attached before cleaning up either one. + */ + for_each_ccs_rw_ctx(ctx_id) { + if (bo->bb_ccs[ctx_id]) + xe_migrate_ccs_rw_copy_clear(bo, ctx_id); } return err; } diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h index 00e58b36c510..e1034d852104 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h @@ -11,11 +11,12 @@ #include "xe_sriov_vf_ccs_types.h" struct drm_printer; +struct ttm_resource; struct xe_device; struct xe_bo; int xe_sriov_vf_ccs_init(struct xe_device *xe); -int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo); +int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem); int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo); int xe_sriov_vf_ccs_register_context(struct xe_device *xe); void xe_sriov_vf_ccs_rebase(struct xe_device *xe); -- cgit v1.2.3 From ad87e2476b3b246580f407afc8ffa91d621bc849 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Mon, 13 Jul 2026 15:17:59 -0700 Subject: drm/xe/wopcm: fix WOPCM size for LNL+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting on LNL the WOPCM size is 8MB instead of 4, so we need to avoid using the [0, 8MB) range of the GGTT as that can be unaccessible from the microcontrollers. Note that the proper long-term fix here is to read the WOPCM size from the HW, but that is a more serious rework that would be difficult to backport, so we can do that as a follow-up. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Signed-off-by: Daniele Ceraolo Spurio Cc: Rodrigo Vivi Cc: Shuicheng Lin Cc: Matt Roper Reviewed-by: Shuicheng Lin Link: https://patch.msgid.link/20260713221758.3285744-2-daniele.ceraolospurio@intel.com (cherry picked from commit 3033b0b24ed0e2f5e56bdd4d9c183417c365a45b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_wopcm.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_wopcm.c b/drivers/gpu/drm/xe/xe_wopcm.c index 900daf1d1b1b..fe65ed246775 100644 --- a/drivers/gpu/drm/xe/xe_wopcm.c +++ b/drivers/gpu/drm/xe/xe_wopcm.c @@ -49,9 +49,9 @@ */ /* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */ -/* FIXME: Larger size require for 2 tile PVC, do a proper probe sooner or later */ +/* FIXME: Larger size require for some platforms, do a proper probe sooner or later */ #define DGFX_WOPCM_SIZE SZ_4M -/* FIXME: Larger size require for MTL, do a proper probe sooner or later */ +#define LNL_WOPCM_SIZE SZ_8M #define MTL_WOPCM_SIZE SZ_4M #define WOPCM_SIZE SZ_2M @@ -179,9 +179,14 @@ err_out: u32 xe_wopcm_size(struct xe_device *xe) { - return IS_DGFX(xe) ? DGFX_WOPCM_SIZE : - xe->info.platform == XE_METEORLAKE ? MTL_WOPCM_SIZE : - WOPCM_SIZE; + if (xe->info.platform >= XE_LUNARLAKE) + return LNL_WOPCM_SIZE; + else if (IS_DGFX(xe)) + return DGFX_WOPCM_SIZE; + else if (xe->info.platform == XE_METEORLAKE) + return MTL_WOPCM_SIZE; + else + return WOPCM_SIZE; } static u32 max_wopcm_size(struct xe_device *xe) -- cgit v1.2.3 From 6384271ac1ac0099198d15df79212a19ebdb929d Mon Sep 17 00:00:00 2001 From: Zongyao Bai Date: Tue, 14 Jul 2026 23:24:32 +0000 Subject: drm/xe/pt: Reset current_op in xe_pt_update_ops_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xe_pt_update_ops_init() fails to reset current_op to 0. On the vm_bind path, ops_execute() calls xe_pt_update_ops_prepare() inside the xe_validation_guard() / drm_exec_until_all_locked() loop. When that loop retries due to lock contention or OOM eviction (drm_exec_retry_on_contention() / xe_validation_retry_on_oom()), xe_pt_update_ops_prepare() runs again on the same vops, and each call to bind_op_prepare() increments current_op without resetting it. After N retries current_op exceeds the array size allocated by xe_vma_ops_alloc(), causing an out-of-bounds write into SLUB-poisoned memory and a subsequent UAF crash in xe_migrate_update_pgtables_cpu() when reading the corrupted pt_op->bind. Also reset needs_svm_lock and needs_invalidation which are derived in the same prepare pass and would otherwise cause wrong migrate ops selection and redundant TLB invalidation on retry. Fix this by resetting current_op, needs_svm_lock and needs_invalidation in xe_pt_update_ops_init(). v2 (Matt): - Add details in commit message. - Add Fixes tag and Cc to stable@vger.kernel.org Fixes: e8babb280b5e ("drm/xe: Convert multiple bind ops into single job") Suggested-by: Matthew Auld Cc: stable@vger.kernel.org Assisted-by: GitHub-Copilot:claude-sonnet-4.6 Signed-off-by: Zongyao Bai Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260714232433.2737533-1-zongyao.bai@intel.com (cherry picked from commit 046045543e530605c441063535e7dca0075369a6) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index e787c0c27c42..39c9c8f0ea2d 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -2365,8 +2365,11 @@ static void xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops) { init_llist_head(&pt_update_ops->deferred); + pt_update_ops->current_op = 0; pt_update_ops->start = ~0x0ull; pt_update_ops->last = 0x0ull; + pt_update_ops->needs_svm_lock = false; + pt_update_ops->needs_invalidation = false; xe_page_reclaim_list_init(&pt_update_ops->prl); } -- cgit v1.2.3 From 299bc6d50b1bed7d1f408391736712f01a0855e2 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 14 Jul 2026 12:14:02 +0530 Subject: drm/xe/guc: Keep scheduler timeline name alive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scheduler keeps a pointer to the timeline name, but q->name is freed with the exec queue while scheduler fences can still reference it. Store the name in struct xe_guc_exec_queue so it shares the scheduler's RCU-deferred lifetime. Fixes: 6bd90e700b42 ("drm/xe: Make dma-fences compliant with the safe access rules") Cc: Thomas Hellström Cc: Rodrigo Vivi Cc: Himal Prasad Ghimiray Cc: Matthew Brost Signed-off-by: Arvind Yadav Reviewed-by: Tvrtko Ursulin Acked-by: Matthew Brost Link: https://patch.msgid.link/20260714064402.2457257-1-arvind.yadav@intel.com Signed-off-by: Tejas Upadhyay (cherry picked from commit 41075f0eb5dcbd3b065d15f15ef7bbe9315188e8) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_exec_queue_types.h | 5 +++++ drivers/gpu/drm/xe/xe_guc_submit.c | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h index e5e53b421f29..cda14d954e57 100644 --- a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h @@ -10,6 +10,7 @@ #include #include "xe_gpu_scheduler_types.h" +#include "xe_hw_fence_types.h" struct dma_fence; struct xe_exec_queue; @@ -24,6 +25,10 @@ struct xe_guc_exec_queue { struct rcu_head rcu; /** @sched: GPU scheduler for this xe_exec_queue */ struct xe_gpu_scheduler sched; + /** + * @name: Scheduler timeline name, kept with @sched until RCU free. + */ + char name[MAX_FENCE_NAME_LEN]; /** @entity: Scheduler entity for this xe_exec_queue */ struct xe_sched_entity entity; /** diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index f5c3d8a97ec6..9109f21d367f 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1955,6 +1955,8 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) xe_exec_queue_assign_name(q, q->guc->id); + strscpy(ge->name, q->name, sizeof(ge->name)); + /* * Use primary queue's submit_wq for all secondary queues of a * multi queue group. This serialization avoids any locking around @@ -1969,7 +1971,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops, submit_wq, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64, timeout, guc_to_gt(guc)->ordered_wq, NULL, - q->name, gt_to_xe(q->gt)->drm.dev); + ge->name, gt_to_xe(q->gt)->drm.dev); if (err) goto err_release_id; -- cgit v1.2.3 From 9b7e60184f4b22e893d4ae95234d5f26261a430c Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 16 Jul 2026 11:56:24 +0530 Subject: drm/xe/guc: Hold device ref until queue teardown completes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GuC exec queue destruction can run asynchronously. If the final device put happens from a destroy worker, drmm cleanup can end up draining the same workqueue and deadlock. Hold a drm_device reference for the queue lifetime and drop it after queue teardown completes. This keeps drmm cleanup from running while async destroy work is still pending. Move GuC destroy work to a module-lifetime Xe workqueue and flush it on PCI remove so hot-unbind/rebind still waits for pending destroy work. With queue-held device refs, guc_submit_sw_fini() cannot run with live GuC IDs. Replace the fini wait with an assertion and remove the unused fini_wq. v2: - Rebase v3: - Switch to queue-lifetime drm_dev_get()/drm_dev_put() model. (Matt) - Queue async teardown on system_dfl_wq instead of xe->destroy_wq. (Matt) - Drop separate deferred drm_dev_put worker. - Remove stale drain_workqueue(xe->destroy_wq) from guc_submit_sw_fini(). v4: - Replace the guc_submit_sw_fini() wait with an assertion and remove the now-unused fini_wq. (sashiko) v5: - Move destroy work to a module-lifetime Xe workqueue instead of system_dfl_wq. (Matt) - Flush the module-lifetime destroy workqueue during PCI remove to preserve the old device-remove wait semantics. v6: - Keep SVM pagemap destroy work on the per-device destroy_wq to avoid letting it outlive the xe_device/drm_device. (Sashiko) - Use WQ_MEM_RECLAIM for xe->destroy_wq because SVM pagemap destroy work can be queued from the reclaim path. v7: - Drop the per-device xe->destroy_wq and use the module-level destroy WQ for SVM pagemap destroy as well. (Matt) - Rename xe_exec_queue_destroy_wq_*() helpers to xe_destroy_wq_*() helpers because the WQ is no longer exec-queue specific. (Matt) v8: - Rebase. v9: - Keep SVM pagemap destroy work on the per-device WQ_MEM_RECLAIM destroy_wq because it can be queued from reclaim and embeds the dev_pagemap used by devres teardown. (Sashiko) - Keep the module-level destroy WQ GuC-only and drop WQ_MEM_RECLAIM from it. - Update the module-WQ kdoc to document the GuC/SVM split. v10: - Keep xe->destroy_wq per-cpu while adding WQ_MEM_RECLAIM to fix the workqueue allocation warning. v11: - Drop the SVM pagemap destroy comment as it was revision-specific. (Thomas) v12: - Rebase. Fixes: 2d2be279f1ca ("drm/xe: fix UAF around queue destruction") Cc: Thomas Hellström Cc: Rodrigo Vivi Cc: Himal Prasad Ghimiray Cc: Tejas Upadhyay Reviewed-by: Matthew Brost Signed-off-by: Arvind Yadav Link: https://patch.msgid.link/20260716062624.211396-1-arvind.yadav@intel.com Signed-off-by: Tejas Upadhyay (cherry picked from commit da1124abac689cc2b1d8995e5f0a816f8a122edb) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_device.c | 2 +- drivers/gpu/drm/xe/xe_device_types.h | 2 +- drivers/gpu/drm/xe/xe_guc_submit.c | 66 +++++++++++++++++++++--------------- drivers/gpu/drm/xe/xe_guc_types.h | 2 -- drivers/gpu/drm/xe/xe_module.c | 49 ++++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_module.h | 5 +++ drivers/gpu/drm/xe/xe_pci.c | 6 ++++ 7 files changed, 100 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index abe25aedeead..f3eb83644a6f 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -580,7 +580,7 @@ int xe_device_init_early(struct xe_device *xe) WQ_MEM_RECLAIM); xe->ordered_wq = alloc_ordered_workqueue("xe-ordered-wq", 0); xe->unordered_wq = alloc_workqueue("xe-unordered-wq", WQ_PERCPU, 0); - xe->destroy_wq = alloc_workqueue("xe-destroy-wq", WQ_PERCPU, 0); + xe->destroy_wq = alloc_workqueue("xe-destroy-wq", WQ_PERCPU | WQ_MEM_RECLAIM, 0); if (!xe->ordered_wq || !xe->unordered_wq || !xe->preempt_fence_wq || !xe->destroy_wq) { /* diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index 32dd2ffbc796..aad10899d9ac 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -355,7 +355,7 @@ struct xe_device { /** @unordered_wq: used to serialize unordered work */ struct workqueue_struct *unordered_wq; - /** @destroy_wq: used to serialize user destroy work, like queue */ + /** @destroy_wq: used to serialize SVM pagemap destroy work */ struct workqueue_struct *destroy_wq; /** @tiles: device tiles */ diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 9109f21d367f..1c92b96e4c32 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -10,6 +10,7 @@ #include #include +#include #include #include "abi/guc_actions_abi.h" @@ -37,6 +38,7 @@ #include "xe_macros.h" #include "xe_map.h" #include "xe_mocs.h" +#include "xe_module.h" #include "xe_pm.h" #include "xe_ring_ops_types.h" #include "xe_sched_job.h" @@ -232,17 +234,9 @@ static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q) static void guc_submit_sw_fini(struct drm_device *drm, void *arg) { struct xe_guc *guc = arg; - struct xe_device *xe = guc_to_xe(guc); struct xe_gt *gt = guc_to_gt(guc); - int ret; - - ret = wait_event_timeout(guc->submission_state.fini_wq, - xa_empty(&guc->submission_state.exec_queue_lookup), - HZ * 5); - drain_workqueue(xe->destroy_wq); - - xe_gt_assert(gt, ret); + xe_gt_assert(gt, xa_empty(&guc->submission_state.exec_queue_lookup)); xa_destroy(&guc->submission_state.exec_queue_lookup); } @@ -319,8 +313,6 @@ int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids) xa_init(&guc->submission_state.exec_queue_lookup); - init_waitqueue_head(&guc->submission_state.fini_wq); - primelockdep(guc); guc->submission_state.initialized = true; @@ -411,9 +403,6 @@ static void __release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q, xe_guc_id_mgr_release_locked(&guc->submission_state.idm, q->guc->id, q->width); - if (xa_empty(&guc->submission_state.exec_queue_lookup)) - wake_up(&guc->submission_state.fini_wq); - mutex_unlock(&guc->submission_state.lock); } @@ -1685,6 +1674,7 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q) { struct xe_guc_exec_queue *ge = q->guc; struct xe_guc *guc = exec_queue_to_guc(q); + struct drm_device *drm = &guc_to_xe(guc)->drm; if (xe_exec_queue_is_multi_queue_secondary(q)) { struct xe_exec_queue_group *group = q->multi_queue.group; @@ -1703,36 +1693,52 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q) * (timeline name). */ kfree_rcu(ge, rcu); + + drm_dev_put(drm); } -static void __guc_exec_queue_destroy_async(struct work_struct *w) +static void guc_exec_queue_do_destroy(struct xe_exec_queue *q) { - struct xe_guc_exec_queue *ge = - container_of(w, struct xe_guc_exec_queue, destroy_async); - struct xe_exec_queue *q = ge->q; + struct xe_guc_exec_queue *ge = q->guc; struct xe_guc *guc = exec_queue_to_guc(q); + struct xe_device *xe = guc_to_xe(guc); + struct drm_device *drm = &xe->drm; - guard(xe_pm_runtime)(guc_to_xe(guc)); - trace_xe_exec_queue_destroy(q); + /* + * guc_exec_queue_fini() drops the queue's drm_device ref. + * Keep the device alive until the PM-runtime guard unwinds. + */ + drm_dev_get(drm); - /* Confirm no work left behind accessing device structures */ - cancel_delayed_work_sync(&ge->sched.base.work_tdr); + scoped_guard(xe_pm_runtime, xe) { + trace_xe_exec_queue_destroy(q); - xe_exec_queue_fini(q); + /* Confirm no work left behind accessing device structures */ + cancel_delayed_work_sync(&ge->sched.base.work_tdr); + + xe_exec_queue_fini(q); + } + + drm_dev_put(drm); } -static void guc_exec_queue_destroy_async(struct xe_exec_queue *q) +static void __guc_exec_queue_destroy_async(struct work_struct *w) { - struct xe_guc *guc = exec_queue_to_guc(q); - struct xe_device *xe = guc_to_xe(guc); + struct xe_guc_exec_queue *ge = + container_of(w, struct xe_guc_exec_queue, destroy_async); + + guc_exec_queue_do_destroy(ge->q); +} +static void guc_exec_queue_destroy_async(struct xe_exec_queue *q) +{ INIT_WORK(&q->guc->destroy_async, __guc_exec_queue_destroy_async); /* We must block on kernel engines so slabs are empty on driver unload */ if (q->flags & EXEC_QUEUE_FLAG_PERMANENT || exec_queue_wedged(q)) - __guc_exec_queue_destroy_async(&q->guc->destroy_async); + guc_exec_queue_do_destroy(q); else - queue_work(xe->destroy_wq, &q->guc->destroy_async); + xe_destroy_wq_queue(&q->guc->destroy_async); } static void __guc_exec_queue_destroy(struct xe_guc *guc, struct xe_exec_queue *q) @@ -1927,6 +1933,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) { struct xe_gpu_scheduler *sched; struct xe_guc *guc = exec_queue_to_guc(q); + struct drm_device *drm = &guc_to_xe(guc)->drm; struct workqueue_struct *submit_wq = NULL; struct xe_guc_exec_queue *ge; long timeout; @@ -1938,6 +1945,8 @@ static int guc_exec_queue_init(struct xe_exec_queue *q) if (!ge) return -ENOMEM; + drm_dev_get(drm); + q->guc = ge; ge->q = q; init_rcu_head(&ge->rcu); @@ -2016,6 +2025,7 @@ err_release_id: release_guc_id(guc, q); err_free: kfree(ge); + drm_dev_put(drm); return err; } diff --git a/drivers/gpu/drm/xe/xe_guc_types.h b/drivers/gpu/drm/xe/xe_guc_types.h index c7b9642b41ba..31a2acb63ac3 100644 --- a/drivers/gpu/drm/xe/xe_guc_types.h +++ b/drivers/gpu/drm/xe/xe_guc_types.h @@ -100,8 +100,6 @@ struct xe_guc { * even initialized - before that not even the lock is valid */ bool initialized; - /** @submission_state.fini_wq: submit fini wait queue */ - wait_queue_head_t fini_wq; } submission_state; /** @hwconfig: Hardware config state */ diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c index 4cb578182912..99347f216ec8 100644 --- a/drivers/gpu/drm/xe/xe_module.c +++ b/drivers/gpu/drm/xe/xe_module.c @@ -7,6 +7,7 @@ #include #include +#include #include @@ -91,6 +92,50 @@ static int xe_check_nomodeset(void) return 0; } +static struct workqueue_struct *xe_destroy_wq; + +static int __init xe_destroy_wq_module_init(void) +{ + xe_destroy_wq = alloc_workqueue("xe-guc-destroy-wq", WQ_UNBOUND, 0); + if (!xe_destroy_wq) + return -ENOMEM; + return 0; +} + +static void xe_destroy_wq_module_exit(void) +{ + if (xe_destroy_wq) + destroy_workqueue(xe_destroy_wq); + xe_destroy_wq = NULL; +} + +/** + * xe_destroy_wq_queue() - Queue work on the destroy workqueue + * @work: work item to queue + * + * The destroy workqueue has module lifetime and is used for GuC exec queue + * teardown that can outlive a single xe_device. SVM pagemap destroy uses the + * per-device xe->destroy_wq instead. + * + * Return: %true if @work was queued, %false if it was already pending. + */ +bool xe_destroy_wq_queue(struct work_struct *work) +{ + return queue_work(xe_destroy_wq, work); +} + +/** + * xe_destroy_wq_flush() - Flush the destroy workqueue + * + * Drains all pending destroy work. Called from PCI remove to ensure + * teardown ordering before the device is destroyed. + */ +void xe_destroy_wq_flush(void) +{ + if (xe_destroy_wq) + flush_workqueue(xe_destroy_wq); +} + struct init_funcs { int (*init)(void); void (*exit)(void); @@ -112,6 +157,10 @@ static const struct init_funcs init_funcs[] = { .init = xe_sched_job_module_init, .exit = xe_sched_job_module_exit, }, + { + .init = xe_destroy_wq_module_init, + .exit = xe_destroy_wq_module_exit, + }, { .init = xe_register_pci_driver, .exit = xe_unregister_pci_driver, diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h index 79cb9639c0f3..e8e54f701cf3 100644 --- a/drivers/gpu/drm/xe/xe_module.h +++ b/drivers/gpu/drm/xe/xe_module.h @@ -8,6 +8,8 @@ #include +struct work_struct; + /* Module modprobe variables */ struct xe_modparam { bool force_execlist; @@ -27,5 +29,8 @@ struct xe_modparam { extern struct xe_modparam xe_modparam; +bool xe_destroy_wq_queue(struct work_struct *work); +void xe_destroy_wq_flush(void); + #endif diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index 3165686e3e04..2bd601eb088b 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -1048,6 +1048,12 @@ static void xe_pci_remove(struct pci_dev *pdev) return; xe_device_remove(xe); + + /* + * Preserve remove-time flush after moving destroy work to module + * lifetime. + */ + xe_destroy_wq_flush(); xe_pm_fini(xe); } -- cgit v1.2.3 From 130910bac905a42225f93841e338bbea4a431b1a Mon Sep 17 00:00:00 2001 From: Satyanarayana K V P Date: Tue, 14 Jul 2026 11:03:00 +0530 Subject: drm/xe/pf: Disable display in admin only PF mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Admin-only PF mode does not expose media or 3D execution capabilities to userspace, so display pipelines cannot receive rendered content. Fixes: d88c4bac8c2a ("drm/xe/pf: Restrict device query responses in admin-only PF mode") Signed-off-by: Satyanarayana K V P Cc: Michal Wajdeczko Cc: Piotr Piórkowski Cc: Michał Winiarski Cc: Rodrigo Vivi Reviewed-by: Piotr Piórkowski Link: https://patch.msgid.link/20260714053259.504308-2-satyanarayana.k.v.p@intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 7ef55ae582eba2b0a7a7441bd3b9aefd38a26bb9) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_device.c | 2 -- drivers/gpu/drm/xe/xe_pci.c | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index f3eb83644a6f..dcb48caa485d 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -426,7 +426,6 @@ static const struct drm_ioctl_desc xe_ioctls_admin_only[] = { static const struct drm_driver admin_only_driver = { .driver_features = - XE_DISPLAY_DRIVER_FEATURES | DRIVER_GEM | DRIVER_RENDER | DRIVER_GEM_GPUVA, .open = xe_file_open, .postclose = xe_file_close, @@ -438,7 +437,6 @@ static const struct drm_driver admin_only_driver = { .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, - XE_DISPLAY_DRIVER_OPS, }; /** diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index 2bd601eb088b..9cd873708136 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -788,7 +788,8 @@ static int xe_info_init_early(struct xe_device *xe, xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) && xe_modparam.probe_display && - desc->has_display; + desc->has_display && + !xe_device_is_admin_only(xe); xe->info.force_execlist = xe_modparam.force_execlist; xe_assert(xe, desc->max_gt_per_tile > 0); -- cgit v1.2.3 From 3b4ca2e01c1dd8c00b675b794732945f460a471b Mon Sep 17 00:00:00 2001 From: Huang Wei Date: Thu, 16 Jul 2026 11:33:41 +0800 Subject: USB: storage: add NO_ATA_1X quirk for Longmai USB Key The Longmai Technologies USB Key (0x04b4:0xb708) advertises itself as a SCSI/Bulk-only mass storage device but does not correctly handle ATA pass-through commands. When such a command (ATA_12 or ATA_16) is sent to the device it fails to respond and the transfer eventually times out, leaving the device unusable. Add an unusual_devs entry for this device that sets the US_FL_NO_ATA_1X flag, so usb-storage short-circuits ATA pass-through commands and returns INVALID COMMAND OPERATION CODE (0x20 0x05 0x24 0x00) instead of forwarding them to the device. Information about the device in /sys/kernel/debug/usb/devices: T: Bus=02 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 12 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=06 Prot=50 MxPS=64 #Cfgs= 1 P: Vendor=04b4 ProdID=b708 Rev= 1.00 S: Manufacturer=Longmai Technologies S: Product=USB Key C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Reported-by: Ai Chao Cc: stable Signed-off-by: Huang Wei Acked-by: Alan Stern Link: https://patch.msgid.link/20260716033341.2830872-1-huangwei@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 255968f9ca42..ac22fa318734 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -395,6 +395,13 @@ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110, USB_SC_DEVICE, USB_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), +/* Reported by Ai Chao */ +UNUSUAL_DEV( 0x04b4, 0xb708, 0x0000, 0xffff, + "Longmai Technologies", + "USB Key", + USB_SC_SCSI, USB_PR_BULK, NULL, + US_FL_NO_ATA_1X), + /* * Reported by Simon Levitt * This entry needs Sub and Proto fields -- cgit v1.2.3 From f797f51a185ffdc1e3f915afed55f308b376842f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 2 Jul 2026 20:13:35 +0100 Subject: arm64: mm: When logging data aborts only decode Xs when ISV=1 When logging the decode of a data abort we currently unconditionally decode and display Xs. Currently the only defined non-RES0 values for this field are for cases where ISV=1, move the decode of Xs into our existing check for ISV=1. This avoids potential confusion if some other use is assigned to these bits for ISV=0 cases in future, or misleading someone into thinking there is a meaningful value there with currently defined architecture. Signed-off-by: Mark Brown Signed-off-by: Will Deacon --- arch/arm64/mm/fault.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 85e23388f9bb..0b52557652be 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -76,6 +76,8 @@ static void data_abort_decode(unsigned long esr) pr_alert(" SF = %lu, AR = %lu\n", (esr & ESR_ELx_SF) >> ESR_ELx_SF_SHIFT, (esr & ESR_ELx_AR) >> ESR_ELx_AR_SHIFT); + pr_alert(" Xs = %llu\n", + (iss2 & ESR_ELx_Xs_MASK) >> ESR_ELx_Xs_SHIFT); } else { pr_alert(" ISV = 0, ISS = 0x%08lx, ISS2 = 0x%08lx\n", esr & ESR_ELx_ISS_MASK, iss2); @@ -87,11 +89,10 @@ static void data_abort_decode(unsigned long esr) (iss2 & ESR_ELx_TnD) >> ESR_ELx_TnD_SHIFT, (iss2 & ESR_ELx_TagAccess) >> ESR_ELx_TagAccess_SHIFT); - pr_alert(" GCS = %ld, Overlay = %lu, DirtyBit = %lu, Xs = %llu\n", + pr_alert(" GCS = %ld, Overlay = %lu, DirtyBit = %lu\n", (iss2 & ESR_ELx_GCS) >> ESR_ELx_GCS_SHIFT, (iss2 & ESR_ELx_Overlay) >> ESR_ELx_Overlay_SHIFT, - (iss2 & ESR_ELx_DirtyBit) >> ESR_ELx_DirtyBit_SHIFT, - (iss2 & ESR_ELx_Xs_MASK) >> ESR_ELx_Xs_SHIFT); + (iss2 & ESR_ELx_DirtyBit) >> ESR_ELx_DirtyBit_SHIFT); } static void mem_abort_decode(unsigned long esr) -- cgit v1.2.3 From e0b291fe117964037e0ba382eff4bb365d531c3a Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Thu, 11 Jun 2026 15:12:01 +0800 Subject: usb: core: port: Deattach Type-C connector on component unbind connector_unbind() is the mirror of connector_bind(), but it is missing the symmetric call to typec_deattach() that connector_bind() makes via: if (port_dev->child) typec_attach(port_dev->connector, &port_dev->child->dev); When a Thunderbolt dock is unplugged, two teardown paths race: 1. The component framework calls connector_unbind() first, which sets port_dev->connector = NULL without calling typec_deattach(). This leaves port->usb2_dev/port->usb3_dev in struct typec_port pointing at the USB device that is about to be freed. 2. usb_disconnect() then calls typec_deattach(port_dev->connector, ...), but port_dev->connector is already NULL, so the call is a no-op and port->usb2_dev is never cleared. 3. Concurrently, UCSI detects a PD partner-disconnect event and calls typec_unregister_partner(), which reads port->usb2_dev (now a dangling pointer to freed memory) and passes it to typec_partner_unlink_device() -> sysfs_remove_link() -> dev_name() on the freed device, corrupting the typec/UCSI partner state. This corruption leaves the Thunderbolt tunnel in an inconsistent state on the next dock hot-plug. On affected hardware the dock's I225/igc NIC fails to enumerate: AER fires a slot reset while the igc driver is still initialising ("PCIe link lost"), and the subsequent igc_reset attempt hits igc_rd32 on an already-detached device: igc 0000:2e:00.0 eth0: PCIe link lost, device now detached igc: Failed to read reg 0x0! WARNING: CPU: 9 PID: 129 at drivers/net/ethernet/intel/igc/igc_main.c:7005 igc_rd32+0xa4/0xc0 [igc] Call Trace: igc_disable_pcie_master+0x16/0xa0 [igc] igc_reset_hw_base+0x14/0x170 [igc] igc_reset+0x63/0x110 [igc] igc_io_slot_reset+0x9e/0xd0 [igc] report_slot_reset+0x5d/0xc0 pcie_do_recovery+0x209/0x400 aer_isr_one_error_type+0x235/0x430 aer_isr+0x4e/0x80 irq_thread+0xf4/0x1f0 4. UCSI later handles the PD partner-disconnect and calls typec_unregister_partner(), which still sees the stale port->usb2_dev and tries to remove its sysfs link a second time: kernfs: can not remove 'typec', no directory WARNING: CPU: 6 PID: 55 at fs/kernfs/dir.c:1706 kernfs_remove_by_name_ns+0xe9/0xf0 Workqueue: events ucsi_handle_connector_change [typec_ucsi] Call Trace: sysfs_remove_link+0x19/0x50 typec_unregister_partner+0x6e/0x120 [typec] ucsi_unregister_partner+0x107/0x150 [typec_ucsi] ucsi_handle_connector_change+0x3ec/0x490 [typec_ucsi] process_one_work+0x18e/0x3e0 worker_thread+0x2e3/0x420 kthread+0x10a/0x230 ret_from_fork+0x121/0x140 ret_from_fork_asm+0x1a/0x30 With worse timing the same stale pointer is dereferenced after the backing memory is freed, turning the warning into a use-after-free. Fix the asymmetry: call typec_deattach() before clearing port_dev->connector, matching what connector_bind() does on the bind side. typec_partner_deattach() is already protected by port->partner_link_lock, so it serialises safely with the concurrent typec_unregister_partner() path. Fixes: 11110783f5ea ("usb: Inform the USB Type-C class about enumerated devices") Cc: stable Signed-off-by: Chia-Lin Kao (AceLan) Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260611071201.1235545-1-acelan.kao@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/port.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index b1364f0c384c..b4452b665f59 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -740,6 +740,8 @@ static void connector_unbind(struct device *dev, struct device *connector, void sysfs_remove_link(&connector->kobj, dev_name(dev)); sysfs_remove_link(&dev->kobj, "connector"); + if (port_dev->child) + typec_deattach(port_dev->connector, &port_dev->child->dev); port_dev->connector = NULL; } -- cgit v1.2.3 From c947360ae63eee1c9eacc030dd6f5a53f717addf Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Jul 2026 19:47:11 +0800 Subject: usb: musb: omap2430: Do not put borrowed of_node in probe omap2430_probe() stores pdev->dev.of_node in a local np variable. This is a borrowed pointer and the probe function does not take a reference to it. The success and error paths nevertheless call of_node_put(np). This drops a reference that is owned by the platform device, and can leave pdev->dev.of_node with an unbalanced reference count. Do not put the borrowed platform device node from omap2430_probe(). References taken for the child MUSB device are handled by the device core, and the ctrl-module phandle reference is still released separately. Fixes: ffbe2feac59b ("usb: musb: omap2430: Fix probe regression for missing resources") Cc: stable Reviewed-by: Johan Hovold Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260713114711.955253-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 333ab79f0ca9..6e749faac33c 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -454,7 +454,6 @@ static int omap2430_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register musb device\n"); goto err_disable_rpm; } - of_node_put(np); return 0; @@ -464,7 +463,6 @@ err_put_control_otghs: if (!IS_ERR(glue->control_otghs)) put_device(glue->control_otghs); err_put_musb: - of_node_put(np); platform_device_put(musb); return ret; -- cgit v1.2.3 From 4e0197fbb0eec588795d5431716a244d9ac8fa93 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Wed, 15 Jul 2026 16:59:05 +0200 Subject: usb: core: sysfs: add lock to bos_descriptors_read() Add a lock to the function bos_descriptors_read(). This function accesses udev->bos, which could be simultaneously freed in usb_reset_and_verify_device(), a function that is commonly called in drivers all over the kernel. Assisted-by: gkh_clanker_t1000 Signed-off-by: Griffin Kroah-Hartman Acked-by: Alan Stern Cc: stable Link: https://patch.msgid.link/20260715-usb_core_patches_3-v1-1-53021f5576fd@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/sysfs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index a07866f1060c..d22dc78457d7 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -899,10 +899,15 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj, { struct device *dev = kobj_to_dev(kobj); struct usb_device *udev = to_usb_device(dev); - struct usb_host_bos *bos = udev->bos; + struct usb_host_bos *bos; struct usb_bos_descriptor *desc; size_t desclen, n = 0; + int rc; + rc = usb_lock_device_interruptible(udev); + if (rc < 0) + return -EINTR; + bos = udev->bos; if (bos) { desc = bos->desc; desclen = le16_to_cpu(desc->wTotalLength); @@ -911,6 +916,7 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj, memcpy(buf, (void *) desc + off, n); } } + usb_unlock_device(udev); return n; } static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */ -- cgit v1.2.3 From fc3afb5728e297994863f8a2a01b88a920bbf53e Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Thu, 16 Jul 2026 18:41:26 +0800 Subject: usb: chipidea: fix usage_count leak when autosuspend_delay is negative The probe() calls pm_runtime_use_autosuspend(), but remove() does not call pm_runtime_dont_use_autosuspend(). This can lead to a usage_count leak if autosuspend_delay is set to a negative value. The pm_runtime_use_autosuspend() also notes that it's important to undo this with pm_runtime_dont_use_autosuspend() at driver exit time. Fixes: 1f874edcb731 ("usb: chipidea: add runtime power management support") Cc: stable Assisted-by: Claude:claude-sonnet-4.6 Signed-off-by: Xu Yang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260716104126.2763454-1-xu.yang_2@oss.nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 07563be0013f..b53672f3ea63 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1250,6 +1250,7 @@ static void ci_hdrc_remove(struct platform_device *pdev) usb_role_switch_unregister(ci->role_switch); if (ci->supports_runtime_pm) { + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); -- cgit v1.2.3 From d5e5cd3654d2b5359a12ea6586120f05b28634ee Mon Sep 17 00:00:00 2001 From: Jinchao Wang Date: Thu, 16 Jul 2026 06:42:17 -0400 Subject: usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback dummy_hcd embeds a single shared usb_request (dum->fifo_req) that the "emulated single-request FIFO" fast-path in dummy_queue() reuses for small IN transfers: it copies the caller's request into it (req->req = *_req) and queues it, treating list_empty(&fifo_req.queue) as "the slot is free". The completion side (dummy_timer/transfer/nuke/dummy_dequeue) follows the standard pattern: list_del_init(&req->queue) unlinks the request, then the lock is dropped and usb_gadget_giveback_request() invokes req->complete(). But list_del_init() makes fifo_req.queue look empty *before* the completion callback returns, so a concurrent dummy_queue() on another CPU sees the slot as free, reuses fifo_req and runs req->req = *_req -- overwriting req->complete while dummy_timer is mid-calling it. The indirect call then jumps to a clobbered pointer, causing a general protection fault / page fault in dummy_timer (syzkaller extid faf3a6cf579fc65591ca). The clobbering write is an in-bounds memcpy on a live shared object, so KASAN cannot flag it. Add a fifo_req_busy bit covering the shared request's whole lifetime: set it in dummy_queue() when the FIFO fast-path takes fifo_req (making it the fast-path guard, replacing the list_empty(&fifo_req.queue) test), and clear it after the completion callback has returned, via a dummy_giveback() helper used at all four gadget-request giveback sites. The shared slot can no longer be reused until its completion callback has finished. Reported-by: syzbot+faf3a6cf579fc65591ca@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=faf3a6cf579fc65591ca Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Signed-off-by: Jinchao Wang Reviewed-by: Alan Stern Link: https://patch.msgid.link/5db8bba5b3499a86cd2e776f9918126b68b2508b.1784198306.git.wangjinchao600@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 40 +++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index f47903461ed5..c0e40fa6dde5 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -278,6 +278,7 @@ struct dummy { unsigned ints_enabled:1; unsigned udc_suspended:1; unsigned pullup:1; + unsigned fifo_req_busy:1; /* * HOST side support @@ -329,6 +330,26 @@ static inline struct dummy *gadget_dev_to_dummy(struct device *dev) /* DEVICE/GADGET SIDE UTILITY ROUTINES */ +/* + * Give back a gadget request with dum->lock dropped around the callback. + * If @req is the shared fifo_req, clear fifo_req_busy afterward: the flag + * was set in dummy_queue() when the shared request was taken and must stay + * set until its completion callback has returned; list_del_init() alone + * makes the request look idle while the callback is still running. + * Caller holds dum->lock and has already done list_del_init() + status. + */ +static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep, + struct dummy_request *req) +{ + bool fifo = req == &dum->fifo_req; + + spin_unlock(&dum->lock); + usb_gadget_giveback_request(_ep, &req->req); + spin_lock(&dum->lock); + if (fifo) + dum->fifo_req_busy = 0; +} + /* called with spinlock held */ static void nuke(struct dummy *dum, struct dummy_ep *ep) { @@ -339,9 +360,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep) list_del_init(&req->queue); req->req.status = -ESHUTDOWN; - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); } } @@ -728,10 +747,11 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req, /* implement an emulated single-request FIFO */ if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && - list_empty(&dum->fifo_req.queue) && + !dum->fifo_req_busy && list_empty(&ep->queue) && _req->length <= FIFO_SIZE) { req = &dum->fifo_req; + dum->fifo_req_busy = 1; req->req = *_req; req->req.buf = dum->fifo_buf; memcpy(dum->fifo_buf, _req->buf, _req->length); @@ -785,9 +805,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req) dev_dbg(udc_dev(dum), "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); - spin_unlock(&dum->lock); - usb_gadget_giveback_request(_ep, _req); - spin_lock(&dum->lock); + dummy_giveback(dum, _ep, req); } spin_unlock_irqrestore(&dum->lock, flags); return retval; @@ -1523,9 +1541,7 @@ top: if (req->req.status != -EINPROGRESS) { list_del_init(&req->queue); - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); /* requests might have been unlinked... */ rescan = 1; @@ -1910,9 +1926,7 @@ restart: dev_dbg(udc_dev(dum), "stale req = %p\n", req); - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); ep->already_seen = 0; goto restart; } -- cgit v1.2.3 From 67c92c6419ea6dbc5b1f3e9691aecea956e3e81c Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 13 Jul 2026 16:43:21 +0800 Subject: usb: typec: ucsi: Detect and skip duplicate altmodes from buggy firmware Some firmware implementations incorrectly return the same altmode multiple times at different offsets when queried via UCSI_GET_ALTERNATE_MODES. This causes sysfs duplicate filename errors and kernel call traces when the driver attempts to register the same altmode twice: sysfs: cannot create duplicate filename '/devices/.../typec/port0/port0.0/partner' typec-thunderbolt port0-partner.1: failed to create symlinks typec-thunderbolt port0-partner.1: probe with driver typec-thunderbolt failed with error -17 The matching rules differ by recipient: - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug): Two altmodes with identical SVID and VDO are byte-for-byte duplicates and the second has no observable function, so drop it. - UCSI_RECIPIENT_SOP (partner): The typec class binds each partner altmode to a port altmode of the same SVID via altmode_match()/device_find_child(), which returns the first port altmode with a matching SVID. If the partner advertises more altmodes for SVID X than the port advertises, the surplus partner altmode(s) collapse onto an already-paired port altmode and trigger the "duplicate filename .../partner" sysfs error during typec_altmode_create_links(). Use the port-side altmode count for SVID X as the authoritative cap and reject any partner altmode that would exceed it. This preserves legitimate multi-Mode partner altmodes (vendor SVIDs that the port really does advertise more than once) while filtering the firmware-generated duplicates that have no port counterpart, and is therefore stricter than a plain SVID+VDO comparison (which still admits the Thunderbolt case where firmware reports the same SVID twice with different VDOs) without being over-broad like a plain SVID match (which would falsely drop legitimate vendor multi-Mode entries). If a duplicate is detected, skip it and emit a clean warning instead of generating a kernel call trace: ucsi_acpi USBC000:00: con2: Firmware bug: duplicate partner altmode SVID 0x8087 at offset 1, ignoring. ucsi_acpi USBC000:00: con2: VDO mismatch: 0x8087a043 vs 0x00000001 The duplicate detection logic lives in a reusable helper ucsi_altmode_is_duplicate() and is invoked from ucsi_register_altmodes(). It applies to all three recipient types: partner (SOP), port (CON), and plug (SOP_P) altmodes. Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes") Cc: stable Signed-off-by: Chia-Lin Kao (AceLan) unchanged: still SVID+VDO exact-dup match. Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260713084323.287516-1-acelan.kao@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 132 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index f56897e23df8..0db43521af3f 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -529,6 +529,129 @@ err: return ret; } +static void ucsi_dump_duplicate_altmode(struct ucsi_connector *con, + u8 recipient, u16 svid, + u32 existing_vdo, u32 new_vdo, + int offset) +{ + static const char * const recipient_names[] = { + [UCSI_RECIPIENT_CON] = "port", + [UCSI_RECIPIENT_SOP] = "partner", + [UCSI_RECIPIENT_SOP_P] = "plug", + [UCSI_RECIPIENT_SOP_PP] = "cable plug prime", + }; + + dev_warn(con->ucsi->dev, + "con%d: Firmware bug: duplicate %s altmode SVID 0x%04x at offset %d, ignoring but please contact the BIOS vendor to fix this issue.\n", + con->num, recipient_names[recipient], svid, offset); + + if (existing_vdo != new_vdo) + dev_warn(con->ucsi->dev, + "con%d: VDO mismatch: 0x%08x vs 0x%08x\n", + con->num, existing_vdo, new_vdo); +} + +/* Count altmodes in @altmodes that advertise @svid. */ +static int ucsi_altmode_count_svid(struct typec_altmode **altmodes, u16 svid) +{ + int count = 0; + int k; + + for (k = 0; k < UCSI_MAX_ALTMODES; k++) { + if (!altmodes[k]) + break; + if (altmodes[k]->svid == svid) + count++; + } + + return count; +} + +/* + * Check if an altmode is a duplicate. Some firmware implementations + * incorrectly return the same altmode multiple times, causing sysfs errors. + * Returns true if the altmode should be skipped. + * + * The matching rules differ by recipient: + * + * - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug): + * Two altmodes with identical SVID and VDO are byte-for-byte duplicates + * and the second has no observable function. Drop them. + * + * - UCSI_RECIPIENT_SOP (partner): + * The typec class binds each partner altmode to a port altmode of the + * same SVID via altmode_match()/device_find_child(), which returns the + * first port altmode with a matching SVID. If the partner advertises + * more altmodes for SVID X than the port advertises, the surplus + * partner altmode(s) collapse onto an already-paired port altmode and + * trigger a "duplicate filename .../partner" sysfs error during + * typec_altmode_create_links(). Use the port-side altmode count for + * SVID X as the authoritative cap and reject any partner altmode that + * would exceed it. This preserves legitimate multi-Mode partner + * altmodes (e.g. vendor SVIDs that the port really does advertise + * twice) while filtering the firmware-generated duplicates that have + * no port counterpart. + */ +static bool ucsi_altmode_is_duplicate(struct ucsi_connector *con, u8 recipient, + const struct ucsi_altmode *alt_batch, int batch_idx, + u16 svid, u32 vdo, int offset) +{ + struct typec_altmode **altmodes; + int port_count, partner_count; + int k; + + /* Check for duplicates within the current batch first */ + for (k = 0; k < batch_idx; k++) { + if (alt_batch[k].svid == svid && alt_batch[k].mid == vdo) { + ucsi_dump_duplicate_altmode(con, recipient, svid, + vdo, vdo, offset); + return true; + } + } + + switch (recipient) { + case UCSI_RECIPIENT_SOP: + /* + * Cap partner altmodes per SVID by the port-side count: + * any further partner altmode for that SVID would alias an + * already-paired port altmode and break typec sysfs. + */ + port_count = ucsi_altmode_count_svid(con->port_altmode, svid); + partner_count = ucsi_altmode_count_svid(con->partner_altmode, + svid); + if (port_count && partner_count >= port_count) { + ucsi_dump_duplicate_altmode(con, recipient, svid, + con->partner_altmode[partner_count - 1]->vdo, + vdo, offset); + return true; + } + return false; + case UCSI_RECIPIENT_CON: + altmodes = con->port_altmode; + break; + case UCSI_RECIPIENT_SOP_P: + altmodes = con->plug_altmode; + break; + default: + return false; + } + + /* CON and SOP_P: drop only exact SVID+VDO duplicates. */ + for (k = 0; k < UCSI_MAX_ALTMODES; k++) { + if (!altmodes[k]) + break; + + if (altmodes[k]->svid != svid || altmodes[k]->vdo != vdo) + continue; + + ucsi_dump_duplicate_altmode(con, recipient, svid, + altmodes[k]->vdo, vdo, offset); + return true; + } + + return false; +} + static int ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) { @@ -653,6 +776,15 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) if (!alt[j].svid) return 0; + /* + * Check for duplicates in current batch and already + * registered altmodes. Skip if duplicate found. + */ + if (ucsi_altmode_is_duplicate(con, recipient, alt, j, + alt[j].svid, alt[j].mid, + i - num + j)) + continue; + memset(&desc, 0, sizeof(desc)); desc.vdo = alt[j].mid; desc.svid = alt[j].svid; -- cgit v1.2.3 From f1aa17f72f9b9589bd724dc826c5b17d164193d1 Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 13 Jul 2026 16:43:22 +0800 Subject: usb: typec: ucsi: Add duplicate detection to nvidia registration path Extend the duplicate altmode detection to ucsi_register_altmodes_nvidia() which is used when a driver provides the update_altmodes() callback. This ensures all drivers benefit from duplicate detection, whether they use the standard registration path or the nvidia path with update_altmodes callback. Without this fix, drivers using the nvidia path (like yoga_c630) would still encounter duplicate altmode registration errors from buggy firmware. Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes") Cc: stable Signed-off-by: Chia-Lin Kao (AceLan) Link: https://patch.msgid.link/20260713084323.287516-2-acelan.kao@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 0db43521af3f..1ae4224d2dfc 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -706,19 +706,25 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) /* now register altmodes */ for (i = 0; i < max_altmodes; i++) { - memset(&desc, 0, sizeof(desc)); - if (multi_dp) { - desc.svid = updated[i].svid; - desc.vdo = updated[i].mid; - } else { - desc.svid = orig[i].svid; - desc.vdo = orig[i].mid; - } - desc.roles = TYPEC_PORT_DRD; + struct ucsi_altmode *altmode_array = multi_dp ? updated : orig; - if (!desc.svid) + if (!altmode_array[i].svid) return 0; + /* + * Check for duplicates in current array and already + * registered altmodes. Skip if duplicate found. + */ + if (ucsi_altmode_is_duplicate(con, recipient, altmode_array, i, + altmode_array[i].svid, + altmode_array[i].mid, i)) + continue; + + memset(&desc, 0, sizeof(desc)); + desc.svid = altmode_array[i].svid; + desc.vdo = altmode_array[i].mid; + desc.roles = TYPEC_PORT_DRD; + ret = ucsi_register_altmode(con, &desc, recipient); if (ret) return ret; -- cgit v1.2.3 From 8706409bd45fb89350f213b8baac5f16a43bb4a4 Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 13 Jul 2026 16:43:23 +0800 Subject: usb: typec: ucsi: yoga_c630: Remove redundant duplicate altmode handling This reverts commit e0c48e42d818 ("usb: typec: ucsi: yoga-c630: remove duplicate AltModes"). The yoga_c630 driver previously implemented its own duplicate altmode detection in yoga_c630_ucsi_update_altmodes() to work around buggy EC firmware that returns duplicate AltModes instead of empty ones. With the introduction of the common ucsi_altmode_is_duplicate() helper in both the standard and nvidia registration paths, duplicate detection is now handled automatically in the core UCSI code. This makes the yoga_c630-specific implementation added in commit e0c48e42d818 ("usb: typec: ucsi: yoga-c630: remove duplicate AltModes") redundant. Remove yoga_c630_ucsi_update_altmodes() and its callback to eliminate code duplication and simplify the driver. Note that this causes the driver to switch back from the nvidia registration path to the standard path, which is the original behavior before commit e0c48e42d818 ("usb: typec: ucsi: yoga-c630: remove duplicate AltModes"). Both paths now include duplicate detection, ensuring the firmware bug is still properly handled. Signed-off-by: Chia-Lin Kao (AceLan) Link: https://patch.msgid.link/20260713084323.287516-3-acelan.kao@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_yoga_c630.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c index 1be18d101842..bb7b09bc7c1c 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -139,28 +139,6 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, return ret; } -static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi, - u8 recipient, - struct ucsi_altmode *orig, - struct ucsi_altmode *updated) -{ - int i; - - if (orig[0].svid == 0 || recipient != UCSI_RECIPIENT_SOP) - return false; - - /* EC is nice and repeats altmodes again and again. Ignore copies. */ - for (i = 1; i < UCSI_MAX_ALTMODES; i++) { - if (orig[i].svid == orig[0].svid) { - dev_dbg(ucsi->dev, "Found duplicate altmodes, starting from %d\n", i); - memset(&orig[i], 0, (UCSI_MAX_ALTMODES - i) * sizeof(*orig)); - break; - } - } - - return false; -} - static void yoga_c630_ucsi_update_connector(struct ucsi_connector *con) { if (con->num == 1) @@ -174,7 +152,6 @@ static const struct ucsi_operations yoga_c630_ucsi_ops = { .read_message_in = yoga_c630_ucsi_read_message_in, .sync_control = yoga_c630_ucsi_sync_control, .async_control = yoga_c630_ucsi_async_control, - .update_altmodes = yoga_c630_ucsi_update_altmodes, .update_connector = yoga_c630_ucsi_update_connector, }; -- cgit v1.2.3 From 1c0ae3df692ea2a4ce992f786346154e75a3f0d5 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Thu, 9 Jul 2026 17:05:30 +0200 Subject: ksmbd: fix integer overflow in set_file_allocation_info() set_file_allocation_info() converts the client-supplied FILE_ALLOCATION_INFORMATION::AllocationSize into a 512-byte block count with: alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; AllocationSize is a fully client-controlled __le64 field; the only validation performed by the caller (smb2_set_info_file(), case FILE_ALLOCATION_INFORMATION) is that the fixed buffer is at least sizeof(struct smb2_file_alloc_info) == 8 bytes. The value itself is never range-checked before this arithmetic. When AllocationSize is close to U64_MAX (e.g. 0xffffffffffffffff), "AllocationSize + 511" wraps around mod 2^64 to a small number (0xffffffffffffffff + 511 = 510), so alloc_blks becomes 0. Since any existing regular file has stat.blocks > 0, the function then takes the "shrink" branch and calls: ksmbd_vfs_truncate(work, fp, alloc_blks * 512); /* == 0 */ silently truncating the file to size 0, even though the client asked to grow the allocation to (what looks like) the maximum possible size. The trailing "if (size < alloc_blks * 512) i_size_write(inode, size);" restore is guarded by a comparison that is never true once alloc_blks == 0, so the truncation is not undone. This lets an authenticated SMB client that already holds an open handle with FILE_WRITE_DATA on a file silently truncate that same file to size 0 via a single crafted SET_INFO(FILE_ALLOCATION_INFORMATION) request advertising a near-U64_MAX AllocationSize, even though the request asks to grow the file's allocation rather than shrink it. This is a functional/data-loss bug, not a privilege-boundary violation: the same client could already truncate the file via FILE_END_OF_FILE_INFORMATION or a plain write. Fix it by validating AllocationSize against MAX_LFS_FILESIZE, the same upper bound the VFS itself uses to reject unrepresentable file sizes, before doing the "+511" rounding, and rejecting oversized values with -EINVAL. Bounding AllocationSize to MAX_LFS_FILESIZE - 511 guarantees the "+511" addition cannot wrap, and that the subsequent "alloc_blks * 512" values passed to vfs_fallocate() and ksmbd_vfs_truncate() stay within a representable loff_t as well. No legitimate SMB client asks for an allocation size anywhere near 2^64 bytes, so this only rejects a value that was previously silently misinterpreted as zero. Runtime-verified on a v6.19 KASAN test stand: sending SET_INFO (FILE_ALLOCATION_INFORMATION) with AllocationSize = 0xffffffffffffffff against ksmbd now returns -EINVAL and leaves the target file's size unchanged, where the unpatched kernel truncated it from 4096 to 0 bytes. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Assisted-by: AuditCode-AI:2026.07 Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index b73167785e87..320b46db9377 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6781,6 +6781,7 @@ static int set_file_allocation_info(struct ksmbd_work *work, */ loff_t alloc_blks; + u64 alloc_size; struct inode *inode; struct kstat stat; int rc; @@ -6796,7 +6797,19 @@ static int set_file_allocation_info(struct ksmbd_work *work, if (rc) return rc; - alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; + /* + * AllocationSize is fully client-controlled (the caller only + * validates the fixed 8-byte buffer length). Reject values that + * would overflow the "round up to 512-byte blocks" conversion + * below instead of silently wrapping it to a tiny block count, + * which would truncate the file to a size the client never + * asked for. + */ + alloc_size = le64_to_cpu(file_alloc_info->AllocationSize); + if (alloc_size > MAX_LFS_FILESIZE - 511) + return -EINVAL; + + alloc_blks = (alloc_size + 511) >> 9; inode = file_inode(fp->filp); if (alloc_blks > stat.blocks) { -- cgit v1.2.3 From aa5d8f3f96aa11a4a54ce993c11ce8af11c546f9 Mon Sep 17 00:00:00 2001 From: Qihang Date: Thu, 9 Jul 2026 22:49:55 +0800 Subject: ksmbd: pin conn during async oplock break notification smb2_oplock_break_noti() and smb2_lease_break_noti() store a ksmbd_conn pointer in an async ksmbd_work and then queue that work on ksmbd-io. The work only increments conn->r_count, which prevents teardown from passing the pending-request wait after the increment, but it does not pin the struct ksmbd_conn object. If connection teardown races with an oplock break notification, the last conn reference can be dropped before the queued worker finishes. The worker then uses the freed conn in ksmbd_conn_write() and ksmbd_conn_r_count_dec(). Take a real conn reference when publishing the conn pointer to the async work item, and drop it after the notification work has decremented r_count. Apply the same lifetime rule to lease break notification, which uses the same work->conn pattern. Fixes: 3aa660c05924 ("ksmbd: prevent connection release during oplock break notification") Signed-off-by: Qihang Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 3c55ae5d6a11..79787099afdc 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -875,6 +875,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) out: ksmbd_free_work_struct(work); ksmbd_conn_r_count_dec(conn); + ksmbd_conn_put(conn); } /** @@ -910,7 +911,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) br_info->open_trunc = opinfo->open_trunc; work->request_buf = (char *)br_info; - work->conn = conn; + work->conn = ksmbd_conn_get(conn); work->sess = opinfo->sess; ksmbd_conn_r_count_inc(conn); @@ -985,6 +986,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) out: ksmbd_free_work_struct(work); ksmbd_conn_r_count_dec(conn); + ksmbd_conn_put(conn); } /** @@ -1034,7 +1036,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack, memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); work->request_buf = (char *)br_info; - work->conn = conn; + work->conn = ksmbd_conn_get(conn); work->sess = opinfo->sess; ksmbd_conn_r_count_inc(conn); -- cgit v1.2.3 From b078f39af3818292687623dd433d996aef5e69c9 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 10 Jul 2026 20:23:34 +0900 Subject: ksmbd: zero the smb2_read alignment tail to avoid an infoleak Commit 6b9a2e09d4cc ("ksmbd: avoid zeroing the read buffer in smb2_read()") switched the SMB2 READ payload buffer from kvzalloc() to kvmalloc(), on the premise that only the nbytes actually read are ever transmitted, so the ALIGN(length, 8) tail need not be initialized. That premise does not hold for a compound response. ksmbd_vfs_read() fills only nbytes, leaving [nbytes, ALIGN(length, 8)) uninitialized. The aux payload is pinned as the last response iov with iov_len == nbytes, but when the READ is a member of a compound, init_chained_smb2_rsp() 8-byte-aligns the previous member by extending that same iov: new_len = ALIGN(len, 8); work->iov[work->iov_idx].iov_len += (new_len - len); inc_rfc1001_len(work->response_buf, new_len - len); so up to 7 uninitialized bytes of the kvmalloc()'d slab tail are sent to the client. When the read length is small the buffer is served from a general kmalloc slab, so those bytes can be stale kernel-heap contents, including pointer values -- an information leak usable to defeat KASLR. An authenticated client triggers it with a compound request containing a READ whose returned nbytes is not 8-aligned (for example [READ, CLOSE] with a 1-byte read). Zero only the alignment tail after the read, preserving the bulk no-zeroing optimization of 6b9a2e09d4cc. Fixes: 6b9a2e09d4cc ("ksmbd: avoid zeroing the read buffer in smb2_read()") Cc: stable@vger.kernel.org Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 320b46db9377..c407425cbba0 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7446,6 +7446,15 @@ int smb2_read(struct ksmbd_work *work) goto out; } + /* + * ksmbd_vfs_read() fills only nbytes; the [nbytes, ALIGN(nbytes, 8)) + * tail of the un-zeroed buffer is transmitted as compound-response + * alignment padding, leaking uninitialized kernel memory to the + * client. Zero just that tail. + */ + if (nbytes & 7) + memset(aux_payload_buf + nbytes, 0, ALIGN(nbytes, 8) - nbytes); + if ((nbytes == 0 && length != 0) || nbytes < mincount) { kvfree(aux_payload_buf); rsp->hdr.Status = STATUS_END_OF_FILE; -- cgit v1.2.3 From 08b9452a338db7b7f9560342a1fff30f7a58a226 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Fri, 10 Jul 2026 20:27:10 +0900 Subject: ksmbd: fix memory leak of xattr_stream_name in smb2_rename() On an SMB2 SET_INFO(FileRenameInformation) whose target names an alternate data stream, smb2_rename() obtains a formatted stream-name string from ksmbd_vfs_xattr_stream_name(), which allocates it with kasprintf() and returns it through an out-param: rc = ksmbd_vfs_xattr_stream_name(stream_name, &xattr_stream_name, ...); if (rc) goto out; rc = ksmbd_vfs_setxattr(..., xattr_stream_name, ...); if (rc < 0) { ... goto out; } goto out; xattr_stream_name is declared inside the alternate-data-stream block, but the out: label is outside that block and frees only new_name, so it cannot release xattr_stream_name. ksmbd_vfs_setxattr() takes a const char * and only reads the name, so it does not take ownership either. Both the setxattr-failure and the success path therefore leak the kasprintf()'d string. An authenticated client with a writable share can leak kernel memory on every stream rename, exhausting kernel memory over time. Free xattr_stream_name after its use, before the block's goto out. The two earlier goto out paths never assign the variable, so there is no double-free. Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c407425cbba0..c8acf9f04e45 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6597,9 +6597,8 @@ static int smb2_rename(struct ksmbd_work *work, pr_err("failed to store stream name in xattr: %d\n", rc); rc = -EINVAL; - goto out; } - + kfree(xattr_stream_name); goto out; } -- cgit v1.2.3 From 610346149d047a52a92c9a0eb329dd565b8f92c5 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Sun, 12 Jul 2026 00:00:00 +0000 Subject: ksmbd: fix stack buffer overflow in multichannel session-key copy Commit 4b706360ffb7 ("ksmbd: fix multichannel binding and enforce channel limit") moved the binding-path session key out of the session-wide sess->sess_key (CIFS_KEY_SIZE = 40) into a new per-channel buffer, and sized both that buffer and the on-stack copy used during binding with SMB2_NTLMV2_SESSKEY_SIZE (16): struct channel { char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; /* 16 */ ... }; ntlm_authenticate() / krb5_authenticate(): char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; /* 16 */ char *auth_key = conn->binding ? channel_key : sess->sess_key; The two writers that fill this destination still bound the copy length against CIFS_KEY_SIZE (40), not against the 16-byte buffer: ksmbd_decode_ntlmssp_auth_blob() (NTLM key exchange): if (sess_key_len > CIFS_KEY_SIZE) /* 40 */ return -EINVAL; arc4_crypt(ctx_arc4, sess_key, (char *)authblob + sess_key_off, sess_key_len); ksmbd_krb5_authenticate(): if (resp->session_key_len > sizeof(sess->sess_key)) /* 40 */ ... memcpy(sess_key, resp->payload, resp->session_key_len); On a binding SESSION_SETUP, auth_key points at the 16-byte channel_key, so a client that supplies an NTLM EncryptedRandomSessionKey of up to 40 bytes (with NTLMSSP_NEGOTIATE_KEY_EXCH), or a Kerberos ticket whose session key is longer than 16 bytes (a normal AES256 key is 32), writes past the 16-byte stack buffer -- up to a 24-byte kernel stack overflow. KASAN reports it as a stack-out-of-bounds write in arc4_crypt() called from ksmbd_decode_ntlmssp_auth_blob(). The destinations must be able to hold the full session key the length checks already permit. Size the per-channel key buffer and the two on-stack channel_key buffers with CIFS_KEY_SIZE, matching sess->sess_key. Fixes: 4b706360ffb7 ("ksmbd: fix multichannel binding and enforce channel limit") Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/mgmt/user_session.h | 2 +- fs/smb/server/smb2pdu.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index 8893a9aaede7..4637a8c8436d 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -19,7 +19,7 @@ struct ksmbd_file_table; struct channel { - char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; + char sess_key[CIFS_KEY_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 c8acf9f04e45..8686b9a4a696 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1689,7 +1689,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; struct ksmbd_user *user; - char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; + char channel_key[CIFS_KEY_SIZE] = {}; char *auth_key = conn->binding ? channel_key : sess->sess_key; u64 prev_id; bool binding = conn->binding; @@ -1826,7 +1826,7 @@ 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; - char channel_key[SMB2_NTLMV2_SESSKEY_SIZE] = {}; + char channel_key[CIFS_KEY_SIZE] = {}; char *auth_key = conn->binding ? channel_key : sess->sess_key; u64 prev_sess_id; bool binding = conn->binding; -- cgit v1.2.3 From b10665730fbf6b5e45fe422badcbbc3f0df96ef5 Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Sun, 12 Jul 2026 00:00:00 +0000 Subject: ksmbd: remove stale channels from all sessions on teardown ksmbd_sessions_deregister() removes a connection's channels from other sessions' channel lists only while conn->binding is still set: if (conn->binding) { hash_for_each_safe(sessions_table, ...) ksmbd_chann_del(conn, sess); } conn->binding is a transient flag: it is cleared once a binding SESSION_SETUP completes, and also by a subsequent non-binding SESSION_SETUP on the same connection (a reauthentication on a bound channel, or a new SessionId==0 setup). A connection that has bound a channel into another session's ksmbd_chann_list and then clears conn->binding leaves that channel behind when it disconnects: the channel, whose chann->conn points at the now freed struct ksmbd_conn, stays on the owner session's list. When the owning connection later tears down, the second loop dereferences the stale channel: xa_for_each(&sess->ksmbd_chann_list, chann_id, chann) if (chann->conn != conn) ksmbd_conn_set_exiting(chann->conn); /* freed */ which is a use-after-free write into the freed ksmbd_conn (the same stale channel is also walked by show_proc_session() through /proc). The session is leaked as well, because its channel list never empties. Remove the conn->binding gate so a connection always removes its channels from every session on teardown. Fixes: faf8578c77f3 ("ksmbd: find bound sessions during reauthentication") Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/mgmt/user_session.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index d6331184ebfc..f99c86284ba3 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -457,22 +457,19 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) { struct ksmbd_session *sess; unsigned long id; + struct hlist_node *tmp; + int bkt; down_write(&sessions_table_lock); - if (conn->binding) { - int bkt; - struct hlist_node *tmp; - - hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) { - if (!ksmbd_chann_del(conn, sess) && - xa_empty(&sess->ksmbd_chann_list)) { - hash_del(&sess->hlist); - down_write(&conn->session_lock); - xa_erase(&conn->sessions, sess->id); - up_write(&conn->session_lock); - if (atomic_dec_and_test(&sess->refcnt)) - ksmbd_session_destroy(sess); - } + hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) { + if (!ksmbd_chann_del(conn, sess) && + xa_empty(&sess->ksmbd_chann_list)) { + hash_del(&sess->hlist); + down_write(&conn->session_lock); + xa_erase(&conn->sessions, sess->id); + up_write(&conn->session_lock); + if (atomic_dec_and_test(&sess->refcnt)) + ksmbd_session_destroy(sess); } } -- cgit v1.2.3 From 35754558f6c21d0fa0985dceb3387071a3348aff Mon Sep 17 00:00:00 2001 From: Gil Portnoy Date: Mon, 13 Jul 2026 09:20:09 +0900 Subject: ksmbd: lock the binding preauth session in smb3_preauth_hash_rsp smb3_preauth_hash_rsp() computes the SMB3.1.1 preauth integrity hash on the response path. For a binding SESSION_SETUP it looks up the per-connection preauth_session and reads its Preauth_HashValue. smb2_sess_setup() frees that preauth_session under ksmbd_conn_lock(). Two SMB2 requests on one connection can run concurrently, so an unlocked lookup and hash can use a preauth_session after another worker frees it. Take ksmbd_conn_lock() before selecting conn->binding and hold it across the selected preauth hash lookup and update. This preserves the existing hash selection while preventing the lookup-to-use lifetime race. Fixes: 1c5daa2ea924 ("ksmbd: handle channel binding with a different user") Signed-off-by: Gil Portnoy Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 8686b9a4a696..bec692bca1ca 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -9808,22 +9808,21 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) } if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { - __u8 *hash_value; + ksmbd_conn_lock(conn); if (conn->binding) { struct preauth_session *preauth_sess; preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); - if (!preauth_sess) - return; - hash_value = preauth_sess->Preauth_HashValue; - } else { - hash_value = sess->Preauth_HashValue; - if (!hash_value) - return; + if (preauth_sess) + ksmbd_gen_preauth_integrity_hash(conn, + work->response_buf, + preauth_sess->Preauth_HashValue); + } else if (sess->Preauth_HashValue) { + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + sess->Preauth_HashValue); } - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - hash_value); + ksmbd_conn_unlock(conn); } } -- cgit v1.2.3 From 15b38176fd1530372905c602fde51fe89ec8c877 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Mon, 13 Jul 2026 21:55:10 +0000 Subject: ksmbd: validate compound request size before reading StructureSize2 When ksmbd validates a compound (chained) SMB2 request, ksmbd_smb2_check_message() reads pdu->StructureSize2 without first checking that the compound element is large enough to contain it. StructureSize2 is a 2-byte field at offset 64 (__SMB2_HEADER_STRUCTURE_SIZE) from the start of each element. The compound-walking logic only guarantees that a full 64-byte SMB2 header is present for the trailing element: when NextCommand is 0, len is reduced to the number of bytes remaining after next_smb2_rcv_hdr_off. A remote client can craft a compound request whose last element has exactly 64 bytes, so the 2-byte StructureSize2 read at offset 64 extends one byte past the receive buffer, producing a slab-out-of-bounds read. BUG: KASAN: slab-out-of-bounds in ksmbd_smb2_check_message (fs/smb/server/smb2misc.c:402) Read of size 2 at addr ffff888012ae31ac by task kworker/0:1/14 The buggy address is located 172 bytes inside of allocated 173-byte region Workqueue: ksmbd-io handle_ksmbd_work Call Trace: ... kasan_report (mm/kasan/report.c:595) ksmbd_smb2_check_message (fs/smb/server/smb2misc.c:402) handle_ksmbd_work (fs/smb/server/server.c:119) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3397) 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) Reject any compound element that is too small to hold StructureSize2 before dereferencing it. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2misc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c index c0c4edd092c2..9f3629c86291 100644 --- a/fs/smb/server/smb2misc.c +++ b/fs/smb/server/smb2misc.c @@ -407,6 +407,11 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) return 1; } + if (len < __SMB2_HEADER_STRUCTURE_SIZE + sizeof(__le16)) { + ksmbd_debug(SMB, "Message is too small for StructureSize2\n"); + return 1; + } + if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { if (!(command == SMB2_OPLOCK_BREAK_HE && (le16_to_cpu(pdu->StructureSize2) == OP_BREAK_STRUCT_SIZE_20 || -- cgit v1.2.3 From 50dce2e2f84b56d8b4b406d97a1543709e8a87f5 Mon Sep 17 00:00:00 2001 From: Abhishek Ojha Date: Wed, 15 Jul 2026 13:04:08 -0400 Subject: regulator: ltc3676: Fix incorrect IRQSTAT bit offsets The LTC3676_IRQSTAT_* bit definitions do not match the IRQSTAT (Interrupt Request Status) register layout documented in Table 15 of the LTC3676/LTC3676-1 datasheet: bit 0 - Pushbutton Status Active bit 1 - Hard Reset Occurred bit 2 - PGOOD Timeout Occurred bit 3 - Undervoltage Warning bit 4 - Undervoltage Standby (Fault) Occurred bit 5 - Overtemperature Warning bit 6 - Overtemperature Standby (Fault) Occurred bit 7 - Reserved The driver instead defines these starting at bit 3, one bit higher than the datasheet specifies, which causes ltc3676_regulator_isr() to check the wrong status bits and misreport (or miss) PGOOD timeout, undervoltage and thermal warning/fault conditions. Fix the bit offsets to match the datasheet. Fixes: 37b918a034fe ("regulator: Add LTC3676 support") Cc: stable@vger.kernel.org Signed-off-by: Abhishek Ojha Link: https://patch.msgid.link/20260715170408.295552-1-Abhishek.ojha@savoirfairelinux.com Signed-off-by: Mark Brown --- drivers/regulator/ltc3676.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c index 73d511eb1c1d..768cdce0d4ec 100644 --- a/drivers/regulator/ltc3676.c +++ b/drivers/regulator/ltc3676.c @@ -45,11 +45,11 @@ #define LTC3676_DVBxA_REF_SELECT BIT(5) #define LTC3676_DVBxB_PGOOD_MASK BIT(5) -#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(3) -#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(4) -#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(5) -#define LTC3676_IRQSTAT_THERMAL_WARN BIT(6) -#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(7) +#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(2) +#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(3) +#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(4) +#define LTC3676_IRQSTAT_THERMAL_WARN BIT(5) +#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(6) enum ltc3676_reg { LTC3676_SW1, -- cgit v1.2.3 From e89978c1cff54e265345c66e1177d19ea5a8bc00 Mon Sep 17 00:00:00 2001 From: Vinod Govindapillai Date: Mon, 15 Jun 2026 23:33:49 +0300 Subject: drm/i915/wm: clear the plane ddb_y entries on plane disable The UV/Y plane DDB entriess are never cleared on sk_wm_plane_disable_noatomic() and can leave stale DDB state for NV12 planes on pre-Gen11 devices Fixes: d34b59d5ba41 ("drm/i915: Add skl_wm_plane_disable_noatomic()") Assisted-by: Copilot:claude-sonnet-4.6 Signed-off-by: Vinod Govindapillai Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260615203355.218578-2-vinod.govindapillai@intel.com (cherry picked from commit 60f68a6ba298fd1e971a2d91576304bee89a16fc) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/skl_watermark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 5a3677ea25b0..a4ce21d4c024 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -3856,7 +3856,7 @@ void skl_wm_plane_disable_noatomic(struct intel_crtc *crtc, return; skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0); - skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0); + skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb_y[plane->id], 0, 0); crtc_state->wm.skl.plane_min_ddb[plane->id] = 0; crtc_state->wm.skl.plane_interim_ddb[plane->id] = 0; -- cgit v1.2.3 From 612978b83f45bf7018815209db5395d759db6f26 Mon Sep 17 00:00:00 2001 From: Emre Cecanpunar Date: Wed, 15 Jul 2026 01:04:30 +0300 Subject: drm/i915/selftests: Fix GT PM sort comparators Compare the sampled clock values instead of their addresses. Comparing addresses leaves the samples unsorted, preventing the code from discarding the minimum and maximum samples. Fixes: 1a5392479207 ("drm/i915/selftests: Measure CS_TIMESTAMP") Signed-off-by: Emre Cecanpunar Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260714220430.238433-1-emreleno@gmail.com (cherry picked from commit 682ea2d28d18bb06f9fc663cb5ab7e80dc0e606a) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/gt/selftest_gt_pm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c index 33351deeea4f..07eaf71955c4 100644 --- a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c +++ b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c @@ -16,9 +16,9 @@ static int cmp_u64(const void *A, const void *B) { const u64 *a = A, *b = B; - if (a < b) + if (*a < *b) return -1; - else if (a > b) + else if (*a > *b) return 1; else return 0; @@ -28,9 +28,9 @@ static int cmp_u32(const void *A, const void *B) { const u32 *a = A, *b = B; - if (a < b) + if (*a < *b) return -1; - else if (a > b) + else if (*a > *b) return 1; else return 0; -- cgit v1.2.3 From efd80de2de9d06ddf0eee55ca11b04e39bfc7cd8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 16 Jul 2026 15:42:09 +0100 Subject: ASoC: Intel: sof_sdw: Add quirks for new Dell laptops A couple of new Dell laptops are shipping using the sidecar amp configuration. Add the required kernel quirk to enable. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260716144209.2411523-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 59eb0328bbb5..c527d575d1ed 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -849,6 +849,8 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }; static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = { + SND_PCI_QUIRK(0x1028, 0x0e53, "Dell XPS WCL", SOC_SDW_SIDECAR_AMPS), + SND_PCI_QUIRK(0x1028, 0x0e54, "Dell XPS PTL", SOC_SDW_SIDECAR_AMPS), SND_PCI_QUIRK(0x1043, 0x1e13, "ASUS Zenbook S14", SOC_SDW_CODEC_MIC), SND_PCI_QUIRK(0x1043, 0x1f43, "ASUS Zenbook S16", SOC_SDW_CODEC_MIC), SND_PCI_QUIRK(0x17aa, 0x2347, "Lenovo P16", SOC_SDW_CODEC_MIC), -- cgit v1.2.3 From e66ddfd94b829b8ecaaac932d9487fb4de6d267c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:01 +0300 Subject: wifi: iwlwifi: mld: validate txq_id in TX response handler Validate txq_id from TX response notification before passing to iwl_trans_reclaim(). Other reclaim paths in this file perform this check to prevent out-of-bounds access on malformed notifications. Assisted-by: GitHubCopilot:claude-haiku-4.5 Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.b938c2dcf08d.I8a88ec359e229f1c41ac9c49fd9ce28a2b62b274@changeid --- drivers/net/wireless/intel/iwlwifi/mld/tx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c index 2185dade95f8..2df6643a5aa8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -1120,6 +1120,10 @@ void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, ssn = le32_to_cpup((__le32 *)agg_status + tx_resp->frame_count) & 0xFFFF; + if (IWL_FW_CHECK(mld, txq_id >= ARRAY_SIZE(mld->fw_id_to_txq), + "Invalid txq id %d\n", txq_id)) + return; + __skb_queue_head_init(&skbs); /* we can free until ssn % q.n_bd not inclusive */ -- cgit v1.2.3 From 7786233bffdd88b7bab816012a41597c2065470c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:02 +0300 Subject: wifi: iwlwifi: add support for additional channels in NVM_GET_INFO We need to expect more channels on devices that support UNII-9. Since iwl_ext_nvm_channels and iwl_uhb_nvm_channels are just a prefix of iwl_unii9_nvm_channels just use iwl_unii9_nvm_channels and modify the number of channels if the device does not support UNII-9 channels. Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.473f48e6135d.I0e93cda753558aa4d9e3efcdd52cbc2eb9302c4a@changeid --- drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c | 1 + .../net/wireless/intel/iwlwifi/fw/api/nvm-reg.h | 33 ++++++++++- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 2 + drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 64 ++++++++++++++-------- 4 files changed, 74 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c index 7a04cb120b1b..6080f5f23e69 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c @@ -15,6 +15,7 @@ .non_shared_ant = ANT_B, \ .vht_mu_mimo_supported = true, \ .uhb_supported = true, \ + .unii9_supported = true, \ .eht_supported = true, \ .uhr_supported = true, \ .num_rbds = IWL_NUM_RBDS_EHT, \ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 443a9a416325..0172c0747a47 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -205,6 +205,7 @@ struct iwl_nvm_get_info_phy { #define IWL_NUM_CHANNELS_V1 51 #define IWL_NUM_CHANNELS_V2 110 +#define IWL_NUM_CHANNELS_V3 115 /** * struct iwl_nvm_get_info_regulatory_v1 - regulatory information @@ -219,12 +220,12 @@ struct iwl_nvm_get_info_regulatory_v1 { } __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */ /** - * struct iwl_nvm_get_info_regulatory - regulatory information + * struct iwl_nvm_get_info_regulatory_v2 - regulatory information * @lar_enabled: is LAR enabled * @n_channels: number of valid channels in the array * @channel_profile: regulatory data of this channel */ -struct iwl_nvm_get_info_regulatory { +struct iwl_nvm_get_info_regulatory_v2 { __le32 lar_enabled; __le32 n_channels; __le32 channel_profile[IWL_NUM_CHANNELS_V2]; @@ -244,6 +245,32 @@ struct iwl_nvm_get_info_rsp_v3 { struct iwl_nvm_get_info_regulatory_v1 regulatory; } __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_3 */ +/** + * struct iwl_nvm_get_info_rsp_v4 - response to get NVM data + * @general: general NVM data + * @mac_sku: data relating to MAC sku + * @phy_sku: data relating to PHY sku + * @regulatory: regulatory data + */ +struct iwl_nvm_get_info_rsp_v4 { + struct iwl_nvm_get_info_general general; + struct iwl_nvm_get_info_sku mac_sku; + struct iwl_nvm_get_info_phy phy_sku; + struct iwl_nvm_get_info_regulatory_v2 regulatory; +} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_4 */ + +/** + * struct iwl_nvm_get_info_regulatory - regulatory information + * @lar_enabled: is LAR enabled + * @n_channels: number of valid channels in the array + * @channel_profile: regulatory data of this channel + */ +struct iwl_nvm_get_info_regulatory { + __le32 lar_enabled; + __le32 n_channels; + __le32 channel_profile[IWL_NUM_CHANNELS_V3]; +} __packed; /* REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_3 */ + /** * struct iwl_nvm_get_info_rsp - response to get NVM data * @general: general NVM data @@ -256,7 +283,7 @@ struct iwl_nvm_get_info_rsp { struct iwl_nvm_get_info_sku mac_sku; struct iwl_nvm_get_info_phy phy_sku; struct iwl_nvm_get_info_regulatory regulatory; -} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_4 */ +} __packed; /* REGULATORY_NVM_GET_INFO_RSP_API_S_VER_5 */ /** * struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 6a3539ad7331..3ac7f000ede4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -415,6 +415,7 @@ struct iwl_mac_cfg { * @vht_mu_mimo_supported: VHT MU-MIMO support * @nvm_type: see &enum iwl_nvm_type * @uhb_supported: ultra high band channels supported + * @unii9_supported: UNII-9 channels supported * @eht_supported: EHT supported * @uhr_supported: UHR supported * @num_rbds: number of receive buffer descriptors to use @@ -450,6 +451,7 @@ struct iwl_rf_cfg { lp_xtal_workaround:1, vht_mu_mimo_supported:1, uhb_supported:1, + unii9_supported:1, eht_supported:1, uhr_supported:1; u8 valid_tx_ant; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index d47b4ae2f486..bd5353fdde81 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -85,16 +85,7 @@ static const u16 iwl_nvm_channels[] = { 149, 153, 157, 161, 165 }; -static const u16 iwl_ext_nvm_channels[] = { - /* 2.4 GHz */ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - /* 5 GHz */ - 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, - 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, - 149, 153, 157, 161, 165, 169, 173, 177, 181 -}; - -static const u16 iwl_uhb_nvm_channels[] = { +static const u16 iwl_unii9_nvm_channels[] = { /* 2.4 GHz */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 5 GHz */ @@ -105,12 +96,16 @@ static const u16 iwl_uhb_nvm_channels[] = { 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, - 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233 + 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233, + + /* UNII-9 */ + 237, 241, 245, 249, 253 }; #define IWL_NVM_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) -#define IWL_NVM_NUM_CHANNELS_EXT ARRAY_SIZE(iwl_ext_nvm_channels) -#define IWL_NVM_NUM_CHANNELS_UHB ARRAY_SIZE(iwl_uhb_nvm_channels) +#define IWL_NVM_NUM_CHANNELS_EXT 51 +#define IWL_NVM_NUM_CHANNELS_UHB 110 +#define IWL_NVM_NUM_CHANNELS_UNII9 ARRAY_SIZE(iwl_unii9_nvm_channels) #define NUM_2GHZ_CHANNELS 14 #define NUM_5GHZ_CHANNELS 37 #define FIRST_2GHZ_HT_MINUS 5 @@ -351,12 +346,15 @@ static int iwl_init_channel_map(struct iwl_trans *trans, int num_of_ch; const u16 *nvm_chan; - if (cfg->uhb_supported) { + if (cfg->unii9_supported) { + num_of_ch = IWL_NVM_NUM_CHANNELS_UNII9; + nvm_chan = iwl_unii9_nvm_channels; + } else if (cfg->uhb_supported) { num_of_ch = IWL_NVM_NUM_CHANNELS_UHB; - nvm_chan = iwl_uhb_nvm_channels; + nvm_chan = iwl_unii9_nvm_channels; } else if (cfg->nvm_type == IWL_NVM_EXT) { num_of_ch = IWL_NVM_NUM_CHANNELS_EXT; - nvm_chan = iwl_ext_nvm_channels; + nvm_chan = iwl_unii9_nvm_channels; } else { num_of_ch = IWL_NVM_NUM_CHANNELS; nvm_chan = iwl_nvm_channels; @@ -1441,7 +1439,9 @@ iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, u8 rx_chains = fw->valid_rx_ant; u8 tx_chains = fw->valid_rx_ant; - if (cfg->uhb_supported) + if (cfg->unii9_supported) + data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UNII9); + else if (cfg->uhb_supported) data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UHB); else data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_EXT); @@ -1506,7 +1506,9 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, u16 lar_config; const __le16 *ch_section; - if (cfg->uhb_supported) + if (cfg->unii9_supported) + data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UNII9); + else if (cfg->uhb_supported) data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS_UHB); else if (cfg->nvm_type != IWL_NVM_EXT) data = kzalloc_flex(*data, channels, IWL_NVM_NUM_CHANNELS); @@ -1727,12 +1729,15 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans, int max_num_ch; struct iwl_reg_capa reg_capa; - if (cfg->uhb_supported) { + if (cfg->unii9_supported) { + max_num_ch = IWL_NVM_NUM_CHANNELS_UNII9; + nvm_chan = iwl_unii9_nvm_channels; + } else if (cfg->uhb_supported) { max_num_ch = IWL_NVM_NUM_CHANNELS_UHB; - nvm_chan = iwl_uhb_nvm_channels; + nvm_chan = iwl_unii9_nvm_channels; } else if (cfg->nvm_type == IWL_NVM_EXT) { max_num_ch = IWL_NVM_NUM_CHANNELS_EXT; - nvm_chan = iwl_ext_nvm_channels; + nvm_chan = iwl_unii9_nvm_channels; } else { max_num_ch = IWL_NVM_NUM_CHANNELS; nvm_chan = iwl_nvm_channels; @@ -2087,13 +2092,26 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, struct iwl_nvm_get_info_rsp_v3 *rsp_v3; bool v4 = fw_has_api(&fw->ucode_capa, IWL_UCODE_TLV_API_REGULATORY_NVM_INFO); - size_t rsp_size = v4 ? sizeof(*rsp) : sizeof(*rsp_v3); + size_t rsp_size; void *channel_profile; ret = iwl_trans_send_cmd(trans, &hcmd); if (ret) return ERR_PTR(ret); + switch (iwl_fw_lookup_notif_ver(fw, REGULATORY_AND_NVM_GROUP, + NVM_GET_INFO, 0)) { + case 5: + rsp_size = sizeof(struct iwl_nvm_get_info_rsp); + break; + case 4: + rsp_size = sizeof(struct iwl_nvm_get_info_rsp_v4); + break; + default: + rsp_size = sizeof(struct iwl_nvm_get_info_rsp_v3); + break; + } + if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != rsp_size, "Invalid payload len in NVM response from FW %d", iwl_rx_packet_payload_len(hcmd.resp_pkt))) { @@ -2107,7 +2125,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, if (empty_otp) IWL_INFO(trans, "OTP is empty\n"); - nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V2); + nvm = kzalloc_flex(*nvm, channels, IWL_NUM_CHANNELS_V3); if (!nvm) { ret = -ENOMEM; goto out; -- cgit v1.2.3 From 8d70881707b47353359df57df12f6de67fdacdd2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:03 +0300 Subject: wifi: iwlwifi: mvm: validate TX_CMD response layout TX_CMD parsing uses frame_count to walk status entries and then read the trailing SCD SSN. Make the minimum-length check follow that exact runtime layout calculation before parsing the payload. For new TX API, reject TX_CMD responses with frame_count != 1 and warn/return in the aggregation handler to document that aggregated accounting is expected via BA notifications. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.0474ee89bab9.I84f151aabecb8921b587da092f29f78c47128f0f@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index dc69c71faa76..d8b088e7c250 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1544,6 +1544,17 @@ static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm, return val & 0xFFF; } +static inline size_t iwl_mvm_tx_resp_min_len(struct iwl_mvm *mvm, + struct iwl_tx_resp *tx_resp) +{ + struct agg_tx_status *agg_status = + iwl_mvm_get_agg_status(mvm, tx_resp); + + /* The aggregate response ends with a trailing SCD SSN __le32 word. */ + return (u8 *)(agg_status + tx_resp->frame_count) - (u8 *)tx_resp + + sizeof(__le32); +} + static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -1847,6 +1858,9 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, int queue = SEQ_TO_QUEUE(sequence); struct ieee80211_sta *sta; + if (WARN_ON_ONCE(iwl_mvm_has_new_tx_api(mvm))) + return; + if (WARN_ON_ONCE(queue < IWL_MVM_DQA_MIN_DATA_QUEUE && (queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE))) return; @@ -1881,6 +1895,26 @@ void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_tx_resp *tx_resp = (void *)pkt->data; + size_t min_len; + + if (IWL_FW_CHECK(mvm, !tx_resp->frame_count, + "invalid TX_CMD frame_count %u\n", + tx_resp->frame_count)) + return; + + if (IWL_FW_CHECK(mvm, + iwl_mvm_has_new_tx_api(mvm) && + tx_resp->frame_count != 1, + "invalid TX_CMD frame_count %u for new TX API\n", + tx_resp->frame_count)) + return; + + min_len = iwl_mvm_tx_resp_min_len(mvm, tx_resp); + if (IWL_FW_CHECK(mvm, iwl_rx_packet_payload_len(pkt) < min_len, + "invalid TX_CMD len %u (frame_count %u, min %zu)\n", + iwl_rx_packet_payload_len(pkt), tx_resp->frame_count, + min_len)) + return; if (tx_resp->frame_count == 1) iwl_mvm_rx_tx_cmd_single(mvm, pkt); -- cgit v1.2.3 From 408d7da38272ce48e2db79b8a9895999f94d7655 Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Wed, 15 Jul 2026 21:57:04 +0300 Subject: wifi: iwlwifi: mvm: validate SAR GEO response payload size The SAR GEO command response is cast to iwl_geo_tx_power_profiles_resp without verifying the payload length. A malformed or unexpected firmware response can lead to reading an invalid structure layout. Add an explicit size check before accessing the response data and return -EIO when the payload size is wrong. Fixes: f604324eefec ("iwlwifi: remove iwl_validate_sar_geo_profile() export") Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.7e749b7d374a.I4ef54548bff6c6e7c7a57bee771ac12508aad677@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 6e507d6dcdd2..fa523be91d8a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -964,12 +964,22 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) return ret; } + if (IWL_FW_CHECK(mvm, + iwl_rx_packet_payload_len(cmd.resp_pkt) != + sizeof(*resp), + "Wrong size for iwl_geo_tx_power_profiles_resp: %d\n", + iwl_rx_packet_payload_len(cmd.resp_pkt))) { + ret = -EIO; + goto out; + } + resp = (void *)cmd.resp_pkt->data; ret = le32_to_cpu(resp->profile_idx); if (WARN_ON(ret > BIOS_GEO_MAX_PROFILE_NUM)) ret = -EIO; +out: iwl_free_resp(&cmd); return ret; } -- cgit v1.2.3 From a31b0e535fd11219556c7382ee9f63b2438c3769 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Wed, 15 Jul 2026 21:57:05 +0300 Subject: wifi: iwlwifi: fw: validate SMEM response size The SMEM parsers cast firmware response payloads directly to shared memory configuration structures. A short response can leave fields outside the received payload while the driver still dereferences them. Check the response payload length before reading the base fields in both parser variants. Require the full legacy extended layout before reading internal TX FIFO data. Valid responses keep the same parsed values. Signed-off-by: Shahar Tzarfati Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.fbdb0016a91d.I5f6c6e04589a24a233559191170ccb43372dee63@changeid --- drivers/net/wireless/intel/iwlwifi/fw/smem.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c index 344ddde85b18..20ed26a1bb00 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021, 2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2021, 2025-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -14,9 +14,17 @@ static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt, { struct iwl_shared_mem_cfg *mem_cfg = (void *)pkt->data; int i, lmac; - int lmac_num = le32_to_cpu(mem_cfg->lmac_num); - u8 api_ver = iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP, - SHARED_MEM_CFG_CMD, 0); + int lmac_num; + u8 api_ver; + + if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < + offsetofend(struct iwl_shared_mem_cfg, lmac_smem[1]), + "bad shared mem notification size\n")) + return; + + lmac_num = le32_to_cpu(mem_cfg->lmac_num); + api_ver = iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP, + SHARED_MEM_CFG_CMD, 0); /* Note: notification has 3 entries, but we only expect 2 */ if (IWL_FW_CHECK(fwrt, lmac_num > ARRAY_SIZE(fwrt->smem_cfg.lmac), @@ -30,7 +38,7 @@ static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt, if (api_ver >= 4 && !IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < sizeof(*mem_cfg), - "bad shared mem notification size\n")) { + "bad shared mem notification size (v4)\n")) { fwrt->smem_cfg.rxfifo2_control_size = le32_to_cpu(mem_cfg->rxfifo2_control_size); } @@ -53,6 +61,11 @@ static void iwl_parse_shared_mem(struct iwl_fw_runtime *fwrt, struct iwl_shared_mem_cfg_v2 *mem_cfg = (void *)pkt->data; int i; + if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < + offsetof(struct iwl_shared_mem_cfg_v2, rxfifo_addr), + "bad shared mem notification size\n")) + return; + fwrt->smem_cfg.num_lmacs = 1; fwrt->smem_cfg.num_txfifo_entries = ARRAY_SIZE(mem_cfg->txfifo_size); @@ -67,6 +80,11 @@ static void iwl_parse_shared_mem(struct iwl_fw_runtime *fwrt, /* new API has more data, from rxfifo_addr field and on */ if (fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { + if (IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < + sizeof(*mem_cfg), + "bad shared mem notification size (extend)\n")) + return; + BUILD_BUG_ON(sizeof(fwrt->smem_cfg.internal_txfifo_size) != sizeof(mem_cfg->internal_txfifo_size)); -- cgit v1.2.3 From bc796f84ec9a95b356959ec7caf1d4fce33f3a76 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:06 +0300 Subject: wifi: iwlwifi: fix pointer arithmetic in iwl_add_mcc_to_tas_block_list The expression list[*size++] increments the pointer 'size' rather than the u8 value it points to (operator precedence: ++ binds to the pointer before the dereference). As a result the block-list entry is written at the correct index but *size is never incremented, so the caller's count stays at zero and subsequent calls overwrite slot 0 every time. Change to list[(*size)++] so that the value pointed to by size is incremented after use as the array index. Fixes: 5f4656610edb ("wifi: iwlwifi: extend TAS_CONFIG cmd support for v5") Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.d2cd92242582.Ife4140a4e27be2a1cd9f886c5a9b376ce182a019@changeid --- drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index 8d9ff36e30f5..1d6d38ee55b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -389,7 +389,7 @@ bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc) if (*size >= IWL_WTAS_BLACK_LIST_MAX) return false; - list[*size++] = mcc; + list[(*size)++] = mcc; return true; } IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list); -- cgit v1.2.3 From daec24a5ed5da77a108e246ad77aa8b889911f93 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:07 +0300 Subject: wifi: iwlwifi: validate payload length in iwl_pnvm_complete_fn iwl_pnvm_complete_fn() casts pkt->data directly to struct iwl_pnvm_init_complete_ntfy and reads the status field without first verifying that the firmware notification payload is large enough to contain that structure. Add a WARN_ON_ONCE check against sizeof(*pnvm_ntf) and return early without reading uninitialised memory if the payload is too short. Fixes: b3e4c0f34c17 ("iwlwifi: move PNVM implementation to common code") Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.7f2a669e5c75.I00465dcfcbccb250ae9af2d9bb305e24de1ba394@changeid --- drivers/net/wireless/intel/iwlwifi/fw/pnvm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index afff8d51ca95..ec0ff58ab312 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2020-2025 Intel Corporation + * Copyright(c) 2020-2026 Intel Corporation */ #include "iwl-drv.h" @@ -12,6 +12,7 @@ #include "fw/api/alive.h" #include "fw/uefi.h" #include "fw/img.h" +#include "fw/dbg.h" #define IWL_PNVM_REDUCED_CAP_BIT BIT(25) @@ -26,6 +27,12 @@ static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait, struct iwl_trans *trans = (struct iwl_trans *)data; struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data; + if (IWL_FW_CHECK(trans, + iwl_rx_packet_payload_len(pkt) < sizeof(*pnvm_ntf), + "Bad notif len: %d\n", + iwl_rx_packet_payload_len(pkt))) + return true; + IWL_DEBUG_FW(trans, "PNVM complete notification received with status 0x%0x\n", le32_to_cpu(pnvm_ntf->status)); -- cgit v1.2.3 From 9d7657aae8c1579584c67b0b66114a6a98db8b2f Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Wed, 15 Jul 2026 21:57:08 +0300 Subject: wifi: iwlwifi: mvm: fix read in wake packet notification handler In iwl_mvm_wowlan_store_wake_pkt(), packet_len was initialized from notif->wake_packet_length before the explicit check that len >= sizeof(*notif). Move the assignment of packet_len to after the size check so that notif->wake_packet_length is only accessed once the payload length has been validated. Fixes: 219ed58feda9 ("wifi: iwlwifi: mvm: Add support for wowlan wake packet notification") Signed-off-by: Shahar Tzarfati Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.99d5cf85a528.Ic4aa736011d4fe88e0cd19723d1d48bb24642198@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 3429d9a10e42..4eaba0bc4a1e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2756,7 +2756,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm, struct iwl_wowlan_status_data *status, u32 len) { - u32 data_size, packet_len = le32_to_cpu(notif->wake_packet_length); + u32 data_size, packet_len; if (len < sizeof(*notif)) { IWL_ERR(mvm, "Invalid WoWLAN wake packet notification!\n"); @@ -2775,6 +2775,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm, return -EIO; } + packet_len = le32_to_cpu(notif->wake_packet_length); data_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, wake_packet); /* data_size got the padding from the notification, remove it. */ -- cgit v1.2.3 From 7455ba7b4d6d869e337db063c466033cf9392fb5 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:09 +0300 Subject: wifi: iwlwifi: mvm: ptp: free response on success path Release CMD_WANT_SKB response buffer after successful timestamp parsing to avoid leaking response allocations. Assisted-by: GitHubCopilot:GPT-5.3-Codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.31b38bef398b.Ib6a5a8bdd800779c8911da6859fd450d3d19c9e9@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/ptp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c index f39eb48864eb..49dcb1388007 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2021 - 2023, 2025 Intel Corporation + * Copyright (C) 2021 - 2023, 2025-2026 Intel Corporation */ #include "mvm.h" @@ -121,6 +121,8 @@ iwl_mvm_get_crosstimestamp_fw(struct iwl_mvm *mvm, u32 *gp2, u64 *sys_time) *sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 | le32_to_cpu(resp->platform_timestamp_lo); + iwl_free_resp(&cmd); + return ret; } -- cgit v1.2.3 From 7b271df210848e8d0dd9c0d7b032050b3f3affbf Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:10 +0300 Subject: wifi: iwlwifi: pcie: validate FW section counts in iwl_pcie_init_fw_sec iwl_pcie_init_fw_sec() iterates over LMAC, UMAC, and paging firmware sections and writes to ctxt_dram->lmac_img[i], ctxt_dram->umac_img[i], and ctxt_dram->virtual_img[i] without first verifying that the counts derived from the firmware image do not exceed the array size. An oversized firmware image could cause out-of-bounds writes into the fixed-size context-info DRAM arrays. Add explicit WARN_ON checks for all three section counts and return -EINVAL if any is exceeded. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.34db46ca12f3.I1aa225492a62f25293c147aa7293afa80a5d4215@changeid --- drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index d5eb895144ef..7f886200d693 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "iwl-trans.h" #include "iwl-fh.h" @@ -99,6 +99,11 @@ int iwl_pcie_init_fw_sec(struct iwl_trans *trans, /* add 2 due to separators */ paging_cnt = iwl_pcie_get_num_sections(fw, lmac_cnt + umac_cnt + 2); + if (WARN_ON(lmac_cnt > ARRAY_SIZE(ctxt_dram->lmac_img) || + umac_cnt > ARRAY_SIZE(ctxt_dram->umac_img) || + paging_cnt > ARRAY_SIZE(ctxt_dram->virtual_img))) + return -EINVAL; + dram->fw = kzalloc_objs(*dram->fw, umac_cnt + lmac_cnt); if (!dram->fw) return -ENOMEM; -- cgit v1.2.3 From 94d3982806c7f194b23484befde12934dda23064 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:11 +0300 Subject: wifi: iwlwifi: mvm: fix out-of-bounds tid_data access in BA notif mvmsta->tid_data was indexed by the TFD loop counter 'i' instead of the actual TID value 'tid'. This writes lq_color into a random tid_data slot unrelated to the BA entry. Since multi-TID blockack is not really in use, 'i' was always 0 and no harm was done. Add a out-of-bound check before accessing the array. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.919edee567eb.Ie85c350e3afe2b39709d0039072740d86660f8ae@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index d8b088e7c250..e02c376296c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -2134,8 +2134,14 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) if (tid == IWL_MGMT_TID) tid = IWL_MAX_TID_COUNT; + if (IWL_FW_CHECK(mvm, tid >= + ARRAY_SIZE(mvmsta->tid_data), + "invalid TID %d in compressed BA\n", + tid)) + continue; + if (mvmsta) - mvmsta->tid_data[i].lq_color = lq_color; + mvmsta->tid_data[tid].lq_color = lq_color; iwl_mvm_tx_reclaim(mvm, sta_id, tid, (int)(le16_to_cpu(ba_tfd->q_num)), -- cgit v1.2.3 From 0e4c0d83267261cf67ec9690856edf4a56bb7dfc Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:12 +0300 Subject: wifi: iwlwifi: mvm: add a check on the tid coming from the firmware ba_notif->tid is a firmware-controlled u8 that is used directly as an array index into tid_data[] without any validation. Add a bounds check against IWL_MAX_TID_COUNT before dereferencing the array. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.d7c3e75d47af.If88948108cfc8b5fb3ce5531d927855d1b3b6b30@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index e02c376296c4..d3c2fe830477 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -2161,6 +2161,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) ba_notif = (void *)pkt->data; sta_id = ba_notif->sta_id; tid = ba_notif->tid; + if (IWL_FW_CHECK(mvm, tid >= ARRAY_SIZE(mvmsta->tid_data), + "invalid TID %d in BA notif\n", tid)) + return; /* "flow" corresponds to Tx queue */ txq = le16_to_cpu(ba_notif->scd_flow); /* "ssn" is start of block-ack Tx window, corresponds to index -- cgit v1.2.3 From 8cb6c719fa3c0812bc0d151a7074f328a61a3062 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 15 Jul 2026 21:57:13 +0300 Subject: wifi: iwlwifi: mvm: copy the correct TK length for ranging When setting the TK for ranging with an associated peer, 32 bytes of TK are copied from the vif key without verifying the actual key length which may be only 16 bytes if CCMP-128 is used. Fix it by setting the copy length according to the key cipher. Signed-off-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.e339570086bd.Iafff5aaf0e25f7d29e06f0ef456107c4062bbc7d@changeid --- .../net/wireless/intel/iwlwifi/mvm/ftm-initiator.c | 55 +++++++++++++++------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index 3a14ca5e512a..8d7f60dcd027 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -646,7 +646,8 @@ static void iter(struct ieee80211_hw *hw, WARN_ON(*target->cipher == IWL_LOCATION_CIPHER_INVALID); } -static void +/* The tk buffer is assumed to be exactly TK_11AZ_LEN bytes */ +static int iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u8 *bssid, u8 *cipher, u8 *hltk, u8 *tk, u8 *rx_pn, u8 *tx_pn, __le32 *flags) @@ -656,12 +657,12 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (mvmvif->ftm_unprotected) - return; + return 0; #endif if (!(le32_to_cpu(*flags) & (IWL_INITIATOR_AP_FLAGS_NON_TB | IWL_INITIATOR_AP_FLAGS_TB))) - return; + return 0; lockdep_assert_held(&mvm->mutex); @@ -679,14 +680,31 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (vif->cfg.assoc && !memcmp(vif->bss_conf.bssid, bssid, ETH_ALEN)) { struct iwl_mvm_ftm_iter_data target; + u32 key_len; target.bssid = bssid; target.cipher = cipher; target.tk = NULL; ieee80211_iter_keys(mvm->hw, vif, iter, &target); - if (!WARN_ON(!target.tk)) - memcpy(tk, target.tk, TK_11AZ_LEN); + if (WARN_ON(!target.tk)) + return -EINVAL; + + switch (*target.cipher) { + case IWL_LOCATION_CIPHER_CCMP_128: + case IWL_LOCATION_CIPHER_GCMP_128: + key_len = WLAN_KEY_LEN_CCMP; + break; + case IWL_LOCATION_CIPHER_GCMP_256: + key_len = WLAN_KEY_LEN_GCMP_256; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + memset(tk, 0, TK_11AZ_LEN); + memcpy(tk, target.tk, key_len); } else { memcpy(tk, entry->tk, sizeof(entry->tk)); } @@ -695,8 +713,10 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, memcpy(tx_pn, entry->tx_pn, sizeof(entry->tx_pn)); FTM_SET_FLAG(SECURED); - return; + return 0; } + + return 0; } static int @@ -708,12 +728,11 @@ iwl_mvm_ftm_put_target_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (err) return err; - iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, - &target->cipher, target->hltk, - target->tk, target->rx_pn, - target->tx_pn, - &target->initiator_ap_flags); - return err; + return iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, + &target->cipher, target->hltk, + target->tk, target->rx_pn, + target->tx_pn, + &target->initiator_ap_flags); } static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm, @@ -881,11 +900,13 @@ iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags); iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id, &target->initiator_ap_flags); - iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, - &target->cipher, target->hltk, - target->tk, target->rx_pn, - target->tx_pn, - &target->initiator_ap_flags); + ret = iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, + &target->cipher, target->hltk, + target->tk, target->rx_pn, + target->tx_pn, + &target->initiator_ap_flags); + if (ret) + return ret; i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 : IWL_MVM_FTM_I2R_MAX_STS; -- cgit v1.2.3 From 0cb5260a1027a43f8cdb961e128f2ddd42e46832 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:14 +0300 Subject: wifi: iwlwifi: mvm: fix a possible underflow We shouldn't trust the firmware about the length of the wowlan packet. Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.fbd989cc85e2.If68de403bfa4943732c025961154c20b01b09e83@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 4eaba0bc4a1e..109265149963 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1509,6 +1509,10 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, if (WARN_ON_ONCE(truncated < 0)) truncated = 0; + /* this would be a firmware bug */ + if (WARN_ON_ONCE(pktsize < sizeof(*hdr))) + return; + if (ieee80211_is_data(hdr->frame_control)) { int hdrlen = ieee80211_hdrlen(hdr->frame_control); int ivlen = 0, icvlen = 4; /* also FCS */ @@ -1517,10 +1521,6 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, if (!pkt) goto report; - skb_put_data(pkt, pktdata, hdrlen); - pktdata += hdrlen; - pktsize -= hdrlen; - if (ieee80211_has_protected(hdr->frame_control)) { /* * This is unlocked and using gtk_i(c)vlen, @@ -1546,6 +1546,17 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, truncated = 0; } + if (IWL_FW_CHECK(mvm, + pktsize <= hdrlen + ivlen + icvlen, + "pktsize is too small %d\n", + pktsize)) { + kfree_skb(pkt); + return; + } + + skb_put_data(pkt, pktdata, hdrlen); + pktdata += hdrlen; + pktsize -= hdrlen; pktsize -= ivlen + icvlen; pktdata += ivlen; -- cgit v1.2.3 From a426d3227c669ef0ad9855b311a68129f10ac8bf Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 15 Jul 2026 21:57:15 +0300 Subject: wifi: iwlwifi: adapt ND match notif sizing to fixed matches array Switch iwl_scan_offload_match_info::matches to a fixed-size array and adjust D3 netdetect size handling accordingly. In MVM D3 paths, compute expected payload size as offsetof(struct iwl_scan_offload_match_info, matches) + matches_len to preserve previous behavior after the struct layout change. In MLD D3 netdetect handling, keep the simple full-notification size assumption and validate against sizeof(*notif) before accessing data. This keeps scan offload / netdetect functionality unchanged while making length checks consistent with the new struct definition. Assisted-by: GitHubCopilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260715215523.4c4346140bcc.I44313ac41daca352e6aecdba09a1c3570c3eea06@changeid --- drivers/net/wireless/intel/iwlwifi/fw/api/scan.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index 08f4cc3ea1c3..ee78371082b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1319,7 +1319,7 @@ struct iwl_scan_offload_match_info { u8 resume_while_scanning; u8 self_recovery; __le16 reserved; - struct iwl_scan_offload_profile_match matches[]; + struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES_V2]; } __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_3 and * SCAN_OFFLOAD_MATCH_INFO_NOTIFICATION_S_VER_1 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 109265149963..6b11fa32ea5c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2459,13 +2459,15 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { - query_len = sizeof(struct iwl_scan_offload_match_info); matches_len = sizeof(struct iwl_scan_offload_profile_match) * max_profiles; + query_len = offsetof(struct iwl_scan_offload_match_info, + matches) + matches_len; } else { - query_len = sizeof(struct iwl_scan_offload_profiles_query_v1); matches_len = sizeof(struct iwl_scan_offload_profile_match_v1) * max_profiles; + query_len = sizeof(struct iwl_scan_offload_profiles_query_v1) + + matches_len; } len = iwl_rx_packet_payload_len(cmd.resp_pkt); @@ -2819,7 +2821,8 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm, if (IS_ERR_OR_NULL(vif)) return; - if (len < sizeof(struct iwl_scan_offload_match_info) + matches_len) { + if (len < offsetof(struct iwl_scan_offload_match_info, matches) + + matches_len) { IWL_ERR(mvm, "Invalid scan match info notification\n"); return; } -- cgit v1.2.3 From 266cddf7bd0f6c79b6c0633aef742a22bf70265b Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 3 Jun 2026 17:37:49 +0900 Subject: gpu: host1x: Fix use-after-free in host1x_bo_clear_cached_mappings __host1x_bo_unpin() drops the last reference to the mapping and frees it, so we can't dereference mapping afterwards. The cache itself outlives the mapping, so use the cache local variable instead. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-tegra/ah6ErK6f4kVudVIA@stanley.mountain/T/#u Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260603-host1x-bocache-leak-fix-v1-1-494101dbfd30@nvidia.com --- drivers/gpu/host1x/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index e3884096c2fe..ea3b584990c9 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -1012,10 +1012,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo) if (WARN_ON(!cache)) continue; - mutex_lock(&mapping->cache->lock); + mutex_lock(&cache->lock); WARN_ON(kref_read(&mapping->ref) != 1); __host1x_bo_unpin(&mapping->ref); - mutex_unlock(&mapping->cache->lock); + mutex_unlock(&cache->lock); } } EXPORT_SYMBOL(host1x_bo_clear_cached_mappings); -- cgit v1.2.3 From a19bda861b3a79e25417462539df8b0d77c6b322 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 16 Jul 2026 10:22:43 +0000 Subject: rust: device: avoid trailing ; in printing macros These macros are used like expressions, so they should not emit a semicolon. This is being turned into a hard error in a future release of Rust. error: trailing semicolon in macro used in expression position --> drivers/gpu/nova-core/firmware/fsp.rs:79:34 | 79 | .inspect_err(|_| dev_err!(dev, "FMC firmware missing '{}' section\n", name)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #79813 = note: this error originates in the macro `dev_err` (in Nightly builds, run with -Z macro-backtrace for more info) [ I was doubly surprised since upstream made it a deny-by-default lint a year ago for Rust 1.91.0, and yet we didn't see it; plus I hadn't seen this in my CI even yesterday. It turns out this just landed into today's nightly (nightly-2026-07-16, using upstream commit d0babd8b6): Link: https://github.com/rust-lang/rust/pull/159222 which says: "The `semicolon_in_expressions_from_macros` lint previously suppressed warnings about non-local macros. This masks a lint that will subsequently become a hard error." So that explains it. And this is the PR that will make it a hard error at some point in the future: Link: https://github.com/rust-lang/rust/pull/159218 Thus starting with Rust 1.99.0 (expected 2026-10-01), we will be seeing the deny-by-default lint above, so clean it up already. - Miguel ] Cc: stable@vger.kernel.org # Needed in 6.18.y and later. Link: https://github.com/rust-lang/rust/issues/79813 Signed-off-by: Alice Ryhl Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Link: https://github.com/rust-lang/rust/pull/159218 Link: https://github.com/rust-lang/rust/pull/159222 Link: https://patch.msgid.link/20260716-device-trail-semicolon-v1-1-f48e9dcfae15@google.com [ Fixed typo. ] Signed-off-by: Miguel Ojeda --- rust/kernel/device.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 645afc49a27d..1a38b3bbdfb7 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -708,9 +708,7 @@ macro_rules! impl_device_context_into_aref { #[macro_export] macro_rules! dev_printk { ($method:ident, $dev:expr, $($f:tt)*) => { - { - $crate::device::Device::$method($dev.as_ref(), $crate::prelude::fmt!($($f)*)) - } + $crate::device::Device::$method($dev.as_ref(), $crate::prelude::fmt!($($f)*)) } } @@ -737,7 +735,7 @@ macro_rules! dev_printk { /// ``` #[macro_export] macro_rules! dev_emerg { - ($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*) } } /// Prints an alert-level message (level 1) prefixed with device information. @@ -763,7 +761,7 @@ macro_rules! dev_emerg { /// ``` #[macro_export] macro_rules! dev_alert { - ($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*) } } /// Prints a critical-level message (level 2) prefixed with device information. @@ -789,7 +787,7 @@ macro_rules! dev_alert { /// ``` #[macro_export] macro_rules! dev_crit { - ($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*) } } /// Prints an error-level message (level 3) prefixed with device information. @@ -815,7 +813,7 @@ macro_rules! dev_crit { /// ``` #[macro_export] macro_rules! dev_err { - ($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*) } } /// Prints a warning-level message (level 4) prefixed with device information. @@ -841,7 +839,7 @@ macro_rules! dev_err { /// ``` #[macro_export] macro_rules! dev_warn { - ($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*) } } /// Prints a notice-level message (level 5) prefixed with device information. @@ -867,7 +865,7 @@ macro_rules! dev_warn { /// ``` #[macro_export] macro_rules! dev_notice { - ($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*) } } /// Prints an info-level message (level 6) prefixed with device information. @@ -893,7 +891,7 @@ macro_rules! dev_notice { /// ``` #[macro_export] macro_rules! dev_info { - ($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*) } } /// Prints a debug-level message (level 7) prefixed with device information. @@ -919,5 +917,5 @@ macro_rules! dev_info { /// ``` #[macro_export] macro_rules! dev_dbg { - ($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*); } + ($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*) } } -- cgit v1.2.3 From 8173f7e2ce67e6ca1d4763f3da14e5b01ce77456 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Tue, 7 Jul 2026 12:41:15 -0400 Subject: rhashtable: clear stale iter->p on table restart rhashtable_walk_start_check() has two restart paths when resuming a walk. When iter->walker.tbl is valid, it re-validates iter->p against the table and sets iter->p = NULL if the object is gone. When iter->walker.tbl is NULL (table was freed during resize), it resets slot and skip but forgets to clear iter->p. rhashtable_walk_next() then dereferences the stale iter->p, reading freed memory. This is a use-after-free. Any caller that does multi-fragment rhashtable walks across walk_stop/walk_start boundaries is affected. Concrete cases include netlink_diag (__netlink_diag_dump in net/netlink/diag.c) and TIPC (tipc_nl_sk_walk in net/tipc/socket.c). Crash stack (netlink_diag): BUG: KASAN: slab-use-after-free in rhashtable_walk_next+0x365/0x3c0 Read of size 8 at addr ffff88801a9d2438 (freed kmalloc-2k, offset 1080) Call Trace: rhashtable_walk_next+0x365/0x3c0 (lib/rhashtable.c:1016) __netlink_diag_dump+0x160/0x760 (net/netlink/diag.c:122) netlink_diag_dump+0xc2/0x240 netlink_dump+0x5bc/0x1270 netlink_recvmsg+0x7a3/0x980 sock_recvmsg+0x1bc/0x200 __sys_recvfrom+0x1d4/0x2c0 Fixes: 5d240a8936f6 ("rhashtable: improve rhashtable_walk stability when stop/start used.") Cc: Reported-by: AutonomousCodeSecurity@microsoft.com Reported-by: Yuan Tan Closes: https://lore.kernel.org/linux-crypto/CAB8m9Wh559e+=n8z51gB8DrbEyCc2mc0MgGjrRR6_VXBmU=2AQ@mail.gmail.com Signed-off-by: Cen Zhang (Microsoft) Reviewed-by: NeilBrown Signed-off-by: Herbert Xu --- lib/rhashtable.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 40cfb38ac919..d459bef245f4 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -878,6 +878,7 @@ int rhashtable_walk_start_check(struct rhashtable_iter *iter) iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht); iter->slot = 0; iter->skip = 0; + iter->p = NULL; return -EAGAIN; } -- cgit v1.2.3 From 3917b1012ee2fef6da16d7450d4267dcb3e93363 Mon Sep 17 00:00:00 2001 From: Ihor Solodrai Date: Mon, 13 Jul 2026 16:52:22 -0700 Subject: bpf: Fix tracing of kfuncs with implicit args A kfunc marked with KF_IMPLICIT_ARGS flag takes implicit arguments (such as bpf_prog_aux) that the verifier injects at load time. resolve_btfids strips those from the kfunc's BTF-visible prototype and keeps the real kernel ABI in a counterpart _impl prototype [1]. fentry/fexit/fmod_ret/fsession programs may attach to the BPF kernel functions, including those with implicit args. However bpf_check_attach_target() and bpf_check_attach_btf_id_multi() extract the struct btf_func_model from the wrong BTF prototype of the kfunc. The btf_func_model is later read to construct the trampoline, which then causes the injected implicit argument to be clobbered and the kfunc dereferencing garbage. Add btf_attach_func_proto() to resolve the real ABI prototype of the kfunc the way the call site does: by looking up the _impl prototype for a KF_IMPLICIT_ARGS kfunc. Use it at both attach-target model construction sites. To enable this, make two supporting changes: * pass bpf_verifier_log instead of bpf_verifier_env to find_kfunc_impl_proto(), so it can be reused from the attach path * add btf_kfunc_check_flag() to test a flag across all of a kfunc's hook sets, because a program attaching to a kfunc is not in the kfunc's call-set KF_IMPLICIT_ARGS must be consistent across the sets, so btf_kfunc_check_flag() returns -EINVAL on inconsistency. btf_kfunc_check_flag() reads the kfunc's flags from the target's kfunc_set_tab. For a module BTF that table is stable only after the module is live, so take a module reference around the read, mirroring how the kfunc call path gates the same lookup with btf_try_get_module(). The remaining call sites of btf_distill_func_proto() are safe as is. The BPF_TRACE_ITER case distills a registered iterator's prototype, and bpf_struct_ops_desc_init() distills the function-pointer members of a struct_ops type. Neither is a kfunc, and so can't have implicit arguments. [1] https://lore.kernel.org/all/20260120222638.3976562-1-ihor.solodrai@linux.dev/ Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS") Reported-by: Tejun Heo Signed-off-by: Ihor Solodrai Link: https://github.com/sched-ext/scx/issues/3687#issuecomment-4906694106 Link: https://patch.msgid.link/20260713235223.1639022-2-ihor.solodrai@linux.dev Signed-off-by: Eduard Zingerman --- include/linux/btf.h | 1 + kernel/bpf/btf.c | 29 ++++++++++++++++++++++ kernel/bpf/verifier.c | 66 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 83 insertions(+), 13 deletions(-) diff --git a/include/linux/btf.h b/include/linux/btf.h index 240401d9b25b..c09b7994de4e 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -578,6 +578,7 @@ const char *btf_str_by_offset(const struct btf *btf, u32 offset); struct btf *btf_parse_vmlinux(void); struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog); u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog); +int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag); bool btf_kfunc_is_allowed(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog); u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog); diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 64572f85edc8..c4673a54c4ba 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -9114,6 +9114,35 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id); } +/* + * Check a single KF_* @flag on a kfunc across all of its hook sets. + * Returns: + * * 1 if @flag is set + * * 0 if @flag is not set + * * -EINVAL if @flag is set inconsistently across the sets + * * -ENOENT if kfunc_btf_id is not a registered kfunc + */ +int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag) +{ + enum btf_kfunc_hook hook; + int res = -ENOENT; + bool is_set; + u32 *flags; + + for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) { + flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id); + if (!flags) + continue; + is_set = *flags & flag; + if (res < 0) + res = is_set; + else if (res != is_set) + return -EINVAL; + } + + return res; +} + u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog) { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9f1333676365..99444eae917e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2584,24 +2584,25 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset) #define KF_IMPL_SUFFIX "_impl" -static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env, +static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_log *log, struct btf *btf, const char *func_name) { - char *buf = env->tmp_str_buf; const struct btf_type *func; + char buf[KSYM_NAME_LEN]; s32 impl_id; int len; - len = snprintf(buf, TMP_STR_BUF_LEN, "%s%s", func_name, KF_IMPL_SUFFIX); - if (len < 0 || len >= TMP_STR_BUF_LEN) { - verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX); + len = snprintf(buf, sizeof(buf), "%s%s", func_name, KF_IMPL_SUFFIX); + if (len < 0 || len >= sizeof(buf)) { + bpf_log(log, "function name %s%s is too long\n", + func_name, KF_IMPL_SUFFIX); return NULL; } impl_id = btf_find_by_name_kind(btf, buf, BTF_KIND_FUNC); if (impl_id <= 0) { - verbose(env, "cannot find function %s in BTF\n", buf); + bpf_log(log, "cannot find function %s in BTF\n", buf); return NULL; } @@ -2653,7 +2654,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env, * can be found through the counterpart _impl kfunc. */ if (kfunc_flags && (*kfunc_flags & KF_IMPLICIT_ARGS)) - func_proto = find_kfunc_impl_proto(env, btf, func_name); + func_proto = find_kfunc_impl_proto(&env->log, btf, func_name); else func_proto = btf_type_by_id(btf, func->type); @@ -18880,6 +18881,47 @@ static int btf_id_allow_sleepable(u32 btf_id, unsigned long addr, const struct b return -EINVAL; } +/* + * Resolve the prototype describing a trace target's real ABI. A + * KF_IMPLICIT_ARGS kfunc has its injected args stripped from the public + * prototype, so use the _impl prototype; other targets use their own. + */ +static const struct btf_type * +btf_attach_func_proto(struct bpf_verifier_log *log, struct btf *btf, u32 func_id) +{ + const struct btf_type *func; + struct module *mod = NULL; + const char *name; + int implicit; + + func = btf_type_by_id(btf, func_id); + if (!func || !btf_type_is_func(func)) + return NULL; + name = btf_name_by_offset(btf, func->name_off); + + /* + * btf_kfunc_check_flag() reads kfunc_set_tab, which for a module is + * stable only once it is live; hold a module ref across the read to + * exclude a concurrent module load. + */ + if (btf_is_module(btf)) { + mod = btf_try_get_module(btf); + if (!mod) + return NULL; + } + implicit = btf_kfunc_check_flag(btf, func_id, KF_IMPLICIT_ARGS); + module_put(mod); + + if (implicit == -EINVAL) { + bpf_log(log, "kfunc %s has inconsistent KF_IMPLICIT_ARGS\n", name); + return NULL; + } + if (implicit > 0) + return find_kfunc_impl_proto(log, btf, name); + + return btf_type_by_id(btf, func->type); +} + int bpf_check_attach_target(struct bpf_verifier_log *log, const struct bpf_prog *prog, const struct bpf_prog *tgt_prog, @@ -19128,8 +19170,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, if (prog_extension && btf_check_type_match(log, prog, btf, t)) return -EINVAL; - t = btf_type_by_id(btf, t->type); - if (!btf_type_is_func_proto(t)) + t = btf_attach_func_proto(log, btf, btf_id); + if (!t || !btf_type_is_func_proto(t)) return -EINVAL; if ((prog->aux->saved_dst_prog_type || prog->aux->saved_dst_attach_type) && @@ -19412,10 +19454,8 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt tname = btf_name_by_offset(btf, t->name_off); if (!tname) return -EINVAL; - if (!btf_type_is_func(t)) - return -EINVAL; - t = btf_type_by_id(btf, t->type); - if (!btf_type_is_func_proto(t)) + t = btf_attach_func_proto(NULL, btf, btf_id); + if (!t || !btf_type_is_func_proto(t)) return -EINVAL; err = btf_distill_func_proto(NULL, btf, t, tname, &tgt_info->fmodel); if (err < 0) -- cgit v1.2.3 From 0af15f3057a60a038864d47fa4b54d1c3c3c654c Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Mon, 13 Jul 2026 16:52:23 -0700 Subject: selftests/bpf: Cover tracing implicit kfunc args KF_IMPLICIT_ARGS kfuncs have a BPF-call prototype and a real kernel target prototype. Add a tracing selftest that attaches fentry and fexit programs to bpf_kfunc_implicit_arg(), runs a syscall BPF program that calls it, and checks that the tracing context exposes both the explicit argument and the implicit prog aux pointer. Co-developed-by: Ihor Solodrai Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Ihor Solodrai Link: https://patch.msgid.link/20260713235223.1639022-3-ihor.solodrai@linux.dev Signed-off-by: Eduard Zingerman --- .../bpf/prog_tests/kfunc_implicit_args_tracing.c | 36 ++++++++++ .../bpf/progs/kfunc_implicit_args_tracing.c | 77 ++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c create mode 100644 tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c new file mode 100644 index 000000000000..61cc5aaba025 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +#include +#include "kfunc_implicit_args_tracing.skel.h" + +void test_kfunc_implicit_args_tracing(void) +{ + struct kfunc_implicit_args_tracing *skel; + LIBBPF_OPTS(bpf_test_run_opts, topts); + int err, fd; + + skel = kfunc_implicit_args_tracing__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + err = kfunc_implicit_args_tracing__attach(skel); + if (!ASSERT_OK(err, "attach")) + goto cleanup; + + fd = bpf_program__fd(skel->progs.trigger_implicit_arg); + err = bpf_prog_test_run_opts(fd, &topts); + if (!ASSERT_OK(err, "test_run")) + goto cleanup; + + ASSERT_EQ(topts.retval, 5, "kfunc_retval"); + ASSERT_EQ(skel->bss->fentry_arg_cnt, 2, "fentry_arg_cnt"); + ASSERT_NEQ(skel->bss->fentry_aux_arg, 0, "fentry_aux_arg"); + ASSERT_EQ(skel->bss->fentry_result, 1, "fentry_result"); + ASSERT_EQ(skel->bss->fexit_arg_cnt, 2, "fexit_arg_cnt"); + ASSERT_NEQ(skel->bss->fexit_aux_arg, 0, "fexit_aux_arg"); + ASSERT_EQ(skel->bss->fexit_result, 1, "fexit_result"); + +cleanup: + kfunc_implicit_args_tracing__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c new file mode 100644 index 000000000000..995f8b8b5b9e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +#include +#include +#include +#include + +extern int bpf_kfunc_implicit_arg(int a) __weak __ksym; + +char _license[] SEC("license") = "GPL"; + +/* Shared arg checks; reports arg count and aux, returns 1 on success. */ +static __always_inline __u64 +check_implicit_args(void *ctx, __u64 *arg_cnt, __u64 *aux_arg) +{ + __u64 a = 0, aux = 0, z = 0; + __u64 result; + __s64 err; + + *arg_cnt = bpf_get_func_arg_cnt(ctx); + result = *arg_cnt == 2; + + err = bpf_get_func_arg(ctx, 0, &a); + result &= err == 0 && (int)a == 5; + + err = bpf_get_func_arg(ctx, 1, &aux); + *aux_arg = aux; + result &= err == 0 && aux != 0; + + err = bpf_get_func_arg(ctx, 2, &z); + result &= err == -EINVAL; + + return result; +} + +__u64 fentry_result; +__u64 fentry_arg_cnt; +__u64 fentry_aux_arg; + +SEC("fentry/bpf_kfunc_implicit_arg") +int BPF_PROG(trace_implicit_arg_fentry) +{ + __u64 ret = 0; + __s64 err; + + fentry_result = check_implicit_args(ctx, &fentry_arg_cnt, &fentry_aux_arg); + + err = bpf_get_func_ret(ctx, &ret); + fentry_result &= err == -EOPNOTSUPP; + + return 0; +} + +__u64 fexit_result; +__u64 fexit_arg_cnt; +__u64 fexit_aux_arg; + +SEC("fexit/bpf_kfunc_implicit_arg") +int BPF_PROG(trace_implicit_arg_fexit) +{ + __u64 ret = 0; + __s64 err; + + fexit_result = check_implicit_args(ctx, &fexit_arg_cnt, &fexit_aux_arg); + + err = bpf_get_func_ret(ctx, &ret); + fexit_result &= err == 0 && ret == 5; + + return 0; +} + +SEC("syscall") +int trigger_implicit_arg(void *ctx) +{ + return bpf_kfunc_implicit_arg(5); +} -- cgit v1.2.3 From faebb7ba1ac65fa5810b640df02ce04e509fdc11 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Thu, 16 Jul 2026 08:13:05 -0700 Subject: accel/amdxdna: Fix use-after-free of mm_struct in job scheduler amdxdna_cmd_submit() stores current->mm in job->mm without holding any reference. aie2_sched_job_run() later access job->mm from the DRM scheduler worker thread. With only a raw pointer and no structural reference, the mm_struct can be freed before the scheduler runs the job. Fix this by calling mmgrab() to hold a structural mm_count reference for the lifetime of the job, paired with mmdrop() in every cleanup path. Fixes: aac243092b70 ("accel/amdxdna: Add command execution") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260716151305.1595780-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_ctx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index bdbd3db12a6c..31a414c3f0d9 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -577,6 +577,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job) amdxdna_arg_bos_put(job); amdxdna_gem_put_obj(job->cmd_bo); dma_fence_put(job->fence); + mmdrop(job->mm); } int amdxdna_cmd_submit(struct amdxdna_client *client, @@ -642,6 +643,7 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, job->hwctx = hwctx; job->mm = current->mm; + mmgrab(job->mm); job->fence = amdxdna_fence_create(hwctx); if (!job->fence) { @@ -676,6 +678,8 @@ put_bos: cmd_put: amdxdna_gem_put_obj(job->cmd_bo); free_job: + if (job->mm) + mmdrop(job->mm); kfree(job); return ret; } -- cgit v1.2.3 From df6134b527a88b3e65ba6ae5073664af091d5fd2 Mon Sep 17 00:00:00 2001 From: Zhiping Zhang Date: Thu, 2 Jul 2026 15:24:58 -0700 Subject: 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 */ } -- cgit v1.2.3 From 2c1931a81122c3cdc4c89448fe0442c69e21c0d5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 10 Jul 2026 18:13:17 +0000 Subject: 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 @@ do_time_wait: } 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 @@ do_time_wait: } 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; -- cgit v1.2.3 From d2e914a4a0d0f753dbae830264850d044026167c Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Fri, 10 Jul 2026 21:36:25 +0200 Subject: 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, -- cgit v1.2.3 From f1f5c8a3955f8fda3f84ed883ac8daa1847e724c Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sat, 11 Jul 2026 11:05:37 -0400 Subject: 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(struct tcf_tunnel_key_params *p) +static void tunnel_key_release_params_rcu(struct rcu_head *head) { - if (!p) - return; + 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); +} - kfree_rcu(p, rcu); +static void tunnel_key_release_params(struct tcf_tunnel_key_params *p) +{ + if (!p) + return; + call_rcu(&p->rcu, tunnel_key_release_params_rcu); } static int tunnel_key_init(struct net *net, struct nlattr *nla, -- cgit v1.2.3 From e0b5252a59383b77d1b8dbeda00b7184dd95f4d3 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Jul 2026 14:12:35 -0400 Subject: 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 -- cgit v1.2.3 From 71132cedd1ecbc4032d76e9928c18a10f7e39b80 Mon Sep 17 00:00:00 2001 From: Diego Fernando Mancera Gomez Date: Fri, 17 Jul 2026 02:07:04 -0600 Subject: usb: atm: ueagle-atm: reject descriptors that confuse probe and disconnect uea_probe() distinguishes a pre-firmware device from a post-firmware one using the USB id (UEA_IS_PREFIRM()), and stores a different object as the interface data in each case: a 'struct completion' for a pre-firmware device (to be waited on in .disconnect()), or a 'struct usbatm_data' for a post-firmware one. uea_disconnect() instead tells the two apart by the number of interfaces of the active configuration (a pre-firmware device exposes a single interface, ADI930 has 2 and eagle has 3), and casts the interface data accordingly. Because the two handlers use different criteria, a crafted device that advertises a pre-firmware id together with a multi-interface descriptor (or a post-firmware id with a single interface) makes them disagree: the small 'struct completion' stored by uea_probe() is then passed to usbatm_usb_disconnect(), which casts it to 'struct usbatm_data' and takes instance->serialize, reading past the end of the allocation: BUG: KASAN: slab-out-of-bounds in __mutex_lock+0x152a/0x1b80 Read of size 8 at addr ffff8880470e2c60 by task kworker/1:2/982 ... __mutex_lock+0x152a/0x1b80 usbatm_usb_disconnect+0x70/0x820 uea_disconnect+0x133/0x2c0 usb_unbind_interface+0x1dd/0x9e0 ... which belongs to the cache kmalloc-96 of size 96 The buggy address is located 0 bytes to the right of allocated 96-byte region [ffff8880470e2c00, ffff8880470e2c60) Reject such inconsistent descriptors in uea_probe() so that both handlers always make the same pre/post-firmware decision. Reported-by: syzbot+e62a973f8322b3bbe3ac@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e62a973f8322b3bbe3ac Fixes: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()") Signed-off-by: Diego Fernando Mancera Gomez Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260717080704.1264-1-diegomancera.dev@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 4e71ed679a76..4266a0cb7e3b 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2549,6 +2549,7 @@ static struct usbatm_driver uea_usbatm_driver = { static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *usb = interface_to_usbdev(intf); + bool single_iface = usb->config->desc.bNumInterfaces == 1; int ret; uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", @@ -2557,6 +2558,22 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) le16_to_cpu(usb->descriptor.bcdDevice), chip_name[UEA_CHIP_VERSION(id)]); + /* + * uea_probe() decides between the pre-firmware and post-firmware case + * from the USB id and stores a different object as interface data in + * each case: a struct completion for a pre-firmware device, a struct + * usbatm_data for a post-firmware one. uea_disconnect() instead tells + * the two apart by the number of interfaces (a pre-firmware device + * exposes a single interface, ADI930 has 2 and eagle has 3). A crafted + * device advertising a pre-firmware id together with a multi-interface + * descriptor (or the other way around) makes the two disagree, so that + * usbatm_usb_disconnect() treats the small completion object as a + * struct usbatm_data and reads out of bounds. Reject such inconsistent + * descriptors so both paths make the same decision. + */ + if (UEA_IS_PREFIRM(id) != single_iface) + return -ENODEV; + usb_reset_device(usb); if (UEA_IS_PREFIRM(id)) { -- cgit v1.2.3 From 1cb8553c02e93e5a150cebd42f9ee3db0ece4707 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sun, 12 Jul 2026 00:37:16 +0800 Subject: 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; -- cgit v1.2.3 From 7fb13fd7e9a59a37cd911efff83abe19e3ee029d Mon Sep 17 00:00:00 2001 From: Jiangshan Yi Date: Wed, 15 Jul 2026 15:35:46 +0800 Subject: serial: 8250_mid: Fix NULL function pointer dereference on DNV/ICX-D/SNR platforms Commit b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") replaced the dnv_board setup and exit callbacks with PTR_IF(false, ...), which evaluates to NULL. However, the three call sites in mid8250_probe() and mid8250_remove() unconditionally dereference these function pointers without NULL checks, causing a NULL pointer dereference (kernel oops) on any Denverton (DNV), Ice Lake Xeon D (ICX-D/CDF), or Snowridge (SNR) platform. Fix this by adding the missing NULL checks before calling the setup and exit callbacks. Fixes: b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") Cc: stable Reviewed-by: Andy Shevchenko Signed-off-by: Jiangshan Yi Link: https://patch.msgid.link/20260715073546.1875083-1-yijiangshan@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index f88809ff370b..82656645b8a6 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -318,9 +318,11 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!uart.port.membase) return -ENOMEM; - ret = mid->board->setup(mid, &uart.port); - if (ret) - return ret; + if (mid->board->setup) { + ret = mid->board->setup(mid, &uart.port); + if (ret) + return ret; + } ret = mid8250_dma_setup(mid, &uart); if (ret) @@ -336,7 +338,8 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err: - mid->board->exit(mid); + if (mid->board->exit) + mid->board->exit(mid); return ret; } @@ -346,7 +349,8 @@ static void mid8250_remove(struct pci_dev *pdev) serial8250_unregister_port(mid->line); - mid->board->exit(mid); + if (mid->board->exit) + mid->board->exit(mid); } static const struct mid8250_board pnw_board = { -- cgit v1.2.3 From af071d9e07e57cfff239e8d09d2f3b05ebc9c667 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Thu, 16 Jul 2026 17:08:09 -0400 Subject: serial: sc16is7xx: implement gpio get_direction() callback 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. Fixes: dfeae619d781 ("serial: sc16is7xx") Cc: stable Signed-off-by: Hugo Villeneuve Acked-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260716210813.2582826-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index daebd92f32c7..6c97953d593b 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1273,6 +1273,17 @@ static int sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned int offset, return 0; } +static int sc16is7xx_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct sc16is7xx_port *s = gpiochip_get_data(chip); + struct uart_port *port = &s->p[0].port; + unsigned int val; + + val = sc16is7xx_port_read(port, SC16IS7XX_IODIR_REG); + + return val & BIT(offset) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { @@ -1350,6 +1361,7 @@ static int sc16is7xx_setup_gpio_chip(struct sc16is7xx_port *s) s->gpio.parent = dev; s->gpio.label = dev_name(dev); s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask; + s->gpio.get_direction = sc16is7xx_gpio_get_direction; s->gpio.direction_input = sc16is7xx_gpio_direction_input; s->gpio.get = sc16is7xx_gpio_get; s->gpio.direction_output = sc16is7xx_gpio_direction_output; -- cgit v1.2.3 From 04aeddf2dadd0eb7ad016a766dcbe9c983311f09 Mon Sep 17 00:00:00 2001 From: Andre Carvalho Date: Fri, 10 Jul 2026 23:19:17 +0100 Subject: 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 --- tools/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 -- cgit v1.2.3 From 2c72eb6286347d05a885412fb076993bd5286b53 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Sun, 12 Jul 2026 21:03:43 +0800 Subject: 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; } -- cgit v1.2.3 From 56d96fededd61192cd7cc8d2b0f36adfd59036c3 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Sat, 11 Jul 2026 04:50:00 -0700 Subject: 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) -- cgit v1.2.3 From 30d49cba27f8905bc288cef5846963f0004f644c Mon Sep 17 00:00:00 2001 From: MinJea Kim Date: Tue, 14 Jul 2026 22:14:21 +0900 Subject: staging: rtl8723bs: fix inverted HT40 secondary channel offset rtw_get_chan_type() maps the driver's channel offset to nl80211 channel types the wrong way around. In this driver HAL_PRIME_CHNL_OFFSET_LOWER means the primary channel is the lower 20 MHz half of the 40 MHz pair, i.e. the secondary channel is above the primary one: rtw_get_center_ch() computes the center channel as "channel + 2" for OFFSET_LOWER, and bwmode_update_check() sets OFFSET_LOWER when the AP's HT operation IE announces SCA (secondary channel above). In nl80211 terms that is NL80211_CHAN_HT40PLUS, not HT40MINUS. Because of the inversion, cfg80211_rtw_get_channel() reports an HT40+ association as HT40-. For an HT40+ AP on a low channel (e.g. channel 3) the resulting chandef spans below the 2.4 GHz band edge and is invalid, so the regulatory core tears the connection down 60 seconds (REG_ENFORCE_GRACE_MS) after the AP's country IE triggers a regdomain change: reg_check_chans_work() considers the reported chandef unusable and calls cfg80211_leave(). The supplicant then reconnects, the country IE changes the regdomain again, and the cycle repeats, causing a disconnect/reconnect loop every ~65 seconds for as long as the link is up. Observed on a TECLAST X80 Power tablet (RTL8723BS) associated to an HT40+ AP on channel 3 with a KR country IE; a kprobe trace showed cfg80211_disconnect() being invoked from reg_check_chans_work(). With the mapping fixed, "iw dev wlan0 info" reports the correct "width: 40 MHz, center1: 2432 MHz" and the periodic disconnects stop. Fixes: 5402cc178c5d ("staging: rtl8723bs: add get_channel cfg80211 implementation") Cc: stable@vger.kernel.org Assisted-by: Claude-Code:claude-fable-5 bpftrace Signed-off-by: MinJea Kim Link: https://patch.msgid.link/20260714131421.3980-1-qndkdrnl@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 6a97afd89dc7..967cd1b34aed 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -1957,7 +1957,7 @@ static u8 rtw_get_chan_type(struct adapter *adapter) else return NL80211_CHAN_NO_HT; case CHANNEL_WIDTH_40: - if (mlme_ext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER) + if (mlme_ext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER) return NL80211_CHAN_HT40PLUS; else return NL80211_CHAN_HT40MINUS; -- cgit v1.2.3 From 0e95ff792ae0aa6fbad9455943e9e1e4062670e9 Mon Sep 17 00:00:00 2001 From: Moksh Panicker Date: Thu, 25 Jun 2026 20:29:11 +0000 Subject: staging: rtl8723bs: fix OOB reads in rtw_get_wps_ie() rtw_get_wps_ie() iterates over IE data from network frames without validating that the IE header and payload fit within the remaining buffer before reading them. Specifically: - in_ie[cnt + 1] is read without checking cnt + 1 < in_len - memcmp(&in_ie[cnt + 2], ...) accesses cnt + 2 without bounds check - in_ie[cnt + 1] is used as length without verifying payload fits Add bounds checks at the top of the loop body to break early if fewer than 2 bytes remain for the IE header, or if the declared payload extends past the end of the buffer. Also require at least 4 bytes of payload before comparing the WPS OUI. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Moksh Panicker Link: https://patch.msgid.link/20260625202911.26782-1-mokshpanicker.7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 54f805a6b5ce..863ddf846218 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -670,7 +670,14 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) while (cnt < in_len) { eid = in_ie[cnt]; - if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { + if (cnt + 2 > in_len) + break; + + if (in_ie[cnt + 1] + 2 > in_len - cnt) + break; + + if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (in_ie[cnt + 1] >= 4) && + (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { wpsie_ptr = &in_ie[cnt]; if (wps_ie) -- cgit v1.2.3 From 6815504f61e79383c77fa8fa8b219b1d6788475a Mon Sep 17 00:00:00 2001 From: Dave Penkler Date: Tue, 30 Jun 2026 17:55:17 +0200 Subject: MAINTAINERS: Add Greg Kroah-Hartman to GPIB Greg was not receiving patches for the GPIB subsystem from folks using scripts/get_maintainer.pl -f drivers/gpib. Signed-off-by: Dave Penkler Link: https://patch.msgid.link/20260630155517.5685-1-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 806bd2d80d15..e8704c09e0ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11107,6 +11107,7 @@ F: drivers/platform/x86/gpd-pocket-fan.c GPIB DRIVERS M: Dave Penkler +M: Greg Kroah-Hartman S: Maintained F: drivers/gpib/ F: include/uapi/linux/gpib.h -- cgit v1.2.3 From 87d3621ccc63b3999d756bb59f0cedd738c28eb3 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 25 May 2026 14:04:22 +0200 Subject: uio_hv_generic: Bind to FCopy device by default The Hyper-V kernel-mode fcopy driver was removed in 6.10 and the new fcopy daemon requires this uio driver to function. However, by default the driver does not bind to any devices, and must be configured through the sysfs "new_id" file. Since the FCopy device is now only usable through this driver, add its ID to the driver's ID table so that the daemon will work "out of the box". Signed-off-by: Ben Hutchings Fixes: ec314f61e4fc ("Drivers: hv: Remove fcopy driver") Cc: stable Link: https://patch.msgid.link/ahQ6xuhSReidmN-3@decadent.org.uk Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_hv_generic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index 29ec2d15ada8..7b4cc456c453 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -396,9 +396,15 @@ hv_uio_remove(struct hv_device *dev) vmbus_free_ring(dev->channel); } +static const struct hv_vmbus_device_id hv_uio_id_table[] = { + { HV_FCOPY_GUID }, + {} +}; +MODULE_DEVICE_TABLE(vmbus, hv_uio_id_table); + static struct hv_driver hv_uio_drv = { .name = "uio_hv_generic", - .id_table = NULL, /* only dynamic id's */ + .id_table = hv_uio_id_table, .probe = hv_uio_probe, .remove = hv_uio_remove, }; -- cgit v1.2.3 From 17221216ae8ce6a24e8a4e787382e3ebc81b88a8 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 27 May 2026 13:51:03 +0100 Subject: comedi: comedi_parport: deal with premature interrupt Syzbot reported a general protection fault in `comedi_get_is_subdevice_running()`, which was called from the interrupt handler `parport_interrupt()` in the "comedi_parport" driver, but it does not currently have a C reproducer for the problem. It's probably due to a premature interrupt for one of two reasons: 1. The driver sets up the interrupt handler before the comedi subdevices used by the interrupt handler have been allocated, but does not disable the interrupt in the parallel port's CTRL register first. 2. The driver uses a user-supplied I/O port base address which Syzbot would have supplied, but it might not be backed by real parallel port hardware. Change the initialization order in the driver's comedi "attach" handler (`parport_attach()`) so that the hardware registers are initialized before the interrupt handler is requested. This should prevent premature interrupts occurring for real hardware. Also add a test to the interrupt handler to ensure the comedi device is fully attached and return early if it isn't. Fixes: 241ab6ad7108e ("Staging: comedi: add comedi_parport driver") Reported-by: syzbot+f24c3d5d316011bacc70@syzkaller.appspotmail.com Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260527125104.96596-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_parport.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/comedi/drivers/comedi_parport.c b/drivers/comedi/drivers/comedi_parport.c index 2604680d86c4..57ee3f9dfba2 100644 --- a/drivers/comedi/drivers/comedi_parport.c +++ b/drivers/comedi/drivers/comedi_parport.c @@ -211,6 +211,13 @@ static irqreturn_t parport_interrupt(int irq, void *d) unsigned int ctrl; unsigned short val = 0; + /* + * Check device is fully attached. Device interrupts should have + * been disabled, but do this in case of bad hardware. + */ + if (!dev->attached) + return IRQ_NONE; + ctrl = inb(dev->iobase + PARPORT_CTRL_REG); if (!(ctrl & PARPORT_CTRL_IRQ_ENA)) return IRQ_NONE; @@ -233,6 +240,9 @@ static int parport_attach(struct comedi_device *dev, if (ret) return ret; + outb(0, dev->iobase + PARPORT_DATA_REG); + outb(0, dev->iobase + PARPORT_CTRL_REG); + if (it->options[1]) { ret = request_irq(it->options[1], parport_interrupt, 0, dev->board_name, dev); @@ -288,9 +298,6 @@ static int parport_attach(struct comedi_device *dev, s->cancel = parport_intr_cancel; } - outb(0, dev->iobase + PARPORT_DATA_REG); - outb(0, dev->iobase + PARPORT_CTRL_REG); - return 0; } -- cgit v1.2.3 From f97752cfe511c1ed9933057455c73aaac07d6517 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Tue, 9 Jun 2026 10:55:04 -0700 Subject: selftests: ntsync: correct CONFIG_NTSYNC name The config fragment for these tests defines CONFIG_WINESYNC, which refers to an earlier name for the ntsync driver before it was merged [1]. Correct it to define CONFIG_NTSYNC instead. [1] https://lore.kernel.org/all/f4cc1a38-1441-62f8-47e4-0c67f5ad1d43@codeweavers.com/ Fixes: 7f853a252cde ("selftests: ntsync: Add some tests for semaphore state.") Cc: stable Signed-off-by: Ethan Nelson-Moore Reviewed-by: Elizabeth Figura Link: https://patch.msgid.link/20260609175505.19632-1-enelsonmoore@gmail.com Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/drivers/ntsync/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/ntsync/config b/tools/testing/selftests/drivers/ntsync/config index 60539c826d06..0aa68de147af 100644 --- a/tools/testing/selftests/drivers/ntsync/config +++ b/tools/testing/selftests/drivers/ntsync/config @@ -1 +1 @@ -CONFIG_WINESYNC=y +CONFIG_NTSYNC=y -- cgit v1.2.3 From ce1fed11d18e163baf7f875152a33bf80f625c1a Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 17 Jun 2026 22:53:50 +0800 Subject: misc: nsm: only unlock nsm_dev on post-lock error paths nsm_dev_ioctl() jumps to the common out label even when the initial copy_from_user() fails before nsm->lock has been taken. The error path then blindly unlocks a mutex that was never acquired. This issue was found by our static analysis tool and then manually reviewed against the current tree. The grounded PoC kept the miscdevice ioctl entry and the pre-lock copy_from_user(&raw, argp, _IOC_SIZE(cmd)) failure path by issuing NSM_IOCTL_RAW with an invalid user pointer. That failure reaches the shared out label before mutex_lock(&nsm->lock). Lockdep reported: WARNING: bad unlock balance detected! exploit/193 is trying to release lock (&global_nsm.lock) at: nsm_dev_ioctl+0x5f/0xcf [vuln_msv] but there are no more locks to release! no locks held by exploit/193. Return immediately on the pre-lock copy_from_user() failure and keep the common unlock label for the post-lock paths only. Fixes: b9873755a6c8 ("misc: Add Nitro Secure Module driver") Cc: stable Signed-off-by: Runyu Xiao Reviewed-by: Alexander Graf Link: https://patch.msgid.link/20260617145350.513875-1-runyu.xiao@seu.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/nsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index ef7b32742340..185900cdad4a 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -367,7 +367,7 @@ static long nsm_dev_ioctl(struct file *file, unsigned int cmd, /* Copy user argument struct to kernel argument struct */ r = -EFAULT; if (copy_from_user(&raw, argp, _IOC_SIZE(cmd))) - goto out; + return r; mutex_lock(&nsm->lock); -- cgit v1.2.3 From f112ea910e554d58b4b39a4492b7d302f0f4204f Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 5 Jul 2026 18:12:59 +0300 Subject: mei: bus: access mei_device under device_lock on cleanup Fix couple of problems in mei_cl_bus_dev_release(): mei_cl_flush_queues() is running without lock. bus->file_list access after mei_dev_bus_put(bus) can become a use-after-free if this was the last reference to bus. Protect queues cleanup and WARN traversal by device lock there to avoid the concurrent access problems. Move WARN traversal before mei_dev_bus_put(bus). This file uses bus variable name for mei_device, adjust code of mei_cl_bus_dev_release() to use bus variable too. Cc: stable Fixes: 35e8a426b16a ("mei: bus: Check for still connected devices in mei_cl_bus_dev_release()") Reviewed-by: Menachem Adin Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260705151259.3054795-1-alexander.usyskin@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index fcde082eb5e3..cfb87ab8667f 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -4,6 +4,7 @@ * Intel Management Engine Interface (Intel MEI) Linux driver */ +#include #include #include #include @@ -1330,15 +1331,16 @@ static void mei_dev_bus_put(struct mei_device *bus) static void mei_cl_bus_dev_release(struct device *dev) { struct mei_cl_device *cldev = to_mei_cl_device(dev); - struct mei_device *mdev = cldev->cl->dev; + struct mei_device *bus = cldev->bus; struct mei_cl *cl; - mei_cl_flush_queues(cldev->cl, NULL); - mei_me_cl_put(cldev->me_cl); - mei_dev_bus_put(cldev->bus); - - list_for_each_entry(cl, &mdev->file_list, link) - WARN_ON(cl == cldev->cl); + scoped_guard(mutex, &bus->device_lock) { + mei_cl_flush_queues(cldev->cl, NULL); + mei_me_cl_put(cldev->me_cl); + list_for_each_entry(cl, &bus->file_list, link) + WARN_ON(cl == cldev->cl); + } + mei_dev_bus_put(bus); kfree(cldev->cl); kfree(cldev); -- cgit v1.2.3 From 3b231f1e9990f4c21220d0a69733ce2105891ff9 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 13 Jul 2026 13:55:23 +0800 Subject: misc: nsm: pin the module while the device is open misc_open() installs a misc driver's file operations with fops_get(), which pins file_operations::owner before replacing the file's f_op. The NSM misc device leaves nsm_dev_fops.owner unset, so opening /dev/nsm does not take a module reference on the nsm driver. If the driver is built as a module, an open file descriptor can therefore survive rmmod of the module that provides its ioctl callbacks. A later ioctl through that descriptor can call into unloaded module text. Set nsm_dev_fops.owner to THIS_MODULE so the misc core holds the module while any /dev/nsm file descriptor is open, matching the lifetime expectation for the installed file operations. Fixes: b9873755a6c8 ("misc: Add Nitro Secure Module driver") Cc: stable Signed-off-by: Xu Rao Link: https://patch.msgid.link/BE6951D13B5E5513+20260713055523.3193089-1-raoxu@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/nsm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index 185900cdad4a..3960506eb7ab 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -413,6 +413,7 @@ static int nsm_device_init_vq(struct virtio_device *vdev) } static const struct file_operations nsm_dev_fops = { + .owner = THIS_MODULE, .unlocked_ioctl = nsm_dev_ioctl, .compat_ioctl = compat_ptr_ioctl, }; -- cgit v1.2.3 From 761b785a0cfbce43761227bc42a7f984f31f8921 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Jul 2026 15:08:51 +0800 Subject: intel_th: fix MSC output device reference leak intel_th_output_open() looks up the output device with bus_find_device_by_devt(), which returns the device with a reference that must be dropped after use. commit 95fc36a234da ("intel_th: fix device leak on output open()") attempted to drop the reference from intel_th_output_release(). However, a successful open replaces file->f_op with the output driver file operations before returning, so close runs the output driver release callback instead. For MSC outputs, close runs intel_th_msc_release(), which only removes the per-file iterator and does not drop the device reference taken by intel_th_output_open(). Consequently, every successful MSC output open leaks one device reference. Drop the device reference from intel_th_msc_release(), which is the release path actually used for MSC output files. Remove the now-unused intel_th_output_release() callback from intel_th_output_fops. Fixes: 95fc36a234da ("intel_th: fix device leak on output open()") Cc: stable Signed-off-by: Guangshuo Li Reviewed-by: Johan Hovold Link: https://patch.msgid.link/20260715070851.2077965-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/core.c | 10 ---------- drivers/hwtracing/intel_th/msu.c | 2 ++ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 3924e63e2eee..56acf31546da 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -843,18 +843,8 @@ err_put_dev: return err; } -static int intel_th_output_release(struct inode *inode, struct file *file) -{ - struct intel_th_device *thdev = file->private_data; - - put_device(&thdev->dev); - - return 0; -} - static const struct file_operations intel_th_output_fops = { .open = intel_th_output_open, - .release = intel_th_output_release, .llseek = noop_llseek, }; diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index a82cf74f39ad..84d99d7b1d20 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -1490,8 +1490,10 @@ static int intel_th_msc_release(struct inode *inode, struct file *file) { struct msc_iter *iter = file->private_data; struct msc *msc = iter->msc; + struct intel_th_device *thdev = msc->thdev; msc_iter_remove(iter, msc); + put_device(&thdev->dev); return 0; } -- cgit v1.2.3 From bb66b1a3452534adb8b72abf2f761375970fe472 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 8 Jul 2026 12:00:26 +0000 Subject: rust_binder: only print failure if error has source The commit that fixes BINDER_GET_EXTENDED_ERROR changed the condition for printing transaction failures so errors are printed even if the cause is a dead or frozen process. Undo this change so that the error is only printed if the failure has an errno associated with it. Cc: stable@kernel.org Fixes: 77bfebf11077 ("rust_binder: fix BINDER_GET_EXTENDED_ERROR") Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260708-get-extended-error-fix-printing-v1-1-6e293b213b70@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/thread.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 3b8520813941..bc0ef8927905 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1273,14 +1273,14 @@ impl Thread { inner.extended_error = ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno()); } - } - pr_warn!( - "{}:{} transaction to {} failed: {err:?}", - info.from_pid, - info.from_tid, - info.to_pid - ); + pr_warn!( + "{}:{} transaction to {} failed: {err:?}", + info.from_pid, + info.from_tid, + info.to_pid + ); + } } } -- cgit v1.2.3 From 5c4851e4cdfc3204128f88991be64a6e3929d3d0 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Thu, 25 Jun 2026 17:18:52 -0400 Subject: lockd: fix NLMv3 GRANTED_MSG handling GRANTED_MSG is a server-to-client callback, so it runs on the client, where nfsd never registers nlmsvc_ops. The nlm3svc_lookup_host() helper is for the server-side request handlers (TEST/LOCK/CANCEL/UNLOCK), which reach nlmsvc_ops->fopen and must reject requests when nfsd isn't running. GRANTED_MSG only calls nlmclnt_grant(). Instead, of calling nlm3svc_lookup_host(), which results in a client failing a GRANTED_MSG call, call nlmsvc_lookup_host(). Fixes: 6c534ad999b6 ("lockd: Use xdrgen XDR functions for the NLMv3 GRANTED_MSG procedure") Cc: stable@vger.kernel.org Signed-off-by: Olga Kornievskaia Reviewed-by: NeilBrown Link: https://patch.msgid.link/20260625211852.31972-1-okorniev@redhat.com Signed-off-by: Chuck Lever --- fs/lockd/svcproc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 4836887f11ef..386a881b520f 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -901,7 +901,8 @@ static __be32 nlmsvc_proc_granted_msg(struct svc_rqst *rqstp) if (argp->xdrgen.cookie.len > NLM_MAXCOOKIELEN) return rpc_garbage_args; - host = nlm3svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + host = nlmsvc_lookup_host(rqstp, argp->xdrgen.alock.caller_name.data, + argp->xdrgen.alock.caller_name.len); if (!host) return rpc_system_err; -- cgit v1.2.3 From 6e8c279f93dba93f42be7c9c6995bc8e318ba2e4 Mon Sep 17 00:00:00 2001 From: Elizabeth Figura Date: Wed, 1 Jul 2026 13:32:14 -0500 Subject: MAINTAINERS: Update wine-devel list address Wine mailing lists were moved from winehq.org to list.winehq.org in December 2025. The old address no longer works. Signed-off-by: Elizabeth Figura Link: https://patch.msgid.link/20260701183214.13973-1-zfigura@codeweavers.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index e8704c09e0ed..63276b82abb5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19298,7 +19298,7 @@ F: fs/ntfs3/ NTSYNC SYNCHRONIZATION PRIMITIVE DRIVER M: Elizabeth Figura -L: wine-devel@winehq.org +L: wine-devel@list.winehq.org S: Supported F: Documentation/userspace-api/ntsync.rst F: drivers/misc/ntsync.c -- cgit v1.2.3 From 40a22ad1b5cb86a27f54d7837be20113d0eb49d2 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 10 Jun 2026 17:18:21 +0200 Subject: drm/appletbdrm: Allocate request/response buffers in begin_fb_access In atomic_check, damage handling is not fully evaluated. Another atomic_check helper could trigger a full modeset and thus invalidate damage clips. Allocation of the request/response buffers in appletbdrm depends on correct damage information. Otherwise it might allocate incorrectly sized buffers. Allocate the buffers in the driver's begin_fb_access helper. It runs early during the commit when damage clipping has been fully evaluated. v5: - pass plane state as the old damage-iterator state v2: - allocate before drm_gem_begin_shadow_fb_access() to avoid leak on error Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Acked-by: Aditya Garg Acked-by: Zack Rusin Link: https://patch.msgid.link/20260610152505.260172-6-tzimmermann@suse.de --- drivers/gpu/drm/tiny/appletbdrm.c | 53 ++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c index cdd35af49892..ef71b9957961 100644 --- a/drivers/gpu/drm/tiny/appletbdrm.c +++ b/drivers/gpu/drm/tiny/appletbdrm.c @@ -315,33 +315,16 @@ static const u32 appletbdrm_primary_plane_formats[] = { DRM_FORMAT_XRGB8888, /* emulated */ }; -static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, - struct drm_atomic_commit *state) +static int appletbdrm_primary_plane_helper_begin_fb_access(struct drm_plane *plane, + struct drm_plane_state *new_plane_state) { - struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); - struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); - struct drm_crtc *new_crtc = new_plane_state->crtc; - struct drm_crtc_state *new_crtc_state = NULL; struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(new_plane_state); + size_t frames_size = 0; struct drm_atomic_helper_damage_iter iter; struct drm_rect damage; - size_t frames_size = 0; size_t request_size; - int ret; - - if (new_crtc) - new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); - ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, - DRM_PLANE_NO_SCALING, - DRM_PLANE_NO_SCALING, - false, false); - if (ret) - return ret; - else if (!new_plane_state->visible) - return 0; - - drm_atomic_helper_damage_iter_init(&iter, old_plane_state, new_plane_state); + drm_atomic_helper_damage_iter_init(&iter, plane->state, new_plane_state); drm_atomic_for_each_plane_damage(&iter, &damage) { frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage)); } @@ -366,6 +349,29 @@ static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, appletbdrm_state->request_size = request_size; appletbdrm_state->frames_size = frames_size; + return drm_gem_begin_shadow_fb_access(plane, new_plane_state); +} + +static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_commit *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + int ret; + + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; + return 0; } @@ -468,7 +474,7 @@ end_fb_cpu_access: } static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plane, - struct drm_atomic_commit *old_state) + struct drm_atomic_commit *old_state) { struct appletbdrm_device *adev = drm_to_adev(plane->dev); struct drm_device *drm = plane->dev; @@ -552,7 +558,8 @@ static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane, } static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs = { - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .begin_fb_access = appletbdrm_primary_plane_helper_begin_fb_access, + .end_fb_access = drm_gem_end_shadow_fb_access, .atomic_check = appletbdrm_primary_plane_helper_atomic_check, .atomic_update = appletbdrm_primary_plane_helper_atomic_update, .atomic_disable = appletbdrm_primary_plane_helper_atomic_disable, -- cgit v1.2.3 From a3fdf74ffa5966e5b4a17f1e9c5687f73bb0d536 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 16 Jul 2026 13:13:58 -0700 Subject: drm/ttm/pool: back up at native page order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ttm_pool_split_for_swap() unconditionally splits high-order pool pages into order-0 pages before backup, so every compound the shrinker touches is shattered even when the rest of the system would prefer it stay intact. Under sustained kswapd pressure this fragments memory enough to drive other parts of MM into recovery loops. Back up each compound at its native order instead. In ttm_pool_backup(), hand the full compound to the new ttm_backup_backup_folio(), which backs up subpages to a contiguous range of shmem indices and returns the base handle plus the number of subpages actually backed up (@nr_backed). On full success, free the compound once at its native order -- no split_page(), no per-4K refcount juggling. A per-folio backup can't be made fully atomic under memory pressure: ttm_backup_backup_folio() must allocate shmem folios before source subpages can be released, so under true OOM any subpage may fail while the rest of the compound is still live. Two mechanisms handle this without regressing reclaim behaviour: - alloc_gfp gets __GFP_NOMEMALLOC whenever order > 0 (cleared again for order-0), so a high-order backup fails fast with -ENOMEM instead of draining kernel reserves, leaving them for other allocations under the same pressure. - If ttm_backup_backup_folio() still returns a short @nr_backed with a valid handle for the successfully-backed prefix, split the source compound with ttm_pool_split_for_swap(), free the prefix as order-0 pages (already safely in shmem), and retry the remaining subpages at order 0, where __GFP_NOMEMALLOC is cleared and reserves may be used as a last resort. This preserves the original split-on-OOM fallback while keeping the common case fragmentation-free, and preserves the "partial backup is allowed" contract (shrunken is incremented per subpage backed up). The restore-side leftover-page split in ttm_pool_restore_commit() is left as-is: it's unreachable in practice and not worth complicating the restore state machine to avoid. Testing: the existing backup_fault_inject point only truncated tt->num_pages, which never exercised the reactive split path above since it never left a compound partially backed up. Wire fault injection into ttm_backup_backup_folio() itself: past the first subpage of a compound, synthesize a -ENOMEM in place of shmem_read_folio_gfp() when should_fail() trips, producing the same short @nr_pages_backed a real failure would and forcing ttm_pool_backup() through the split-and-retry path. The fault_attr stays private to ttm_pool.c; ttm_backup.c reaches it through ttm_backup_fault_inject_folio(), declared in ttm_pool_internal.h. While converting the writeback branch to operate on the whole folio, the unlock condition after shmem_writeout() also changed from `if (ret)` to `if (ret == AOP_WRITEPAGE_ACTIVATE)`, matching the actual contract: shmem_writeout()/swap_writeout() only leave the folio locked when returning AOP_WRITEPAGE_ACTIVATE; any other return (including a hard error from arch_prepare_to_swap()) means the folio was already unlocked internally. The old `if (ret)` check would have double- unlocked in that hard-error case. Cc: Christian Koenig Cc: Huang Rui Cc: Matthew Auld Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org Fixes: b63d715b8090 ("drm/ttm/pool, drm/ttm/tt: Provide a helper to shrink pages") Suggested-by: Thomas Hellström Assisted-by: Claude:claude-opus-4.6 Signed-off-by: Matthew Brost Reviewed-by: Thomas Hellström Link: https://patch.msgid.link/20260716201358.4086085-1-matthew.brost@intel.com --- drivers/gpu/drm/ttm/ttm_backup.c | 129 +++++++++++++++++++++----------- drivers/gpu/drm/ttm/ttm_pool.c | 89 ++++++++++++++++++---- drivers/gpu/drm/ttm/ttm_pool_internal.h | 8 ++ include/drm/ttm/ttm_backup.h | 12 +-- 4 files changed, 173 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c index 81df4cb5606b..3c067aadc52d 100644 --- a/drivers/gpu/drm/ttm/ttm_backup.c +++ b/drivers/gpu/drm/ttm/ttm_backup.c @@ -6,9 +6,10 @@ #include #include -#include #include +#include "ttm_pool_internal.h" + /* * Need to map shmem indices to handle since a handle value * of 0 means error, following the swp_entry_t convention. @@ -68,17 +69,23 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, } /** - * ttm_backup_backup_page() - Backup a page + * ttm_backup_backup_folio() - Backup a folio * @backup: The struct backup pointer to use. - * @page: The page to back up. - * @writeback: Whether to perform immediate writeback of the page. + * @folio: The folio to back up. + * @order: The allocation order of @folio. Since TTM allocates higher-order + * pages without __GFP_COMP, folio_nr_pages(@folio) would always + * return 1; the caller must pass the true order explicitly. + * @writeback: Whether to perform immediate writeback of the folio's pages. * This may have performance implications. - * @idx: A unique integer for each page and each struct backup. + * @idx: A unique integer for the first page of the folio and each struct backup. * This allows the backup implementation to avoid managing * its address space separately. - * @page_gfp: The gfp value used when the page was allocated. - * This is used for accounting purposes. + * @folio_gfp: The gfp value used when the folio was allocated. + * Currently unused. * @alloc_gfp: The gfp to be used when allocating memory. + * @nr_pages_backed: Output. On a successful return, set to the number of + * pages actually backed up, which may be less than (1 << @order) + * if an -ENOMEM was encountered mid-folio. * * Context: If called from reclaim context, the caller needs to * assert that the shrinker gfp has __GFP_FS set, to avoid @@ -87,53 +94,87 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, * that the shrinker gfp has __GFP_IO set, since without it, * we're not allowed to start backup IO. * - * Return: A handle on success. Negative error code on failure. - * - * Note: This function could be extended to back up a folio and - * implementations would then split the folio internally if needed. - * Drawback is that the caller would then have to keep track of - * the folio size- and usage. + * Return: A handle for the first backed-up page on success (handles for + * subsequent pages follow sequentially). -ENOMEM if no pages could be backed + * up. Any other negative error code if a non-ENOMEM failure occurred; in that + * case any pages backed up so far are truncated before returning. */ s64 -ttm_backup_backup_page(struct file *backup, struct page *page, - bool writeback, pgoff_t idx, gfp_t page_gfp, - gfp_t alloc_gfp) +ttm_backup_backup_folio(struct file *backup, struct folio *folio, + unsigned int order, bool writeback, pgoff_t idx, + gfp_t folio_gfp, gfp_t alloc_gfp, + pgoff_t *nr_pages_backed) { struct address_space *mapping = backup->f_mapping; - unsigned long handle = 0; + int nr_pages = 1 << order; struct folio *to_folio; - int ret; - - to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp); - if (IS_ERR(to_folio)) - return PTR_ERR(to_folio); - - folio_mark_accessed(to_folio); - folio_lock(to_folio); - folio_mark_dirty(to_folio); - copy_highpage(folio_file_page(to_folio, idx), page); - handle = ttm_backup_shmem_idx_to_handle(idx); - - if (writeback && !folio_mapped(to_folio) && - folio_clear_dirty_for_io(to_folio)) { - folio_set_reclaim(to_folio); - ret = shmem_writeout(to_folio, NULL, NULL); - if (!folio_test_writeback(to_folio)) - folio_clear_reclaim(to_folio); + int ret, i; + + *nr_pages_backed = 0; + + for (i = 0; i < nr_pages; ) { + int to_nr, j; + /* - * If writeout succeeds, it unlocks the folio. errors - * are otherwise dropped, since writeout is only best - * effort here. + * Only inject past the first subpage so *nr_pages_backed is + * always > 0 here, matching a genuine mid-compound -ENOMEM + * and driving the caller's reactive split fallback instead + * of an early, no-progress failure. */ - if (ret) + if (IS_ENABLED(CONFIG_FAULT_INJECTION) && i && + ttm_backup_fault_inject_folio()) + to_folio = ERR_PTR(-ENOMEM); + else + to_folio = shmem_read_folio_gfp(mapping, idx + i, alloc_gfp); + if (IS_ERR(to_folio)) { + int err = PTR_ERR(to_folio); + + if (err == -ENOMEM && *nr_pages_backed) + return ttm_backup_shmem_idx_to_handle(idx); + + if (*nr_pages_backed) { + shmem_truncate_range(file_inode(backup), + (loff_t)idx << PAGE_SHIFT, + ((loff_t)(idx + i) << PAGE_SHIFT) - 1); + /* + * The pages just truncated are no longer + * backed up; don't let the caller mistake + * them for valid handles. + */ + *nr_pages_backed = 0; + } + return err; + } + + to_nr = min_t(int, nr_pages - i, + folio_next_index(to_folio) - (idx + i)); + + folio_mark_accessed(to_folio); + folio_lock(to_folio); + folio_mark_dirty(to_folio); + + for (j = 0; j < to_nr; j++) + copy_highpage(folio_file_page(to_folio, idx + i + j), + folio_page(folio, i + j)); + + if (writeback && !folio_mapped(to_folio) && + folio_clear_dirty_for_io(to_folio)) { + folio_set_reclaim(to_folio); + ret = shmem_writeout(to_folio, NULL, NULL); + if (!folio_test_writeback(to_folio)) + folio_clear_reclaim(to_folio); + if (ret == AOP_WRITEPAGE_ACTIVATE) + folio_unlock(to_folio); + } else { folio_unlock(to_folio); - } else { - folio_unlock(to_folio); - } + } - folio_put(to_folio); + folio_put(to_folio); + i += to_nr; + *nr_pages_backed = i; + } - return handle; + return ttm_backup_shmem_idx_to_handle(idx); } /** diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 46983e7de7a3..e7648ad3e615 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -53,8 +53,23 @@ #ifdef CONFIG_FAULT_INJECTION #include static DECLARE_FAULT_ATTR(backup_fault_inject); + +/* + * Exposed to ttm_backup.c so a mid-compound subpage can be made to fail + * with -ENOMEM, exercising the reactive split-and-retry fallback in + * ttm_pool_backup() for high-order backups. + */ +bool ttm_backup_fault_inject_folio(void) +{ + return should_fail(&backup_fault_inject, 1); +} #else #define should_fail(...) false + +bool ttm_backup_fault_inject_folio(void) +{ + return false; +} #endif /** @@ -487,7 +502,7 @@ static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct page *p) /** * DOC: Partial backup and restoration of a struct ttm_tt. * - * Swapout using ttm_backup_backup_page() and swapin using + * Swapout using ttm_backup_backup_folio() and swapin using * ttm_backup_copy_page() may fail. * The former most likely due to lack of swap-space or memory, the latter due * to lack of memory or because of signal interruption during waits. @@ -1036,12 +1051,12 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, { struct file *backup = tt->backup; struct page *page; - unsigned long handle; gfp_t alloc_gfp; gfp_t gfp; int ret = 0; pgoff_t shrunken = 0; - pgoff_t i, num_pages; + pgoff_t i, j, num_pages, npages; + pgoff_t nr_backed; if (WARN_ON(ttm_tt_is_backed_up(tt))) return -EINVAL; @@ -1119,9 +1134,11 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (IS_ENABLED(CONFIG_FAULT_INJECTION) && should_fail(&backup_fault_inject, 1)) num_pages = DIV_ROUND_UP(num_pages, 2); - for (i = 0; i < num_pages; ++i) { - s64 shandle; + for (i = 0; i < num_pages; i += npages) { + unsigned int order; + s64 handle; + npages = 1; page = tt->pages[i]; if (unlikely(!page)) continue; @@ -1130,19 +1147,61 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt, if (unlikely(ttm_backup_page_ptr_is_handle(page))) continue; - ttm_pool_split_for_swap(pool, page); + order = ttm_pool_page_order(pool, page); + npages = 1UL << order; + + /* + * We don't allow dipping kernel reserves for high order backup + */ + if (order) + alloc_gfp |= __GFP_NOMEMALLOC; + else + alloc_gfp &= ~__GFP_NOMEMALLOC; + + /* + * Back up the compound atomically at its native order. If + * fault injection truncated num_pages mid-compound, skip + * the partial tail rather than splitting. + */ + if (unlikely(i + npages > num_pages)) + break; - shandle = ttm_backup_backup_page(backup, page, flags->writeback, i, - gfp, alloc_gfp); - if (shandle < 0) { - /* We allow partially shrunken tts */ - ret = shandle; + handle = ttm_backup_backup_folio(backup, page_folio(page), + order, flags->writeback, i, + gfp, alloc_gfp, + &nr_backed); + /* + * Zero progress on this compound (whether order 0 or a + * high-order compound that failed before backing up even + * its first subpage) is unrecoverable: bail out rather than + * looping forever with npages == nr_backed == 0 below. + */ + if (unlikely(handle < 0 && !nr_backed)) { + ret = handle; break; } - handle = shandle; - tt->pages[i] = ttm_backup_handle_to_page_ptr(handle); - __free_pages_gpu_account(page, 0, false); - shrunken++; + + for (j = 0; j < nr_backed; j++) + tt->pages[i + j] = ttm_backup_handle_to_page_ptr(handle + j); + + shrunken += nr_backed; + + if (unlikely(nr_backed < npages)) { + /* + * Partial OOM backup: split the compound and free the + * subpages whose content is now in shmem. Continue the + * loop from the first un-backed order-0 page. + */ + ttm_pool_split_for_swap(pool, page); + for (j = 0; j < nr_backed; j++) + __free_pages_gpu_account(page + j, 0, false); + npages = nr_backed; + continue; + } + + /* Fully backed up: free at native order. */ + page->private = 0; + __free_pages_gpu_account(page, order, false); } return shrunken ? shrunken : ret; diff --git a/drivers/gpu/drm/ttm/ttm_pool_internal.h b/drivers/gpu/drm/ttm/ttm_pool_internal.h index 24c179fd69d1..cbb17a2129fe 100644 --- a/drivers/gpu/drm/ttm/ttm_pool_internal.h +++ b/drivers/gpu/drm/ttm/ttm_pool_internal.h @@ -22,4 +22,12 @@ static inline unsigned int ttm_pool_beneficial_order(struct ttm_pool *pool) return pool->alloc_flags & 0xff; } +/* + * Implemented in ttm_pool.c, used by ttm_backup.c. Returns true if a fault + * should be injected mid-compound to test the reactive split-and-retry + * fallback in ttm_pool_backup(). Always returns false when + * CONFIG_FAULT_INJECTION is disabled. + */ +bool ttm_backup_fault_inject_folio(void); + #endif diff --git a/include/drm/ttm/ttm_backup.h b/include/drm/ttm/ttm_backup.h index 29b9c855af77..49efa713e87c 100644 --- a/include/drm/ttm/ttm_backup.h +++ b/include/drm/ttm/ttm_backup.h @@ -13,9 +13,8 @@ * ttm_backup_handle_to_page_ptr() - Convert handle to struct page pointer * @handle: The handle to convert. * - * Converts an opaque handle received from the - * ttm_backup_backup_page() function to an (invalid) - * struct page pointer suitable for a struct page array. + * Converts an opaque handle received from a ttm_backup_backup_*() + * function to an (invalid) struct page pointer suitable for a struct page array. * * Return: An (invalid) struct page pointer. */ @@ -59,9 +58,10 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst, pgoff_t handle, bool intr, gfp_t additional_gfp); s64 -ttm_backup_backup_page(struct file *backup, struct page *page, - bool writeback, pgoff_t idx, gfp_t page_gfp, - gfp_t alloc_gfp); +ttm_backup_backup_folio(struct file *backup, struct folio *folio, + unsigned int order, bool writeback, pgoff_t idx, + gfp_t folio_gfp, gfp_t alloc_gfp, + pgoff_t *nr_pages_backed); void ttm_backup_fini(struct file *backup); -- cgit v1.2.3 From 26b483d52417253d88a3a01262ac85914a7aec8e Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 17 Jul 2026 17:25:58 +0100 Subject: Revert "arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates" This reverts commit e057b94772328221405b067c3a85fe479b915dc8. Sashiko points out that updating 'orig_x0' after secure_computing() has returned is too late to handle the case where a seccomp filter is re-evaluated after initially returning SECCOMP_RET_TRACE. This means that a tracer can manipulate the first argument of the syscall behind seccomp's back. For now, revert the initial fix and we'll have another crack at it soon. Since the incorrect fix was cc'd to stable, do the same here with an appropriate fixes tag. Cc: stable@vger.kernel.org Fixes: e057b9477232 ("arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates") Link: https://sashiko.dev/#/patchset/20260716120640.6590-1-will@kernel.org Signed-off-by: Will Deacon --- arch/arm64/kernel/ptrace.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 390c9b2bd966..4d08598e2891 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -2408,21 +2408,6 @@ static void report_syscall_exit(struct pt_regs *regs) } } -static void update_syscall_orig_x0_after_ptrace(struct pt_regs *regs) -{ - /* - * Keep orig_x0 authoritative so that seccomp (via - * syscall_get_arguments()), audit and the restart path all see the same - * first argument the syscall is dispatched with, even if it has been - * updated by a tracer. Skip this for NO_SYSCALL (set either by the user - * or the tracer), as regs[0] holds the return value (see the comment in - * el0_svc_common()) and can be unwound using syscall_rollback(). - * For compat tasks, orig_r0 is provided directly through GPR index 17. - */ - if (!is_compat_task() && regs->syscallno != NO_SYSCALL) - regs->orig_x0 = regs->regs[0]; -} - int syscall_trace_enter(struct pt_regs *regs) { unsigned long flags = read_thread_flags(); @@ -2432,26 +2417,12 @@ int syscall_trace_enter(struct pt_regs *regs) ret = report_syscall_entry(regs); if (ret || (flags & _TIF_SYSCALL_EMU)) return NO_SYSCALL; - - /* - * Ensure ptrace changes to x0 during a regular - * syscall-enter-stop (PTRACE_SYSCALL) are visible to - * subsequent seccomp checks, tracepoints and audit. - */ - update_syscall_orig_x0_after_ptrace(regs); } /* Do the secure computing after ptrace; failures should be fast. */ if (secure_computing() == -1) return NO_SYSCALL; - /* - * Ensure tracer changes to x0 during seccomp ptrace exit - * processing (SECCOMP_RET_TRACE) are visible to tracepoints and - * audit. - */ - update_syscall_orig_x0_after_ptrace(regs); - if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) trace_sys_enter(regs, regs->syscallno); -- cgit v1.2.3 From b4bee12ebeccfcd5959ace2c3a4af08f5a917d4c Mon Sep 17 00:00:00 2001 From: Soeren Moch Date: Fri, 17 Jul 2026 11:32:03 +0800 Subject: PCI: imx6: Keep i.MX6 Root Port MSI/MSI-X Capabilities with iMSI-RX to work around hardware bug On some NXP chipsets, disabling Root Port MSI/MSI-X Capabilities blocks MSIs originating from Endpoints from reaching the iMSI-RX controller. To address this hardware bug, commit 3a4e8302e72f ("PCI: imx6: Keep Root Port MSI capability with iMSI-RX to work around hardware bug") preserves Root Port MSI and MSI-X Capabilities on i.MX7D, i.MX8MM, and i.MX8MQ when iMSI-RX is in use. The same applies to i.MX6Q, i.MX6QP, and i.MX6SX, so preserve Root Port MSI/MSI-X Capabilities there as well. Note that preserving these Capabilities means Root Port-originated MSIs such as AER and PME won't be received due to separate hardware limitations. Users may need to use workarounds such as passing the 'pcie_pme=nomsi' command-line parameter. Fixes: f5cd8a929c825 ("PCI: dwc: Remove MSI/MSIX capability for Root Port if iMSI-RX is used as MSI controller") Signed-off-by: Soeren Moch Signed-off-by: Richard Zhu Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Reviewed-by: Manivannan Sadhasivam Acked-by: Richard Zhu Cc: stable@vger.kernel.org # 7.0+ Link: https://patch.msgid.link/20260717033203.2965045-1-hongxing.zhu@oss.nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 98e1db751132..dba27eac6bff 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1995,7 +1995,8 @@ static const struct imx_pcie_drvdata drvdata[] = { .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | IMX_PCIE_FLAG_BROKEN_SUSPEND | - IMX_PCIE_FLAG_SUPPORTS_SUSPEND, + IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", .ltssm_off = IOMUXC_GPR12, @@ -2011,7 +2012,8 @@ static const struct imx_pcie_drvdata drvdata[] = { .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | IMX_PCIE_FLAG_SKIP_L23_READY | - IMX_PCIE_FLAG_SUPPORTS_SUSPEND, + IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP, .gpr = "fsl,imx6q-iomuxc-gpr", .ltssm_off = IOMUXC_GPR12, .ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2, @@ -2026,7 +2028,8 @@ static const struct imx_pcie_drvdata drvdata[] = { .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | IMX_PCIE_FLAG_SKIP_L23_READY | - IMX_PCIE_FLAG_SUPPORTS_SUSPEND, + IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", .ltssm_off = IOMUXC_GPR12, -- cgit v1.2.3 From 3afc64c61ce906a04f073ca350b46de10e8302f9 Mon Sep 17 00:00:00 2001 From: Woraphat Khiaodaeng Date: Fri, 17 Jul 2026 22:45:37 +0700 Subject: io_uring/bpf-ops: reject re-registration of an already-bound ops io_install_bpf() only rejects a second registration on the ctx side (ctx->bpf_ops) and sets the per-map back-pointer ops->priv unconditionally. The struct_ops link path never advances a map past BPF_STRUCT_OPS_STATE_READY, so the same io_uring_bpf_ops map can be registered more than once, and bpf_io_reg() re-resolves the target ring via fget(ops->ring_fd) on every call. A caller can therefore point the same ring_fd at a different io_ring_ctx between two BPF_LINK_CREATE calls. The second registration passes the ctx->bpf_ops check (the new ctx has none) and overwrites ops->priv, orphaning the first ctx. Teardown (io_eject_bpf()/bpf_io_unreg()) only reaches a ctx through ops->priv, so the orphaned ctx is never torn down: its ctx->loop_step keeps pointing into the struct_ops trampoline, which is freed once the map is gone. A later io_uring_enter() on the orphaned ring then calls the dangling ctx->loop_step from io_run_loop() -- a use-after-free of freed executable memory, reachable by a task with CAP_BPF + CAP_PERFMON. Reject registration when ops->priv is already set, as hid_bpf_reg() does for its struct_ops. Cc: stable@vger.kernel.org Fixes: 98f37634b12b ("io_uring/bpf-ops: implement bpf ops registration") Signed-off-by: Woraphat Khiaodaeng Reviewed-by: Gabriel Krisman Bertazi Reviewed-by: Pavel Begunkov Link: https://patch.msgid.link/20260717154537.129736-1-worapat.kd2@gmail.com Signed-off-by: Jens Axboe --- io_uring/bpf-ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io_uring/bpf-ops.c b/io_uring/bpf-ops.c index 5a50f0675fe5..cf2bd068e331 100644 --- a/io_uring/bpf-ops.c +++ b/io_uring/bpf-ops.c @@ -168,6 +168,8 @@ static int io_install_bpf(struct io_ring_ctx *ctx, struct io_uring_bpf_ops *ops) if (ctx->bpf_ops) return -EBUSY; + if (ops->priv) + return -EBUSY; if (WARN_ON_ONCE(!ops->loop_step)) return -EINVAL; -- cgit v1.2.3 From 62c740f823a8e47ffe56e45a7472c27cf988e2f6 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Wed, 3 Jun 2026 11:05:21 +0000 Subject: drivers/virt: pkvm: Fix end calculation in mmio_guard_ioremap_hook() Sashiko (locally) reports a logical issues in mmio_guard_ioremap_hook() mmio_guard_ioremap_hook() attempts to handle unaligned addresses and sizes. However, aligning the start address before adding the size, might shift the end to the page before. Fixes: 0f1269495800 ("drivers/virt: pkvm: Intercept ioremap using pKVM MMIO_GUARD hypercall") Signed-off-by: Mostafa Saleh Reviewed-by: Catalin Marinas Tested-by: Aneesh Kumar K.V (Arm) Signed-off-by: Will Deacon --- drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c index 4230b817a80b..d66291def0f4 100644 --- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c +++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c @@ -82,8 +82,8 @@ static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size, if (protval != PROT_DEVICE_nGnRE && protval != PROT_DEVICE_nGnRnE) return 0; + end = PAGE_ALIGN(phys + size); phys = PAGE_ALIGN_DOWN(phys); - end = phys + PAGE_ALIGN(size); while (phys < end) { const int func_id = ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_FUNC_ID; -- cgit v1.2.3 From 879a6754d3d11e30af24b7dc486f561510d62641 Mon Sep 17 00:00:00 2001 From: Pu Hu Date: Fri, 10 Jul 2026 06:32:53 +0000 Subject: arm64: kprobes: Only handle faults originating from XOL slot kprobe_fault_handler() currently treats any page fault taken while in KPROBE_HIT_SS or KPROBE_REENTER state as a kprobe single-step fault. This assumption does not hold: perf or tracing code may run from the debug exception path during the single-step window and take its own page fault. When the fault is handled as a kprobe fault, the PC is rewritten to the probe address, corrupting the exception recovery context for the real fault. A typical reproducer is running perf with preemptirq tracepoints and dwarf callchains while a kprobe is installed on a frequently executed function. Fix this in two layers: 1. At function entry, bail out immediately for simulated kprobes (ainsn.xol_insn == NULL), since they have no XOL slot and any fault taken during their execution cannot be a single-step fault. 2. For kprobes with an XOL slot, only handle the fault when the faulting PC matches the XOL instruction address. Faults from any other PC are left to the normal page fault handler. This follows the same principle as the x86 fix in commit 6381c24cd6d5 ("kprobes/x86: Fix page-fault handling logic"). Signed-off-by: Pu Hu Signed-off-by: Hongyan Xia Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Will Deacon --- arch/arm64/kernel/probes/kprobes.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c index 43a0361a8bf0..798e4b091d1a 100644 --- a/arch/arm64/kernel/probes/kprobes.c +++ b/arch/arm64/kernel/probes/kprobes.c @@ -282,9 +282,31 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr) struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + /* + * Simulated kprobes execute in the debug trap context and have no + * XOL slot. Any page fault taken while a simulated kprobe is in + * progress cannot have been caused by kprobe single-stepping and + * must be left alone for the normal page fault handler, including + * fixup_exception. + */ + if (cur && !cur->ainsn.xol_insn) + return 0; + switch (kcb->kprobe_status) { case KPROBE_HIT_SS: case KPROBE_REENTER: + /* + * A page fault taken while in KPROBE_HIT_SS or + * KPROBE_REENTER state is only attributable to kprobe + * single-stepping if the faulting PC points to the + * current kprobe's XOL instruction. If the fault occurred + * elsewhere (e.g. in perf or tracing code invoked from the + * debug exception path), leave it for the normal page fault + * handler to process. + */ + if (instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn) + break; + /* * We are here because the instruction being single * stepped caused a page fault. We reset the current -- cgit v1.2.3 From 23f851ac0078a908bf3422d6467ebc1db5828c46 Mon Sep 17 00:00:00 2001 From: Pu Hu Date: Fri, 10 Jul 2026 06:32:55 +0000 Subject: arm64: kprobes: Allow reentering kprobes while single-stepping A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This can happen when tracing or perf code runs from the debug exception path while the first kprobe is preparing or executing its out-of-line single-step instruction. Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable, the same as a hit in KPROBE_REENTER. This is too strict. A hit in KPROBE_HIT_SS is still a one-level reentry and can be handled by saving the current kprobe state and setting up single-step for the new probe, just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE. The truly unrecoverable case is hitting another kprobe while already in KPROBE_REENTER, because the reentry save area has already been consumed. Move KPROBE_HIT_SS to the recoverable reentry cases and leave KPROBE_REENTER as the unrecoverable nested reentry case. This change also requires saving saved_irqflag in struct prev_kprobe. When a nested kprobe calls kprobes_save_local_irqflag(), it overwrites kcb->saved_irqflag with the currently masked DAIF value, losing the outer kprobe's original DAIF state. Without this fix, when the outer kprobe's single-step finishes, kprobes_restore_local_irqflag() applies the wrong DAIF mask and leaves interrupts permanently disabled. Extend struct prev_kprobe with a saved_irqflag field and save/restore it alongside kp and status. This ensures the outer kprobe's original interrupt state is preserved across reentry. This mirrors the x86 fix in commit 6a5022a56ac3 ("kprobes/x86: Allow to handle reentered kprobe on single-stepping"). Signed-off-by: Pu Hu Signed-off-by: Hongyan Xia Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Will Deacon --- arch/arm64/include/asm/kprobes.h | 6 ++++++ arch/arm64/kernel/probes/kprobes.c | 23 ++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h index f2782560647b..35ce2c94040e 100644 --- a/arch/arm64/include/asm/kprobes.h +++ b/arch/arm64/include/asm/kprobes.h @@ -26,6 +26,12 @@ struct prev_kprobe { struct kprobe *kp; unsigned int status; + + /* + * The original DAIF state of the outer kprobe, saved here before + * a nested kprobe overwrites kcb->saved_irqflag during reentry. + */ + unsigned long saved_irqflag; }; /* per-cpu kprobe control block */ diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c index 798e4b091d1a..4e0efad5caf2 100644 --- a/arch/arm64/kernel/probes/kprobes.c +++ b/arch/arm64/kernel/probes/kprobes.c @@ -174,12 +174,27 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) { kcb->prev_kprobe.kp = kprobe_running(); kcb->prev_kprobe.status = kcb->kprobe_status; + + /* + * Save the outer kprobe's original DAIF flags before the nested + * kprobe calls kprobes_save_local_irqflag() and overwrites + * kcb->saved_irqflag. Without this, the outer kprobe will restore + * the wrong DAIF state and leave interrupts permanently masked. + */ + kcb->prev_kprobe.saved_irqflag = kcb->saved_irqflag; } static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) { __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); kcb->kprobe_status = kcb->prev_kprobe.status; + + /* + * Restore the outer kprobe's saved_irqflag so that when its + * single-step completes, kprobes_restore_local_irqflag() uses + * the correct original DAIF value. + */ + kcb->saved_irqflag = kcb->prev_kprobe.saved_irqflag; } static void __kprobes set_current_kprobe(struct kprobe *p) @@ -240,10 +255,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p, switch (kcb->kprobe_status) { case KPROBE_HIT_SSDONE: case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SS: + /* + * A probe can be hit while another kprobe is preparing or + * executing its XOL single-step instruction. This is still a + * recoverable one-level reentry, so handle it in the same way as + * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE. + */ kprobes_inc_nmissed_count(p); setup_singlestep(p, regs, kcb, 1); break; - case KPROBE_HIT_SS: case KPROBE_REENTER: pr_warn("Failed to recover from reentered kprobes.\n"); dump_kprobe(p); -- cgit v1.2.3 From 29c57db1629e7a3cddfe1a4b13e72682acfef38e Mon Sep 17 00:00:00 2001 From: Roman Li Date: Wed, 13 May 2026 21:49:15 -0400 Subject: drm/amdgpu/discovery: Fix device family for DCN42 GC 11.7.0 and 11.7.1 should map to AMDGPU_FAMILY_GC_11_5_4 for DCN42. Fixes: cf591e67c095 ("drm/amdgpu: add support for GC IP version 11.7.0") Fixes: a928d8d81ec5 ("drm/amdgpu: add support for GC IP version 11.7.1") Signed-off-by: Roman Li Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit f8ee6447e7ec1d75d6663c817e45566dd01f440b) --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 7b9bb998906d..2860f12915c0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -3137,9 +3137,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + adev->family = AMDGPU_FAMILY_GC_11_5_0; + break; case IP_VERSION(11, 7, 0): case IP_VERSION(11, 7, 1): - adev->family = AMDGPU_FAMILY_GC_11_5_0; + adev->family = AMDGPU_FAMILY_GC_11_5_4; break; case IP_VERSION(12, 0, 0): case IP_VERSION(12, 0, 1): -- cgit v1.2.3 From fbbbd98f200f11e7f9b66ca7f2d18546be8b254e Mon Sep 17 00:00:00 2001 From: Matthew Stewart Date: Thu, 4 Jun 2026 11:36:09 -0400 Subject: drm/amd/display: Fix DCN42B null registers & register masks [why] DCN42B is missing some register masks, which are causing errors in dmesg. [how] Make DCN42B reuse the DCN42 register lists, and add the missing defines manually. Fixes: 64142f9d51af ("drm/amd/display: Fix DCN42 null registers & register masks") Reviewed-by: Ovidiu (Ovi) Bunea Signed-off-by: Matthew Stewart Signed-off-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit b7d69145907cdefcbd39a70a31eefd30919af9f1) --- .../display/dc/resource/dcn42b/dcn42b_resource.c | 20 +++- .../display/dc/resource/dcn42b/dcn42b_resource.h | 116 +-------------------- 2 files changed, 20 insertions(+), 116 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index 527d17f29f3b..1a3b9e942caa 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -22,6 +22,7 @@ #include "dcn35/dcn35_resource.h" #include "dcn321/dcn321_resource.h" #include "dcn401/dcn401_resource.h" +#include "dcn42/dcn42_resource.h" #include "dcn42/dcn42_resource_fpu.h" #include "dcn10/dcn10_ipp.h" @@ -116,6 +117,23 @@ #define regAPG9_APG_DBG_GEN_CONTROL 0x38ae #define regAPG9_APG_DBG_GEN_CONTROL_BASE_IDX 2 +#define regHUBP0_HUBPREQ_DEBUG_DB 0x05f8 +#define regHUBP0_HUBPREQ_DEBUG_DB_BASE_IDX 2 +#define regHUBP0_HUBPREQ_DEBUG 0x05f9 +#define regHUBP0_HUBPREQ_DEBUG_BASE_IDX 2 +#define regHUBP1_HUBPREQ_DEBUG_DB 0x06d4 +#define regHUBP1_HUBPREQ_DEBUG_DB_BASE_IDX 2 +#define regHUBP1_HUBPREQ_DEBUG 0x06d5 +#define regHUBP1_HUBPREQ_DEBUG_BASE_IDX 2 +#define regHUBP2_HUBPREQ_DEBUG_DB 0x07b0 +#define regHUBP2_HUBPREQ_DEBUG_DB_BASE_IDX 2 +#define regHUBP2_HUBPREQ_DEBUG 0x07b1 +#define regHUBP2_HUBPREQ_DEBUG_BASE_IDX 2 +#define regHUBP3_HUBPREQ_DEBUG_DB 0x088c +#define regHUBP3_HUBPREQ_DEBUG_DB_BASE_IDX 2 +#define regHUBP3_HUBPREQ_DEBUG 0x088d +#define regHUBP3_HUBPREQ_DEBUG_BASE_IDX 2 + enum dcn401_clk_src_array_id { DCN401_CLK_SRC_PLL0, DCN401_CLK_SRC_PLL1, @@ -461,7 +479,7 @@ static const struct dcn_optc_mask optc_mask = { OPTC_COMMON_MASK_SH_LIST_DCN42B(_MASK)}; #define hubp_regs_init(id) \ - HUBP_REG_LIST_DCN42B_RI(id) + HUBP_REG_LIST_DCN42_RI(id) static struct dcn_hubp2_registers hubp_regs[4]; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h index 2da3e3c8304a..2824a0e1acc9 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.h @@ -344,7 +344,6 @@ * DCCG_SRII(PHASE, DP_DTO, 3), * DCCG_SRII(MODULO, DP_DTO, 3), * SR(DSCCLK3_DTO_PARAM), - * SR(HDMISTREAMCLK_CNTL), * SR(SYMCLKD_CLOCK_ENABLE), * SR(SYMCLKE_CLOCK_ENABLE) */ @@ -360,6 +359,7 @@ SR(PHYBSYMCLK_CLOCK_CNTL), \ SR(PHYCSYMCLK_CLOCK_CNTL), \ SR(DPSTREAMCLK_CNTL), \ + SR(HDMISTREAMCLK_CNTL), \ SR(SYMCLK32_SE_CNTL), \ SR(SYMCLK32_LE_CNTL), \ DCCG_SRII(PIXEL_RATE_CNTL, OTG, 0), \ @@ -542,120 +542,6 @@ SRI_ARR(DC_ABM1_ACE_OFFSET_SLOPE_DATA, ABM, id), \ SRI_ARR(DC_ABM1_ACE_PWL_CNTL, ABM, id) -/* HUBP */ -/* Not in DCN42B: HUBPREQ_DEBUG_DB and HUBPREQ_DEBUG */ -#define HUBP_REG_LIST_DCN42B_RI(id) \ - SRI_ARR(DCN_DMDATA_VM_CNTL, HUBPREQ, id), \ - SRI_ARR(FLIP_PARAMETERS_3, HUBPREQ, id), \ - SRI_ARR(FLIP_PARAMETERS_4, HUBPREQ, id), \ - SRI_ARR(FLIP_PARAMETERS_5, HUBPREQ, id), \ - SRI_ARR(FLIP_PARAMETERS_6, HUBPREQ, id), \ - SRI_ARR(VBLANK_PARAMETERS_5, HUBPREQ, id), \ - SRI_ARR(VBLANK_PARAMETERS_6, HUBPREQ, id), \ - HUBP_REG_LIST_DCN_VM_RI(id), \ - SRI_ARR(PREFETCH_SETTINGS, HUBPREQ, id), \ - SRI_ARR(PREFETCH_SETTINGS_C, HUBPREQ, id), \ - SRI_ARR(DCN_VM_SYSTEM_APERTURE_LOW_ADDR, HUBPREQ, id), \ - SRI_ARR(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR, HUBPREQ, id), \ - SRI_ARR(CURSOR_SETTINGS, HUBPREQ, id), \ - SRI_ARR(CURSOR_SURFACE_ADDRESS_HIGH, CURSOR0_, id), \ - SRI_ARR(CURSOR_SURFACE_ADDRESS, CURSOR0_, id), \ - SRI_ARR(CURSOR_SIZE, CURSOR0_, id), \ - SRI_ARR(CURSOR_CONTROL, CURSOR0_, id), \ - SRI_ARR(CURSOR_POSITION, CURSOR0_, id), \ - SRI_ARR(CURSOR_HOT_SPOT, CURSOR0_, id), \ - SRI_ARR(CURSOR_DST_OFFSET, CURSOR0_, id), \ - SRI_ARR(DMDATA_ADDRESS_HIGH, CURSOR0_, id), \ - SRI_ARR(DMDATA_ADDRESS_LOW, CURSOR0_, id), \ - SRI_ARR(DMDATA_CNTL, CURSOR0_, id), \ - SRI_ARR(DMDATA_SW_CNTL, CURSOR0_, id), \ - SRI_ARR(DMDATA_QOS_CNTL, CURSOR0_, id), \ - SRI_ARR(DMDATA_SW_DATA, CURSOR0_, id), \ - SRI_ARR(DMDATA_STATUS, CURSOR0_, id), \ - SRI_ARR(FLIP_PARAMETERS_0, HUBPREQ, id), \ - SRI_ARR(FLIP_PARAMETERS_1, HUBPREQ, id), \ - SRI_ARR(FLIP_PARAMETERS_2, HUBPREQ, id), \ - SRI_ARR(DCN_CUR1_TTU_CNTL0, HUBPREQ, id), \ - SRI_ARR(DCN_CUR1_TTU_CNTL1, HUBPREQ, id), \ - SRI_ARR(DCSURF_FLIP_CONTROL2, HUBPREQ, id), \ - SRI_ARR(VMID_SETTINGS_0, HUBPREQ, id), \ - SRI_ARR(DCHUBP_CNTL, HUBP, id), \ - SRI_ARR(DCSURF_ADDR_CONFIG, HUBP, id), \ - SRI_ARR(DCSURF_TILING_CONFIG, HUBP, id), \ - SRI_ARR(DCSURF_SURFACE_PITCH, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_PITCH_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_CONFIG, HUBP, id), \ - SRI_ARR(DCSURF_FLIP_CONTROL, HUBPREQ, id), \ - SRI_ARR(DCSURF_PRI_VIEWPORT_DIMENSION, HUBP, id), \ - SRI_ARR(DCSURF_PRI_VIEWPORT_START, HUBP, id), \ - SRI_ARR(DCSURF_SEC_VIEWPORT_DIMENSION, HUBP, id), \ - SRI_ARR(DCSURF_SEC_VIEWPORT_START, HUBP, id), \ - SRI_ARR(DCSURF_PRI_VIEWPORT_DIMENSION_C, HUBP, id), \ - SRI_ARR(DCSURF_PRI_VIEWPORT_START_C, HUBP, id), \ - SRI_ARR(DCSURF_SEC_VIEWPORT_DIMENSION_C, HUBP, id), \ - SRI_ARR(DCSURF_SEC_VIEWPORT_START_C, HUBP, id), \ - SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \ - SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS, HUBPREQ, id), \ - SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \ - SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS, HUBPREQ, id), \ - SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \ - SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS, HUBPREQ, id), \ - SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \ - SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS, HUBPREQ, id), \ - SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_INUSE, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_INUSE_HIGH, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_INUSE_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_INUSE_HIGH_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_CONTROL, HUBPREQ, id), \ - SRI_ARR(DCSURF_SURFACE_FLIP_INTERRUPT, HUBPREQ, id), \ - SRI_ARR(HUBPRET_CONTROL, HUBPRET, id), \ - SRI_ARR(HUBPRET_READ_LINE_STATUS, HUBPRET, id), \ - SRI_ARR(DCN_EXPANSION_MODE, HUBPREQ, id), \ - SRI_ARR(DCHUBP_REQ_SIZE_CONFIG, HUBP, id), \ - SRI_ARR(DCHUBP_REQ_SIZE_CONFIG_C, HUBP, id), \ - SRI_ARR(BLANK_OFFSET_0, HUBPREQ, id), \ - SRI_ARR(BLANK_OFFSET_1, HUBPREQ, id), \ - SRI_ARR(DST_DIMENSIONS, HUBPREQ, id), \ - SRI_ARR(DST_AFTER_SCALER, HUBPREQ, id), \ - SRI_ARR(VBLANK_PARAMETERS_0, HUBPREQ, id), \ - SRI_ARR(REF_FREQ_TO_PIX_FREQ, HUBPREQ, id), \ - SRI_ARR(VBLANK_PARAMETERS_1, HUBPREQ, id), \ - SRI_ARR(VBLANK_PARAMETERS_3, HUBPREQ, id), \ - SRI_ARR(NOM_PARAMETERS_4, HUBPREQ, id), \ - SRI_ARR(NOM_PARAMETERS_5, HUBPREQ, id), \ - SRI_ARR(PER_LINE_DELIVERY_PRE, HUBPREQ, id), \ - SRI_ARR(PER_LINE_DELIVERY, HUBPREQ, id), \ - SRI_ARR(VBLANK_PARAMETERS_2, HUBPREQ, id), \ - SRI_ARR(VBLANK_PARAMETERS_4, HUBPREQ, id), \ - SRI_ARR(NOM_PARAMETERS_6, HUBPREQ, id), \ - SRI_ARR(NOM_PARAMETERS_7, HUBPREQ, id), \ - SRI_ARR(DCN_TTU_QOS_WM, HUBPREQ, id), \ - SRI_ARR(DCN_GLOBAL_TTU_CNTL, HUBPREQ, id), \ - SRI_ARR(DCN_SURF0_TTU_CNTL0, HUBPREQ, id), \ - SRI_ARR(DCN_SURF0_TTU_CNTL1, HUBPREQ, id), \ - SRI_ARR(DCN_SURF1_TTU_CNTL0, HUBPREQ, id), \ - SRI_ARR(DCN_SURF1_TTU_CNTL1, HUBPREQ, id), \ - SRI_ARR(DCN_CUR0_TTU_CNTL0, HUBPREQ, id), \ - SRI_ARR(DCN_CUR0_TTU_CNTL1, HUBPREQ, id), \ - SRI_ARR(HUBP_CLK_CNTL, HUBP, id), \ - SRI_ARR(HUBPRET_READ_LINE_VALUE, HUBPRET, id), \ - SRI_ARR(DCHUBP_MALL_CONFIG, HUBP, id), \ - SRI_ARR(DCHUBP_VMPG_CONFIG, HUBP, id), \ - SRI_ARR(UCLK_PSTATE_FORCE, HUBPREQ, id), \ - SRI_ARR(HUBP_3DLUT_DLG_PARAM, CURSOR0_, id), \ - HUBP_3DLUT_FL_REG_LIST_DCN401(id) struct dcn42b_resource_pool { struct resource_pool base; }; -- cgit v1.2.3 From 2b0386d4293920e690c0e017708f999b93cc729b Mon Sep 17 00:00:00 2001 From: Yongqiang Sun Date: Mon, 6 Jul 2026 15:15:07 -0400 Subject: drm/amdkfd: fix 32-bit overflow in CWSR total size calculation total_cwsr_size was computed in 32-bit before being used as a BO/SVM allocation size. With large ctx_save_restore_area_size and debug_memory_size multiplied by the XCC count, the product can wrap, yielding an undersized CWSR save area that firmware later overruns. Promote total_cwsr_size to u64 and use check_add_overflow()/ check_mul_overflow() in both kfd_queue_acquire_buffers() and kfd_queue_release_buffers(). Signed-off-by: Yongqiang Sun Reviewed-by: Philip Yang Signed-off-by: Alex Deucher (cherry picked from commit 319f7e13423ae3f486b9aea82f9ad2d6af0ee608) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index 28354a4e5dd5..98a5512b701b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -23,6 +23,7 @@ */ #include +#include #include "kfd_priv.h" #include "kfd_topology.h" #include "kfd_svm.h" @@ -235,7 +236,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope struct kfd_topology_device *topo_dev; u64 expected_queue_size; struct amdgpu_vm *vm; - u32 total_cwsr_size; + u64 total_cwsr_size; int err; topo_dev = kfd_topology_device_by_id(pdd->dev->id); @@ -308,8 +309,14 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope goto out_err_unreserve; } - total_cwsr_size = (properties->ctx_save_restore_area_size + - topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask); + total_cwsr_size = (u64)properties->ctx_save_restore_area_size + + topo_dev->node_props.debug_memory_size; + if (check_mul_overflow(total_cwsr_size, + NUM_XCC(pdd->dev->xcc_mask), + &total_cwsr_size)) { + err = -EINVAL; + goto out_err_unreserve; + } total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE); err = kfd_queue_buffer_get(vm, (void *)properties->ctx_save_restore_area_address, @@ -344,7 +351,7 @@ out_err_release: int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_properties *properties) { struct kfd_topology_device *topo_dev; - u32 total_cwsr_size; + u64 total_cwsr_size; kfd_queue_buffer_put(&properties->wptr_bo); kfd_queue_buffer_put(&properties->rptr_bo); @@ -355,8 +362,12 @@ int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_prope topo_dev = kfd_topology_device_by_id(pdd->dev->id); if (!topo_dev) return -EINVAL; - total_cwsr_size = (properties->ctx_save_restore_area_size + - topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask); + total_cwsr_size = (u64)properties->ctx_save_restore_area_size + + topo_dev->node_props.debug_memory_size; + if (check_mul_overflow(total_cwsr_size, + NUM_XCC(pdd->dev->xcc_mask), + &total_cwsr_size)) + return -EINVAL; total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE); kfd_queue_buffer_svm_put(pdd, properties->ctx_save_restore_area_address, total_cwsr_size); -- cgit v1.2.3 From 9fb646bc4d87f62bcbf0a7ea326430eb802c475c Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Fri, 29 May 2026 11:09:08 +0200 Subject: drm/amd/display: Fix preferred link rate for NUTMEG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When there is a preferred link rate setting, it needs to be applied to both the current and initial link rate. This was regressed by a "coding style" fix, which caused the current link rate to not respect the preferred value. This commit restores the functionality of NUTMEG, the DP bridge encoder found on old APUs such as Kaveri. Fixes: a62346043a89 ("drm/amd/display: Fix coding style issue") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5465 Cc: Chuanyu Tseng Reviewed-by: Fangzhi Zuo Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit e78b0a367f8690b682029d90e75308dc84ed51de) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c index 47abb4066709..4079f128bade 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c @@ -750,8 +750,10 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap)) return false; - if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) + if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) { initial_link_setting.link_rate = link->preferred_link_setting.link_rate; + current_link_setting.link_rate = link->preferred_link_setting.link_rate; + } /* search for the minimum link setting that: * 1. is supported according to the link training result -- cgit v1.2.3 From e39b7cf5c62e027af166772e46382356ecb45c36 Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Fri, 29 May 2026 11:09:09 +0200 Subject: drm/amd/display: Add dp_skip_rbr flag for NUTMEG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional changes. Just clean up a conceptual mismatch. Based on feedback on the NUTMEG code in DC, the preferred_link_setting is meant to force the DP link to a specific setting, meaning both the link rate and lane count should be locked to an exact value. What NUTMEG needs is a lower bound on the link rate, which is not the same concept. Implement this as a HW workaround flag instead. Suggested-by: Wenjing Liu Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 871ceb853841bcaa4e6cec3723b16c4887a760be) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dc.h | 2 ++ drivers/gpu/drm/amd/display/dc/link/link_detection.c | 2 +- drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 82d02ebbd829..ebdcbfa7db80 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1815,6 +1815,8 @@ struct dc_scratch_space { bool dp_skip_DID2; bool dp_skip_reset_segment; bool dp_skip_fs_144hz; + /* Some DP bridges don't work with RBR and must use HBR. */ + bool dp_skip_rbr; bool dp_mot_reset_segment; /* Some USB4 docks do not handle turning off MST DSC once it has been enabled. */ bool dpia_mst_dsc_always_on; diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index 7d8951fecd57..29dbb5e410d8 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -623,7 +623,7 @@ static bool detect_dp(struct dc_link *link, link->dpcd_caps.sink_count.bits.SINK_COUNT = 1; /* NUTMEG requires that we use HBR, doesn't work with RBR. */ if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_00001A) - link->preferred_link_setting.link_rate = LINK_RATE_HIGH; + link->wa_flags.dp_skip_rbr = true; } return true; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c index 4079f128bade..1cd17a0272bc 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c @@ -750,9 +750,9 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap)) return false; - if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) { - initial_link_setting.link_rate = link->preferred_link_setting.link_rate; - current_link_setting.link_rate = link->preferred_link_setting.link_rate; + if (link->wa_flags.dp_skip_rbr) { + initial_link_setting.link_rate = LINK_RATE_HIGH; + current_link_setting.link_rate = LINK_RATE_HIGH; } /* search for the minimum link setting that: -- cgit v1.2.3 From 9b3aa1dec7c364b0d7b171dcc00ab1092e362aef Mon Sep 17 00:00:00 2001 From: Fangzhi Zuo Date: Wed, 24 Jun 2026 16:54:30 -0400 Subject: drm/amd/display: Fix 8K Mode Not Parsed by EDID [why] The 8K120/8K240 timings live in DisplayID extension blocks 2 and 3 of this EDID. The EDID is a 4-block (512-byte) HDMI 2.1 EDID that uses HF-EEODB. drm core reads and parses this correctly, but amdgpu rebuilds its own copy. Only 2 of 4 blocks were copied into sink->dc_edid, that leads to drm_edid_connector_add_modes() never sees blocks 2 and 3. [how] Directly populate edid_blob_ptr with a blob whose length is the full, and HF-EEODB-aware size. Reviewed-by: Sun peng (Leo) Li Signed-off-by: Fangzhi Zuo Signed-off-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 11a90eaf5c808ba800249dda0d481c35d0888589) --- .../drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index c6f94eb71ffa..6be7f6edd0b2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -1201,11 +1201,25 @@ enum dc_edid_status dm_helpers_read_local_edid( continue; edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw() - if (!edid || - edid->extensions >= sizeof(sink->dc_edid.raw_edid) / EDID_LENGTH) + /* + * Use the length of the EDID property blob populated by + * drm_edid_connector_update() above. It reflects the true number + * of EDID blocks, including any HDMI Forum EDID Extension Override + * Data Block (HF-EEODB) count, which the raw byte 0x7e extension + * count can hide (e.g. HDMI 8K sinks). + */ + if (!edid || !connector->edid_blob_ptr || + connector->edid_blob_ptr->length > sizeof(sink->dc_edid.raw_edid)) return EDID_BAD_INPUT; - sink->dc_edid.length = EDID_LENGTH * (edid->extensions + 1); + /* + * FIXME: amdgpu_dm today does not consider the HF-EEODB, which + * may contain additional mode info for sinks. This is a + * workaround until dc_edid is refactored out from DC into + * amdgpu_dm's ownership, allowing amdgpu_dm to use drm_edid + * directly + */ + sink->dc_edid.length = connector->edid_blob_ptr->length; memmove(sink->dc_edid.raw_edid, (uint8_t *)edid, sink->dc_edid.length); /* We don't need the original edid anymore */ -- cgit v1.2.3 From f1b5d8f9cc54ae8a2567ac126867ae488e1bf625 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 29 Jun 2026 15:27:00 -0500 Subject: drm/amd/display: Fix backlight max_brightness to match exported range [Why] FWTS autobrightness fails on eDP panels because actual_brightness can read higher than the advertised max_brightness (e.g. 63576 vs 62451). The conversion helpers expose the firmware PWM range to userspace as [0..max]. But max_brightness is advertised as (max - min), which is smaller. So reading the level can return a value above max_brightness. This regressed in commit 4b61b8a39051 ("drm/amd/display: Add debugging message for brightness caps"), which changed max_brightness to (max - min) and undid commit 8dbd72cb7900 ("drm/amd/display: Export full brightness range to userspace"). [How] Advertise max_brightness as max, and scale the initial AC/DC brightness against max too. Update the KUnit expectations to match. Fixes: 4b61b8a39051 ("drm/amd/display: Add debugging message for brightness caps") Reviewed-by: Alex Hung Signed-off-by: Mario Limonciello Signed-off-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit bd9e2b5b0473c75abc0f4134dfe79ecbfb16610d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 18145d78334f..ff337de44f7a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -5563,11 +5563,11 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector) caps = &dm->backlight_caps[aconnector->bl_idx]; if (get_brightness_range(caps, &min, &max)) { if (power_supply_is_system_supplied() > 0) - props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100); + props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100); else - props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100); + props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100); /* min is zero, so max needs to be adjusted */ - props.max_brightness = max - min; + props.max_brightness = max; drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max, caps->ac_level, caps->dc_level); } else -- cgit v1.2.3 From 5b1250efc17058595d2776f3c1e53e258f0968ae Mon Sep 17 00:00:00 2001 From: Dmytro Laktyushkin Date: Fri, 3 Jul 2026 10:08:37 -0400 Subject: drm/amd/display: fix dcn42 det allocation order set_pipe_unlock_order needs to be set to true for the pipes to be unlocked in correct order to avoid det overallocation Reviewed-by: Taimur Hassan Signed-off-by: Dmytro Laktyushkin Signed-off-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 198663d035cc439eb48844a2da66f6ae1b0de303) --- drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index 7de12b16d7ad..a8241036def2 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -2142,6 +2142,7 @@ static bool dcn42_resource_construct( dc->config.use_pipe_ctx_sync_logic = true; dc->config.dc_mode_clk_limit_support = false; dc->config.enable_windowed_mpo_odm = true; + dc->config.set_pipe_unlock_order = true; /* Need to ensure DET gets freed before allocating */ /* Use psp mailbox to enable assr */ dc->config.use_assr_psp_message = true; /* dcn42 and afterward always support external panel replay */ -- cgit v1.2.3 From f9ad396aa803c34513ac4c93bce42435dfa96b68 Mon Sep 17 00:00:00 2001 From: Dmytro Laktyushkin Date: Fri, 3 Jul 2026 13:01:12 -0400 Subject: drm/amd/display: fix dcn42b det allocation order set_pipe_unlock_order needs to be set to true for the pipes to be unlocked in correct order to avoid det overallocation Reviewed-by: Charlene Liu Signed-off-by: Dmytro Laktyushkin Signed-off-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 183bbded999a70c5996e8f399fa8790568d71112) --- drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index 1a3b9e942caa..e9c33e3466ea 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -2105,6 +2105,7 @@ static bool dcn42b_resource_construct( dc->config.use_pipe_ctx_sync_logic = true; dc->config.dc_mode_clk_limit_support = false; dc->config.enable_windowed_mpo_odm = true; + dc->config.set_pipe_unlock_order = true; /* Need to ensure DET gets freed before allocating */ /* Use psp mailbox to enable assr */ dc->config.use_assr_psp_message = true; /* dcn42 and afterward always support external panel replay */ -- cgit v1.2.3 From 5d75ec2e5f1736c2f10c7d6f4565bf1bf29f29a7 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Fri, 10 Jul 2026 16:45:42 +0800 Subject: drm/amdgpu/userq: fix indefinite fence wait during GPU reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pre_reset only force-completes fences of MAPPED queues. A queue in any other state (e.g. mid-eviction) keeps its last_fence pending; after a GPU reset that fence never signals, so the eviction/suspend worker and process teardown (amdgpu_evf_mgr_flush_suspend) wait on it forever and wedge the machine: INFO: task kworker/6:28 blocked for more than 120 seconds. Workqueue: events amdgpu_eviction_fence_suspend_worker [amdgpu] Call Trace: dma_fence_wait_timeout+0x7e/0x130 amdgpu_userq_evict+0x67/0x140 [amdgpu] amdgpu_eviction_fence_suspend_worker+0xd8/0x160 [amdgpu] process_scheduled_works+0xa6/0x420 Force-complete every queue's fence regardless of state. The unmap and mark-hung step stays gated on MAPPED, since unmapping a queue that is not mapped is invalid. Fixes: 290f46cf5726 ("drm/amdgpu: Implement user queue reset functionality") Reviewed-by: Christian König Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit 9102b39fa924dcc3dc75a3137bfa9633c40b88c0) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index d854343b3734..572f2949cb64 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1376,16 +1376,19 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev) /* TODO: We probably need a new lock for the queue state */ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) { - if (queue->state != AMDGPU_USERQ_STATE_MAPPED) - continue; - - userq_funcs = adev->userq_funcs[queue->queue_type]; - userq_funcs->unmap(queue); - /* just mark all queues as hung at this point. - * if unmap succeeds, we could map again - * in amdgpu_userq_post_reset() if vram is not lost + if (queue->state == AMDGPU_USERQ_STATE_MAPPED) { + userq_funcs = adev->userq_funcs[queue->queue_type]; + userq_funcs->unmap(queue); + /* just mark all queues as hung at this point. + * if unmap succeeds, we could map again + * in amdgpu_userq_post_reset() if vram is not lost + */ + queue->state = AMDGPU_USERQ_STATE_HUNG; + } + /* Force-complete any pending fence regardless of queue state so + * that eviction/suspend and queue teardown waiters don't block + * forever on a fence that will never signal after the reset. */ - queue->state = AMDGPU_USERQ_STATE_HUNG; amdgpu_userq_fence_driver_force_completion(queue); } } -- cgit v1.2.3 From a2f895f3c852063258d62e9f74b081de07ca95df Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 1 Jul 2026 18:53:21 +0800 Subject: drm/amdgpu: fix bo->pin leaking in amdgpu_bo_create_reserved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_bo_create_reserved() only allocates a new BO when *bo_ptr (struct amdgpu_bo **bo_ptr as input parameter) is NULL, it simply skips creation when *bo_ptr is non-NULL. But it unconditionally reserves, pins, gart allocates and maps the BO afterwards. When the same non-NULL BO pointer is passed in again, for example firmware buffers that live in adev and are re-loaded on every resume / cp_resume / start under AMDGPU_FW_LOAD_DIRECT, amdgpu_bo_pin() just increases pin_count unconditionally, however the matching teardown only unpins once, so pin_count never drops to zero, so TTM is not able to move, swap or evict a BO, causing BO leaks. This commit fixes this issue by only pinning the bo once at creation, and repeated calls no longer take additional pin references. Signed-off-by: Zhu Lingshan Reviewed-by: Alex Deucher Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 3ddc0ae76202c447b6aec61e907b852bc94671cf) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index f98bfba59a2c..718937777e53 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -276,10 +276,12 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev, goto error_free; } - r = amdgpu_bo_pin(*bo_ptr, domain); - if (r) { - dev_err(adev->dev, "(%d) kernel bo pin failed\n", r); - goto error_unreserve; + if (free) { + r = amdgpu_bo_pin(*bo_ptr, domain); + if (r) { + dev_err(adev->dev, "(%d) kernel bo pin failed\n", r); + goto error_unreserve; + } } r = amdgpu_ttm_alloc_gart(&(*bo_ptr)->tbo); @@ -302,7 +304,8 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev, return 0; error_unpin: - amdgpu_bo_unpin(*bo_ptr); + if (free) + amdgpu_bo_unpin(*bo_ptr); error_unreserve: amdgpu_bo_unreserve(*bo_ptr); -- cgit v1.2.3 From db7e8108809a2245f0a17ba323f027cac0941ffb Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 8 Jul 2026 14:35:14 -0500 Subject: drm/amdgpu: Fix VFCT bus number matching with soft filter On systems where PCI bus renumbering occurs (e.g. pci=realloc, resource conflicts), the runtime bus number may differ from the BIOS POST bus number recorded in the VFCT table. This causes amdgpu_acpi_vfct_bios() to fail finding the VBIOS even though the correct device entry exists. Introduce amdgpu_acpi_vfct_match() which treats the bus number as a soft filter: vendor/device/function identity is the hard requirement, while exact bus match is the preferred path. When bus numbers disagree but device identity matches, accept the VFCT entry and log a dev_notice for diagnostics. Reported-by: Oz Tiram Closes: https://lore.kernel.org/amd-gfx/20260621173211.28443-1-oz@shift-computing.de/ Reviewed-by: Alex Deucher Link: https://patch.msgid.link/20260708193518.702584-2-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 11c141672045ffc0187aa604f2c0f597bc334fb2) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 45 ++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index aa039e148a5e..fbe350a45871 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -372,6 +372,45 @@ static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) } #ifdef CONFIG_ACPI +/** + * amdgpu_acpi_vfct_match() - Check if a VFCT entry matches the device + * @adev: AMDGPU device + * @vhdr: VFCT image header to check + * + * VFCT entries contain the PCI bus number as recorded during BIOS POST. + * On systems where the kernel renumbers PCI buses (e.g. pci=realloc or + * resource conflicts), the runtime bus number may differ from the POST + * value. Match by device identity (vendor + device + function) and use + * the bus number as a preference: exact bus match is preferred, but when + * the bus numbers disagree we accept the entry if the device identity + * matches. + * + * Returns: 0 on match, -ENODEV on no match + */ +static int amdgpu_acpi_vfct_match(struct amdgpu_device *adev, + VFCT_IMAGE_HEADER *vhdr) +{ + /* Vendor and device IDs must always match */ + if (vhdr->VendorID != adev->pdev->vendor || + vhdr->DeviceID != adev->pdev->device) + return -ENODEV; + + if (vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) || + vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn)) + return -ENODEV; + + /* Exact bus number match - preferred */ + if (vhdr->PCIBus == adev->pdev->bus->number) + return 0; + + /* Bus mismatch but device identity matches (PCI renumbering case) */ + dev_notice(adev->dev, + "VFCT bus number mismatch: table %u != runtime %u, matching by device identity (vendor 0x%04x device 0x%04x)\n", + vhdr->PCIBus, adev->pdev->bus->number, + adev->pdev->vendor, adev->pdev->device); + return 0; +} + static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) { struct acpi_table_header *hdr; @@ -407,11 +446,7 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) } if (vhdr->ImageLength && - vhdr->PCIBus == adev->pdev->bus->number && - vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) && - vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) && - vhdr->VendorID == adev->pdev->vendor && - vhdr->DeviceID == adev->pdev->device) { + !amdgpu_acpi_vfct_match(adev, vhdr)) { adev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); -- cgit v1.2.3 From 65bff26617607c1331283232016c0e89088c5b78 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 8 Jul 2026 14:35:15 -0500 Subject: drm/amdgpu: Release VFCT ACPI table reference amdgpu_acpi_vfct_bios() fetches the VFCT table with acpi_get_table() but never releases it. acpi_get_table() takes a reference on the table (incrementing its validation_count and mapping it on the 0->1 transition); without a paired acpi_put_table() the mapping is leaked on every call, whether or not a matching VBIOS image is found. Route all exit paths after the table is acquired through a common acpi_put_table(). The VBIOS image is copied out with kmemdup() before the table is released, so it remains valid for the caller. Reviewed-by: Alex Deucher Link: https://patch.msgid.link/20260708193518.702584-3-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit ca5988682b4cba4cd125a0fa99b2de1239164ae4) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index fbe350a45871..8525c45ab209 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -417,13 +417,14 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) acpi_size tbl_size; UEFI_ACPI_VFCT *vfct; unsigned int offset; + bool r = false; if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) return false; tbl_size = hdr->length; if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { dev_info(adev->dev, "ACPI VFCT table present but broken (too short #1),skipping\n"); - return false; + goto out; } vfct = (UEFI_ACPI_VFCT *)hdr; @@ -436,13 +437,13 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) offset += sizeof(VFCT_IMAGE_HEADER); if (offset > tbl_size) { dev_info(adev->dev, "ACPI VFCT image header truncated,skipping\n"); - return false; + goto out; } offset += vhdr->ImageLength; if (offset > tbl_size) { dev_info(adev->dev, "ACPI VFCT image truncated,skipping\n"); - return false; + goto out; } if (vhdr->ImageLength && @@ -453,15 +454,19 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) if (!check_atom_bios(adev, vhdr->ImageLength)) { amdgpu_bios_release(adev); - return false; + goto out; } adev->bios_size = vhdr->ImageLength; - return true; + r = true; + goto out; } } dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n"); - return false; + +out: + acpi_put_table(hdr); + return r; } #else static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) -- cgit v1.2.3 From ea9d70db278957e1e81e4aea58b4b131ba262cfb Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Sat, 11 Jul 2026 13:21:07 +0200 Subject: drm/amdgpu: Print vmid, pasid and more task info in devcoredump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are in the dmesg logs but are missing from devcoredumps. Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit fed7aa36d79802c3e02acd05aeae8b0a877e47c2) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 7 ++++++- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index e77db76b48b8..acab3d94a51a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -355,10 +355,14 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf drm_printf(&p, "kernel: %s\n", init_utsname()->release); drm_printf(&p, "module: " KBUILD_MODNAME "\n"); drm_printf(&p, "time: %ptSp\n", &coredump->reset_time); + drm_printf(&p, "pasid: %u\n", coredump->pasid); + drm_printf(&p, "vmid: %u\n", coredump->vmid); if (coredump->reset_task_info.task.pid) - drm_printf(&p, "process_name: %s PID: %d\n", + drm_printf(&p, "process_name: %s TGID: %d thread: %s PID: %d\n", coredump->reset_task_info.process_name, + coredump->reset_task_info.tgid, + coredump->reset_task_info.task.comm, coredump->reset_task_info.task.pid); /* SOC Information */ @@ -562,6 +566,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, amdgpu_vm_put_task_info(ti); } coredump->pasid = job->pasid; + coredump->vmid = job->vmid; coredump->num_ibs = job->num_ibs; for (i = 0; i < job->num_ibs; ++i) { coredump->ibs[i].gpu_addr = job->ibs[i].gpu_addr; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h index 2371e20fc68b..63f27337c09a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h @@ -63,6 +63,7 @@ struct amdgpu_coredump_info { char *formatted; unsigned int pasid; + unsigned int vmid; int num_ibs; struct amdgpu_coredump_ib_info ibs[] __counted_by(num_ibs); }; -- cgit v1.2.3 From b5eab15944b6b140f003af6e639a25f5c3a8ed0b Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Sat, 11 Jul 2026 13:21:08 +0200 Subject: drm/amdgpu: Reserve space for IB contents in devcoredumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the contents of IBs are abruptly cut off and don't show the full contents. This patch makes sure to reserve space for those contents too so they may be printed. Signed-off-by: Timur Kristóf Acked-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 4e2c0821509fed754e8c31d5053d152fbb3484a5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index acab3d94a51a..6480a344006d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -234,6 +234,9 @@ amdgpu_devcoredump_print_ibs(struct drm_printer *p, drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i, coredump->ibs[i].gpu_addr, coredump->ibs[i].ib_size_dw); + + for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++) + drm_printf(p, "0xffffffff\n"); } return; } -- cgit v1.2.3 From 54d4dee9f89e1dac1a6c2618de0bb2e3f35cc2f0 Mon Sep 17 00:00:00 2001 From: yanglinlin Date: Mon, 13 Jul 2026 11:12:28 +0800 Subject: drm/amd/display: fix __udivdi3 link error When compiling the AMDGPU display driver for 32-bit architectures, the linker reports undefined reference to `__udivdi3` in functions get_dp_dto_frequency_100hz() and dcn401_get_dp_dto_frequency_100hz(). This is because the code uses 64-bit division (/) on 32-bit systems, which GCC cannot handle directly and instead tries to call the missing __udivdi3 helper function. Replace the raw division with div_u64(), the kernel's standard 64-bit division helper, to avoid the link error. Signed-off-by: Linlin Yang Reported-by: k2ci Signed-off-by: Alex Deucher (cherry picked from commit 0421fc6ab3a8514e99156ff3c2cee13ee9af3fa7) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 7c293917e6fd..ecb8493ec523 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -1229,9 +1229,9 @@ static bool get_dp_dto_frequency_100hz( */ modulo_hz = REG_READ(MODULO[inst]); if (modulo_hz) { - temp = div_u64((uint64_t)clock_hz * dp_dto_ref_khz * 10, modulo_hz); - ASSERT(temp / 100 <= 0xFFFFFFFFUL); - *pixel_clk_100hz = (unsigned int)(temp / 100); + temp = clock_hz * dp_dto_ref_khz * 10; + ASSERT(temp <= UINT_MAX * modulo_hz * 100ULL); + *pixel_clk_100hz = div_u64(temp, modulo_hz * 100); } else *pixel_clk_100hz = 0; } else { @@ -1285,13 +1285,12 @@ static bool dcn401_get_dp_dto_frequency_100hz(const struct clock_source *clock_s * - target pix_clk_hz = (DPDTO INTEGER * DPDTO MODULO + DPDTO PHASE) */ temp = (unsigned long long)dp_dto_integer * modulo_hz + phase_hz; - - if (temp / 100 > 0xFFFFFFFFUL) { + if (temp > (UINT_MAX * 100ULL)) { /* pixel rate 100hz should never be this high, if it is, throw an assert and return 0 */ BREAK_TO_DEBUGGER(); *pixel_clk_100hz = 0; } else { - *pixel_clk_100hz = (unsigned int)(temp / 100); + *pixel_clk_100hz = div_u64(temp, 100); } return true; -- cgit v1.2.3 From 85371c5ef502d10add72eab38711e191dccea981 Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Mon, 13 Jul 2026 08:14:43 +0200 Subject: drm/amd/pm/ci: Don't disable MCLK DPM on Bonaire 0x6658 (R7 260X) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old radeon driver has a documented workaround in ci_dpm.c which claims that Bonaire 0x6658 with old memory controller firmware is unstable with MCLK DPM, so as a precaution I disabled MCLK DPM on this ASIC in amdgpu. Note that the old MC firmware is not actually used with amdgpu, but in theory it's possible that the VBIOS sets up the ASIC with an old MC firmware that is already running when amdgpu initializes (in which case amdgpu doesn't load its own firmware). What I expected to happen is that the GPU would simply use its maximum memory clock, and indeed this is what seemed to happen according to amdgpu_pm_info which reads the current MCLK value from the SMU. However, some users reported a huge perf regression and upon a closer look it seems that the GPU seems to not actually use the highest MCLK value, despite the SMU reporting that it does. Let's not disable MCLK DPM on Bonaire 0x6658 (R7 260X). Keep MCLK DPM disabled on R9 M380 in the 2015 iMac because that still hangs if we enable it. Fixes: 9851f29cb06c ("drm/amd/pm/ci: Disable MCLK DPM on problematic CI ASICs") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit d34acad064ee7d82bd18f5d87592c422d4d323ac) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c index 1d6e30269d56..4d553be56396 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c @@ -106,11 +106,8 @@ int hwmgr_early_init(struct pp_hwmgr *hwmgr) hwmgr->od_enabled = false; switch (hwmgr->chip_id) { case CHIP_BONAIRE: - /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM - * R7 260X cards with old MC ucode: MCLK DPM is unstable - */ - if (adev->pdev->subsystem_vendor == 0x106B || - adev->pdev->device == 0x6658) { + /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM */ + if (adev->pdev->subsystem_vendor == 0x106B) { dev_info(adev->dev, "disabling MCLK DPM on quirky ASIC"); adev->pm.pp_feature &= ~PP_MCLK_DPM_MASK; hwmgr->feature_mask &= ~PP_MCLK_DPM_MASK; -- cgit v1.2.3 From cea54c52d82dd9948126b7518af51ca1de094933 Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Sat, 11 Jul 2026 13:34:35 +0200 Subject: drm/amd/display: Set native cursor mode for disabled CRTCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always set native cursor mode when the CRTC is disabled, to make sure it doesn't cause atomic commits to fail when they are trying to disable the CRTC. Fixes: 41af6215cdbc ("drm/amd/display: Reject cursor plane on DCE when scaled differently than primary") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5432 Cc: Leo Li Cc: Michel Dänzer Signed-off-by: Timur Kristóf Tested-by: Viktor Jägersküpper Signed-off-by: Alex Deucher (cherry picked from commit 2f79f0130f828cf26fe2dcf45291821616af7b47) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index ff337de44f7a..fe6eea28d119 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -12814,10 +12814,15 @@ static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev, /* Overlay cursor not supported on HW before DCN * DCN401/420 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions * as previous DCN generations, so enable native mode on DCN401/420 + * + * Always set native cursor mode when the CRTC is disabled, + * to make sure it doesn't cause atomic commits to fail when + * they are trying to disable the CRTC. */ if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1) || amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) || - amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1)) { + amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) || + !dm_crtc_state->base.enable) { *cursor_mode = DM_CURSOR_NATIVE_MODE; return 0; } -- cgit v1.2.3 From d340cba0df4cf327c7e89c7c1a4e79d4771d7dd5 Mon Sep 17 00:00:00 2001 From: Andriy Korud Date: Fri, 10 Jul 2026 12:52:26 +0200 Subject: drm/amd/display: dce100: skip non-DP stream encoders for DP MST On DCE8-class ASICs (e.g. Bonaire), the resource pool contains digital DIG stream encoders plus one analog DAC encoder. When assigning a stream encoder for a second DisplayPort MST stream, if the preferred digital encoder is already acquired, dce100_find_first_free_match_stream_enc_for_link() falls back to the first free pool entry. That entry may be the analog encoder, whose funcs table lacks DP hooks such as dp_set_stream_attribute. The subsequent atomic commit then dereferences NULL function pointers in link_set_dpms_on() and crashes. Skip encoders without dp_set_stream_attribute when the stream uses a DP signal (including MST). Use dc_is_dp_signal(stream->signal) for the MST fallback path instead of checking only the link connector signal. Tested on: - GPU: AMD Radeon R7 260X (Bonaire / DCE8) - Board: Supermicro C9X299-PG300 - Setup: DP MST daisy chain, hotplug second monitor or have it connected on boot - Kernel: 7.1.3 (issue observed since 6.19) - Result: kernel oops without patch; dual monitors stable with patch Signed-off-by: Andriy Korud Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5162 Signed-off-by: Alex Deucher (cherry picked from commit 28ec64943e3ee4d9b8d30cea61e380f1429953a8) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c index b92d4f378d60..97ea22af5d2b 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c @@ -992,6 +992,11 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link( for (i = 0; i < pool->stream_enc_count; i++) { if (!res_ctx->is_stream_enc_acquired[i] && pool->stream_enc[i]) { + /* DP/MST needs a digital encoder; skip analog/no-DP encoders */ + if (dc_is_dp_signal(stream->signal) && + (!pool->stream_enc[i]->funcs || + !pool->stream_enc[i]->funcs->dp_set_stream_attribute)) + continue; /* Store first available for MST second display * in daisy chain use case */ @@ -1014,7 +1019,7 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link( * required for non DP connectors. */ - if (j >= 0 && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) + if (j >= 0 && dc_is_dp_signal(stream->signal)) return pool->stream_enc[j]; return NULL; -- cgit v1.2.3 From f8922d5a946699fc2bdc7660e6778bd6726bf8b8 Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Sun, 12 Jul 2026 19:39:26 +0200 Subject: drm/amd/pm/si: Don't schedule thermal work when queue isn't initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When DPM is turned off with the amdgpu.dpm=0 module parameter, the thermal work queue isn't initialized so we shouldn't schedule any work on it. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit bd018d36171a695952c6d391471c279c9e05c8b2) --- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index 8079da7c5335..9d837d23d3a7 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -7689,7 +7689,7 @@ static int si_dpm_process_interrupt(struct amdgpu_device *adev, break; } - if (queue_thermal) + if (queue_thermal && amdgpu_dpm) schedule_work(&adev->pm.dpm.thermal.work); return 0; -- cgit v1.2.3 From 86b6cf8387a4a28754fa251c79e1abfc2e57b534 Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Sun, 12 Jul 2026 19:39:27 +0200 Subject: drm/amd/pm/si: Fix AC/DC switch notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were two mistakes in the previous implementation: The check for ATOM_PP_PLATFORM_CAP_HARDWAREDC should be inverted. We recently learned that the kernel should send PPSMC_MSG_RunningOnAC when the flag is set, and not the other way around. The clocks also need to be recomputed, because the code in the si_apply_state_adjust_rules() function selects different limits on AC and DC. Fixes: 2d071f6457af ("drm/amd/pm/si: Notify the SMC when switching to AC") Tested-by: Jeremy Klarenbeek Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 358dd0a9ce66d898fa934887385327547d599d88) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index 9d837d23d3a7..012227c70600 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -3892,13 +3892,16 @@ static void si_notify_hw_of_powersource(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - /* Check if the platform already manages the AC/DC switch via dedicated GPIO. */ - if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) - return; - - /* The SMU automatically notices DC, but needs to be notified when switching to AC. */ - if (adev->pm.ac_power) + /* + * Check if the platform already manages the AC/DC switch via dedicated GPIO. + * Otherwise SMU automatically notices DC, but needs to be notified of AC. + */ + if (adev->pm.ac_power && + (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)) amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC); + + /* Recompute clocks with updated max_limits. */ + amdgpu_legacy_dpm_compute_clocks(adev); } static PPSMC_Result si_send_msg_to_smc_with_parameter(struct amdgpu_device *adev, -- cgit v1.2.3 From b2ff0595c31cce4303957dd0058e14c89eb70152 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 3 Jun 2026 15:41:28 -0400 Subject: drm/amdgpu: always emit the job vm fence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need the fence to reemit the gds switch or spm update after a queue reset. Fixes: a17ef941212b ("drm/amdgpu: rework ring reset backup and reemit v9") Cc: timur.kristof@gmail.com Cc: christian.koenig@amd.com Reviewed-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit bc639a9eadc75822f7f15a4315c198a4b5513bd2) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index bb99b7c3a010..f224d33c4bc4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -855,12 +855,10 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, job->oa_size); } - if (vm_flush_needed || pasid_mapping_needed || cleaner_shader_needed) { - amdgpu_fence_emit(ring, job->hw_vm_fence, 0); - fence = &job->hw_vm_fence->base; - /* get a ref for the job */ - dma_fence_get(fence); - } + amdgpu_fence_emit(ring, job->hw_vm_fence, 0); + fence = &job->hw_vm_fence->base; + /* get a ref for the job */ + dma_fence_get(fence); if (vm_flush_needed) { mutex_lock(&id_mgr->lock); -- cgit v1.2.3 From 0148ac33547b9af1c5a7f3bb6e5baffcb6e9fac2 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 8 Jul 2026 22:15:20 -0500 Subject: drm/amdgpu: Disable PCIe dynamic speed switching on Ryzen Pinnacle Ridge AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs have PCI controllers that don't support PCIe dynamic speed switching, causing system freezes during GPU initialization when enabled. Disable dynamic speed switching when this CPU is detected. Assisted-by: Claude:sonnet Fixes: 466a7d115326 ("drm/amd: Use the first non-dGPU PCI device for BW limits") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5436 Reviewed-by: Lijo Lazar Link: https://patch.msgid.link/20260709031520.841611-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 9ceb4e034a327a04155f32f1cd1a5031dfa5fe02) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 53335ca96b1d..e5f26e5892ba 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1323,6 +1323,15 @@ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device if (c->x86_vendor == X86_VENDOR_INTEL) return false; + + /* + * AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs don't + * support PCIe dynamic speed switching. + * https://gitlab.freedesktop.org/drm/amd/-/work_items/5436 + */ + if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 0x17 && + c->x86_model == 0x08) + return false; #endif return true; } -- cgit v1.2.3 From ec917f19ff1077709e1c4aa7926c7451106080d0 Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Sun, 12 Jul 2026 19:39:28 +0200 Subject: drm/amd/pm/smu7: Fix AC/DC switch notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were two mistakes in the previous implementation: The check for AutomaticDCTransition should be inverted. We recently learned that the kernel should send PPSMC_MSG_RunningOnAC when the flag is set, and not the other way around. The clocks also need to be recomputed, because the code in the smu7_apply_state_adjust_rules() function selects different limits on AC and DC. Fixes: 96da0d86614e ("drm/amd/pm/smu7: Notify SMU7 of DC->AC switch") Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 516f8fc30a1b56af03f39e93c18707d13419fb1f) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index 95bf187f02a5..bc82ba2e4c9b 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -5857,15 +5857,19 @@ static int smu7_power_off_asic(struct pp_hwmgr *hwmgr) static void smu7_notify_ac_dc(struct pp_hwmgr *hwmgr) { struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev); + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - /* Check if the platform already manages the AC/DC switch via dedicated GPIO. */ - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, + /* + * Check if the platform already manages the AC/DC switch via dedicated GPIO. + * Otherwise SMU automatically notices DC, but needs to be notified of AC. + */ + if (adev->pm.ac_power && + phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_AutomaticDCTransition)) - return; - - /* The SMU automatically notices DC, but needs to be notified when switching to AC. */ - if (adev->pm.ac_power) smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunningOnAC, NULL); + + /* Recompute clocks with updated max_limits. */ + pp_funcs->pm_compute_clocks(adev->powerplay.pp_handle); } static const struct pp_hwmgr_func smu7_hwmgr_funcs = { -- cgit v1.2.3 From 79f408cc06525c6cd01d1a5bf83086a1beede49b Mon Sep 17 00:00:00 2001 From: Timur Kristóf Date: Sat, 11 Jul 2026 13:49:58 +0200 Subject: drm/amdgpu/ttm: Consider concurrent VM flushes for buffer entities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow using multiple SDMA schedulers only on GPUs where we are allowed to do concurrent VM flushes. This consideration is necessary because all GART windows are mapped in VMID 0 (the kernel VMID) so each buffer entity would flush VMID 0 concurrently. Practically this means that we can't use multiple SDMA engines for TTM on GFX6-8 and Navi 1x. Fixes: 01c836788b37 ("drm/amdgpu: pass all the sdma scheds to amdgpu_mman") Fixes: e4029f7a9474 ("drm/amdgpu: only use working sdma schedulers for ttm") Cc: Pierre-Eric Pelloux-Prayer Signed-off-by: Timur Kristóf Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit a8171229bc836607fbc225d323ebc4d14489cfbb) --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 025625e7e800..eb8bbfc7e6d9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2684,12 +2684,22 @@ void amdgpu_sdma_set_buffer_funcs_scheds(struct amdgpu_device *adev, return; } - /* Navi1x's workaround requires us to limit to a single SDMA sched - * for ttm. - */ hub = &adev->vmhub[AMDGPU_GFXHUB(0)]; - adev->mman.num_buffer_funcs_scheds = hub->sdma_invalidation_workaround ? - 1 : n; + + /* + * Allow using multiple SDMA schedulers only on GPUs where + * we are allowed to do concurrent VM flushes. + * This consideration is necessary because all GART windows + * are mapped in VMID 0 (the kernel VMID) so each buffer + * entity would flush VMID 0 concurrently. + * + * Also consider the SDMA invalidation workaround on + * Navi 1x GPUs, which also prevents us from using + * multiple SDMA engines on VMID 0 at the same time. + */ + adev->mman.num_buffer_funcs_scheds = + (adev->vm_manager.concurrent_flush && + !hub->sdma_invalidation_workaround) ? n : 1; } #if defined(CONFIG_DEBUG_FS) -- cgit v1.2.3 From b240f792ae02e9b687eafff934a39e57d1e45365 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 18:51:15 +0800 Subject: drm/amdkfd: free MQD managers on DQM init failures The change referenced by the Fixes tag releases the HIQ SDMA MQD trunk buffer when device_queue_manager_init() fails after it has been allocated. However, the same failure path can also be reached after init_mqd_managers() has succeeded. At that point dqm->mqd_mgrs[] contains per-type MQD manager objects owned by the device queue manager. The normal teardown path frees those objects from uninitialize(), but the initialization error path only frees dqm itself. Free the MQD managers from the initialization error path as well. This is safe for earlier failures because dqm is zeroed when allocated and init_mqd_managers() clears the entries it rolls back internally. Fixes: b7cccc8286bb ("drm/amdkfd: fix a memory leak in device_queue_manager_init()") Signed-off-by: Guangshuo Li Reviewed-by: Mukul Joshi Reviewed-by: Felix Kuehling Signed-off-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 1fff2e07b6670bc5b8f7344a8708c136259cb176) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 4 ++++ 1 file changed, 4 insertions(+) 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 678ec611a4f2..67137679a901 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3103,6 +3103,7 @@ static void deallocate_hiq_sdma_mqd(struct kfd_node *dev, struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev) { struct device_queue_manager *dqm; + int i; pr_debug("Loading device queue manager\n"); @@ -3231,6 +3232,9 @@ struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev) deallocate_hiq_sdma_mqd(dev, &dqm->hiq_sdma_mqd); out_free: + for (i = 0; i < KFD_MQD_TYPE_MAX; i++) + kfree(dqm->mqd_mgrs[i]); + kfree(dqm); return NULL; } -- cgit v1.2.3 From bad177fa75e607e396cd57daaaed881450d7a471 Mon Sep 17 00:00:00 2001 From: Alessandro Rinaldi Date: Fri, 26 Jun 2026 16:36:00 +0200 Subject: drm/amd/display: Force PWM backlight on Lenovo Legion 5 15ARH05 The Lenovo Legion 5 15ARH05 (Renoir) ships a BOE 0x08DF eDP panel that advertises AUX/DPCD backlight control, so amdgpu's automatic detection (amdgpu_backlight == -1) selects AUX. On this panel the AUX backlight path has no effect: brightness writes are accepted but the panel level never changes, the display is stuck at a fixed brightness and max_brightness is reported as a bogus 511000. As a result neither the desktop brightness slider nor the brightness hotkeys do anything. Forcing PWM backlight (amdgpu.backlight=0) restores working control: max_brightness becomes 65535 and the level tracks writes. This has long been applied by users as a manual kernel-parameter workaround. Extend the generic panel backlight quirk with a force_pwm flag, add an entry for the Legion 5 15ARH05 / BOE 0x08DF panel, and have amdgpu disable AUX backlight (use PWM) when the quirk matches and the user lets the driver auto-select the backlight type. Signed-off-by: Alessandro Rinaldi Tested-by: Alessandro Rinaldi Reviewed-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 81b39f43e7e53589491e2eef6bad5389626b4b9c) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 7 +++++-- drivers/gpu/drm/drm_panel_backlight_quirks.c | 9 +++++++++ include/drm/drm_utils.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index fe6eea28d119..6210a1ff7cc2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4069,6 +4069,8 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps; caps->aux_support = false; + panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid); + if (caps->ext_caps->bits.oled == 1 /* * || @@ -4081,6 +4083,9 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) caps->aux_support = false; else if (amdgpu_backlight == 1) caps->aux_support = true; + else if (!IS_ERR_OR_NULL(panel_backlight_quirk) && + panel_backlight_quirk->force_pwm) + caps->aux_support = false; if (caps->aux_support) aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX; @@ -4096,8 +4101,6 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) else caps->aux_min_input_signal = 1; - panel_backlight_quirk = - drm_get_panel_backlight_quirk(aconnector->drm_edid); if (!IS_ERR_OR_NULL(panel_backlight_quirk)) { if (panel_backlight_quirk->min_brightness) { caps->min_input_signal = diff --git a/drivers/gpu/drm/drm_panel_backlight_quirks.c b/drivers/gpu/drm/drm_panel_backlight_quirks.c index f85cb293a3db..e417c7533053 100644 --- a/drivers/gpu/drm/drm_panel_backlight_quirks.c +++ b/drivers/gpu/drm/drm_panel_backlight_quirks.c @@ -20,6 +20,15 @@ struct drm_get_panel_backlight_quirk { }; static const struct drm_get_panel_backlight_quirk drm_panel_min_backlight_quirks[] = { + /* Lenovo Legion 5 15ARH05, AUX backlight non-functional, force PWM */ + { + .dmi_match.field = DMI_SYS_VENDOR, + .dmi_match.value = "LENOVO", + .dmi_match_other.field = DMI_PRODUCT_VERSION, + .dmi_match_other.value = "Lenovo Legion 5 15ARH05", + .ident.panel_id = drm_edid_encode_panel_id('B', 'O', 'E', 0x08df), + .quirk = { .force_pwm = true, }, + }, /* 13 inch matte panel */ { .dmi_match.field = DMI_BOARD_VENDOR, diff --git a/include/drm/drm_utils.h b/include/drm/drm_utils.h index 6a46f755daba..7e077484c5bb 100644 --- a/include/drm/drm_utils.h +++ b/include/drm/drm_utils.h @@ -19,6 +19,7 @@ int drm_get_panel_orientation_quirk(int width, int height); struct drm_panel_backlight_quirk { u16 min_brightness; u32 brightness_mask; + bool force_pwm; }; const struct drm_panel_backlight_quirk * -- cgit v1.2.3 From 9fa26b9eed6195bf840f39ac183b9a6237548755 Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Sun, 28 Jun 2026 15:27:40 +0800 Subject: drm/amd/display: set new_stream to NULL after release In dm_update_crtc_state(), the skip_modeset path releases new_stream via dc_stream_release() but does not set the pointer to NULL. If a later error (e.g., color management failure) triggers the fail label, the error path calls dc_stream_release() again on the same dangling pointer, causing a double release and potential use-after-free. Fix this by setting new_stream to NULL after the initial release. Fixes: 9b690ef3c704 ("drm/amd/display: Avoid full modeset when not required") Signed-off-by: WenTao Liang Reviewed-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 99f3af19073b3ddbfd96e789124cce12c4277b28) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 6210a1ff7cc2..b1e6d735cf02 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -12101,6 +12101,7 @@ skip_modeset: /* Release extra reference */ if (new_stream) dc_stream_release(new_stream); + new_stream = NULL; /* * We want to do dc stream updates that do not require a -- cgit v1.2.3 From 46c3c32ba655c94b885b75b5adb8e481612126bf Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 14:06:47 +0800 Subject: drm/amd/display: wire DCN42B mcache programming callback DCN42B enables DML2 and DML21 by default and defines dcn42b_prepare_mcache_programming(), but the resource function table only wires the callback when CONFIG_DRM_AMD_DC_DML21 is defined. There is no in-tree Kconfig symbol named DRM_AMD_DC_DML21, so the preprocessor always removes the callback entry. Sibling DCN42 and DCN401 resource tables wire their prepare_mcache_programming callbacks unconditionally, and the core DC code already checks whether the callback pointer is present before calling it. Remove the stale guard so DCN42B exposes the callback relation that its source and DML21 build world already provide. This is an RFC patch draft from static conditional callback legality auditing. It needs AMD display maintainer review before submission as a final fix. Signed-off-by: Pengpeng Hou Reviewed-by: George Zhang Signed-off-by: Alex Deucher (cherry picked from commit 85453fb4ff726e1ddb9984ee83dca260903c5353) --- drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index e9c33e3466ea..94e166c0a9b0 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -1900,9 +1900,7 @@ static struct resource_funcs dcn42b_res_pool_funcs = { .update_soc_for_wm_a = dcn30_update_soc_for_wm_a, .add_phantom_pipes = dcn32_add_phantom_pipes, .calculate_mall_ways_from_bytes = dcn32_calculate_mall_ways_from_bytes, -#ifdef CONFIG_DRM_AMD_DC_DML21 .prepare_mcache_programming = dcn42b_prepare_mcache_programming, -#endif .build_pipe_pix_clk_params = dcn42b_build_pipe_pix_clk_params, .get_power_profile = dcn401_get_power_profile, .get_vstartup_for_pipe = dcn401_get_vstartup_for_pipe, -- cgit v1.2.3 From 75c8746b9d0a0317d8a58aa0efa2ff2f7359908b Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 13 Jul 2026 14:53:13 -0500 Subject: drm/amd: Create a device link between APU display and XHCI devices Some AMD APU multi-function devices expose an integrated USB xHCI controller. In some circumstances (such as larger VRAM), the PM core can resume can fail when the xHCI controller is resuming in parallel with the GPU/display function. On affected systems, the xHCI controller can complete pci_pm_resume and start resuming USB devices while the GPU is still in its much longer resume path. This race condition leads to USB device resume failures followed by: xhci_hcd ...: xHCI host not responding to stop endpoint command xhci_hcd ...: HC died; cleaning up Create a device link from any xHCI controller sharing the same PCIe root port as the APU display function. The link uses DL_FLAG_STATELESS and DL_FLAG_PM_RUNTIME to ensure the GPU completes its resume before the xHCI controller begins resuming USB devices. This device link is done specifically in amdgpu so that if the platform firmware has been modified such that this issue doesn't happen the version can be detected and the workaround skipped. Suggested-by: Aaron Ma Reported-by: mrh@frame.work Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221073 Acked-by: Alex Deucher Tested-by: Mark Pearson Tested-by: Alexander F Tested-by: Francis DB Link: https://patch.msgid.link/20260713195313.1739762-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher (cherry picked from commit 07c93d7eeb0d990bc1b8e3b1eafa464bc9feee97) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 12 ++++++ drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 12 +++++- .../gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 45 ++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 762ec3cede96..3969a7670482 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -1367,6 +1367,14 @@ static void smu_feature_cap_init(struct smu_context *smu) bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT); } +static int smu_set_power_dep(struct smu_context *smu, bool enable) +{ + if (!smu->ppt_funcs->set_power_dep) + return 0; + + return smu->ppt_funcs->set_power_dep(smu, enable); +} + static int smu_sw_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -1428,6 +1436,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block) if (!smu->ppt_funcs->get_fan_control_mode) smu->adev->pm.no_fan = true; + smu_set_power_dep(smu, true); + return 0; } @@ -1450,6 +1460,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block) smu_fini_microcode(smu); + smu_set_power_dep(smu, false); + return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index d76e0b005308..e3a89e9a9df4 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -749,6 +749,9 @@ struct smu_context { bool pm_enabled; bool is_apu; + /* Power dependency link from an integrated xHCI controller to the GPU */ + struct device_link *usb_power_link; + uint32_t smc_driver_if_version; uint32_t smc_fw_if_version; uint32_t smc_fw_version; @@ -1648,12 +1651,19 @@ struct pptable_funcs { int (*ras_send_msg)(struct smu_context *smu, enum smu_message_type msg, uint32_t param, uint32_t *read_arg); - /** * @get_ras_smu_drv: Get RAS smu driver interface * Return: ras_smu_drv * */ int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv); + + /** + * @set_power_dep: Create or destroy a power dependency link + * from an integrated xHCI controller to the GPU so that the GPU is + * resumed before the USB controller during PM resume. @enable is true + * to create the link and false to tear it down. + */ + int (*set_power_dep)(struct smu_context *smu, bool enable); }; typedef enum { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index 75719c47a41e..3d73f2050bbe 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -1701,6 +1701,50 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu) return 0; } +/* + * Link any xHCI controller sharing the GPU's PCIe root port as a consumer + * of the GPU so the GPU resumes first, avoiding an xHCI resume race. + */ +static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool enable) +{ + struct amdgpu_device *adev = smu->adev; + struct pci_dev *gpu_pdev = adev->pdev; + struct pci_dev *root_port, *usb_pdev = NULL; + struct device_link *link; + + if (!enable) { + if (smu->usb_power_link) { + device_link_del(smu->usb_power_link); + smu->usb_power_link = NULL; + } + return 0; + } + + root_port = pcie_find_root_port(gpu_pdev); + while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, usb_pdev))) { + struct pci_dev *usb_root; + + usb_root = pcie_find_root_port(usb_pdev); + if (usb_root != root_port) + continue; + + /* Create device link: USB (consumer) depends on GPU (supplier) */ + link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME); + if (link) { + smu->usb_power_link = link; + drm_info(adev_to_drm(adev), "USB controller %s D0 power state depends on %s\n", + pci_name(usb_pdev), pci_name(gpu_pdev)); + /* Only create one link for the first USB controller found */ + break; + } + } + + pci_dev_put(usb_pdev); + + return 0; +} + static const struct pptable_funcs smu_v14_0_0_ppt_funcs = { .check_fw_status = smu_v14_0_check_fw_status, .check_fw_version = smu_cmn_check_fw_version, @@ -1734,6 +1778,7 @@ static const struct pptable_funcs smu_v14_0_0_ppt_funcs = { .dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable, .get_dpm_clock_table = smu_v14_0_common_get_dpm_table, .set_mall_enable = smu_v14_0_common_set_mall_enable, + .set_power_dep = smu_v14_0_0_set_power_dep, }; static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu) -- cgit v1.2.3 From 8382cd234981ae36299bb66a10bac2cd8ff1b99d Mon Sep 17 00:00:00 2001 From: Leo Li Date: Fri, 12 Jun 2026 13:29:31 -0400 Subject: drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock [Why] On DCN, vblank events were delivered from VSTARTUP/VUPDATE (dm_crtc_high_irq/dm_vupdate_high_irq) and pageflip completion from GRPH_PFLIP (dm_pflip_high_irq). These signals can be masked by hardware by a few things: * DPG - DCN can Dynamically Power Gate parts of the display pipe when a self-refresh capable eDP is connected. DPG is engaged when there's enough static frames (detected through drm_vblank_off). Once gated, even though the OTG (output timing generator) is still enabled, VSTARTUP and GRPH_FLIP are masked. * GSL - Driver can use the Global Sync Lock to block HW from latching onto double-buffered registers during programming, to prevent HW from latching onto a partially programmed state. This will mask VSTARTUP, GRPH_FLIP, and VUPDATE. See dcn20_pipe_control_lock(). * MALL - A DCN accessible cache introduced in DCN32+ DGPUs that can store fb data to allow for longer DRAM sleep. When scanning out from MALL, VSTARTUP is masked. When masked, events are never delivered, which can show up as flip_done timeouts in the wild. However, there is an interrupt source on DCN that is never masked: VUPDATE_NO_LOCK. It's simply an unmasked variant of VUPDATE, which fires while the OTG is active, at the exact point hardware latches double-buffered registers. It is therefore the natural single signal for delivering both vblank and flip-completion events on DCN, and the correct point to timestamp both VRR and non-VRR vblanks. DCE's interrupt sources are different, it does not have an unmaskable VUPDATE_NO_LOCK. The only unmaskable DCE interrupt is VLINE0, but it can only be programmed as a vline offset from vsync_start, making it unsuitable for VRR. Thus, we keep DCE untouched and use the existing mix of interrupt sources. [How] For DCN1 and newer only: * Factor the body of dm_crtc_high_irq() into dm_crtc_high_irq_handler() and drive it from dm_vupdate_high_irq() (VUPDATE_NO_LOCK). DCE keeps using dm_crtc_high_irq() (VSTARTUP) and dm_pflip_high_irq() (GRPH_PFLIP) unchanged. * Stop registering VSTARTUP (crtc_irq) and GRPH_PFLIP (pageflip_irq) on DCN, and stop enabling them in amdgpu_dm_crtc_set_vblank() / manage_dm_interrupts(). Enable VUPDATE whenever vblank is enabled on DCN (previously only in VRR mode). The secure-display vline0 interrupt is left untouched. * VUPDATE_NO_LOCK does not early-fire on an immediate (tearing / async) flip, since HW latches the new address right away. Deliver the flip completion event immediately after programming such flips in amdgpu_dm_commit_planes(), and clear pflip_status so the next vupdate handler does not double-send. v2: Do not gate VUPDATE_NO_LOCK on DCN in dm_handle_vrr_transition() Also toggle VUPDATE_NO_LOCK on DCN in dm_gpureset_toggle_interrupts() Re-cook vblank event count and timestamp for immediate flips Fixes: 9b47278cec98 ("drm/amd/display: temp w/a for dGPU to enter idle optimizations") Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/3787 Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141 Assisted-by: Copilot:claude-opus-4.8 Co-developed-by: Matthew Schwartz Signed-off-by: Matthew Schwartz Tested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Leo Li Signed-off-by: Alex Deucher (cherry picked from commit c87e6635d2db02c88ae8d09529362da672d34770) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 347 +++++++++++---------- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 70 +++-- 2 files changed, 228 insertions(+), 189 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index b1e6d735cf02..39c0ea1f50b1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -580,89 +580,25 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev, queue_work(system_percpu_wq, &offload_work->work); } -static void dm_vupdate_high_irq(void *interrupt_params) -{ - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; - struct amdgpu_crtc *acrtc; - struct drm_device *drm_dev; - struct drm_vblank_crtc *vblank; - ktime_t frame_duration_ns, previous_timestamp; - unsigned long flags; - int vrr_active; - - acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE); - - if (acrtc) { - vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); - drm_dev = acrtc->base.dev; - vblank = drm_crtc_vblank_crtc(&acrtc->base); - previous_timestamp = atomic64_read(&irq_params->previous_timestamp); - frame_duration_ns = vblank->time - previous_timestamp; - - if (frame_duration_ns > 0) { - trace_amdgpu_refresh_rate_track(acrtc->base.index, - frame_duration_ns, - ktime_divns(NSEC_PER_SEC, frame_duration_ns)); - atomic64_set(&irq_params->previous_timestamp, vblank->time); - } - - drm_dbg_vbl(drm_dev, - "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, - vrr_active); - - /* Core vblank handling is done here after end of front-porch in - * vrr mode, as vblank timestamping will give valid results - * while now done after front-porch. This will also deliver - * page-flip completion events that have been queued to us - * if a pageflip happened inside front-porch. - */ - if (vrr_active && acrtc->dm_irq_params.stream) { - bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; - bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; - bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state - == VRR_STATE_ACTIVE_VARIABLE; - - amdgpu_dm_crtc_handle_vblank(acrtc); - - /* BTR processing for pre-DCE12 ASICs */ - if (adev->family < AMDGPU_FAMILY_AI) { - spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); - mod_freesync_handle_v_update( - adev->dm.freesync_module, - acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params); - - if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { - schedule_dc_vmin_vmax(adev, - acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params.adjust); - } - spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); - } - } - } -} - /** - * dm_crtc_high_irq() - Handles CRTC interrupt - * @interrupt_params: used for determining the CRTC instance + * dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling + * @adev: amdgpu device + * @acrtc: the CRTC to service * - * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK - * event handler. + * Performs writeback completion, vblank event handling, CRC processing, VRR BTR + * updates and pageflip completion delivery. + * + * On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from + * dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank + * from dm_crtc_high_irq(). */ -static void dm_crtc_high_irq(void *interrupt_params) +static void dm_crtc_high_irq_handler(struct amdgpu_device *adev, + struct amdgpu_crtc *acrtc) { - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; struct drm_writeback_job *job; - struct amdgpu_crtc *acrtc; unsigned long flags; int vrr_active; - - acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); - if (!acrtc) - return; + bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0; if (acrtc->wb_conn) { spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags); @@ -699,12 +635,17 @@ static void dm_crtc_high_irq(void *interrupt_params) vrr_active, acrtc->dm_irq_params.active_planes); /** - * Core vblank handling at start of front-porch is only possible - * in non-vrr mode, as only there vblank timestamping will give - * valid results while done in front-porch. Otherwise defer it - * to dm_vupdate_high_irq after end of front-porch. + * Core vblank handling. + * + * On DCN this handler runs at VUPDATE_NO_LOCK, the register latch + * point, which is the correct place to timestamp both VRR and non-VRR + * vblanks. + * + * On DCE this handler runs at the start of front-porch, where only + * non-VRR timestamping is valid; VRR vblank is deferred to + * dm_vupdate_high_irq() after end of front-porch. */ - if (!vrr_active) + if (is_dcn || !vrr_active) amdgpu_dm_crtc_handle_vblank(acrtc); /** @@ -737,18 +678,16 @@ static void dm_crtc_high_irq(void *interrupt_params) } /* - * If there aren't any active_planes then DCH HUBP may be clock-gated. - * In that case, pageflip completion interrupts won't fire and pageflip - * completion events won't get delivered. Prevent this by sending - * pending pageflip events from here if a flip is still pending. + * Deliver pageflip completion events (DCN only). + * + * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch + * point. Deliver any pending pageflip completion event from here. * - * If any planes are enabled, use dm_pflip_high_irq() instead, to - * avoid race conditions between flip programming and completion, - * which could cause too early flip completion events. + * NOTE: This can deliver an event for a flip that was armed but not yet + * programmed into HW; that race is closed in a follow-up change by + * checking the programmed flip status. */ - if (adev->family >= AMDGPU_FAMILY_RV && - acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED && - acrtc->dm_irq_params.active_planes == 0) { + if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) { if (acrtc->event) { drm_crtc_send_vblank_event(&acrtc->base, acrtc->event); acrtc->event = NULL; @@ -760,6 +699,104 @@ static void dm_crtc_high_irq(void *interrupt_params) spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); } +static void dm_vupdate_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_crtc *acrtc; + struct drm_device *drm_dev; + struct drm_vblank_crtc *vblank; + ktime_t frame_duration_ns, previous_timestamp; + unsigned long flags; + int vrr_active; + + acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE); + if (!acrtc) + return; + + vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); + drm_dev = acrtc->base.dev; + vblank = drm_crtc_vblank_crtc(&acrtc->base); + previous_timestamp = atomic64_read(&irq_params->previous_timestamp); + frame_duration_ns = vblank->time - previous_timestamp; + + if (frame_duration_ns > 0) { + trace_amdgpu_refresh_rate_track(acrtc->base.index, + frame_duration_ns, + ktime_divns(NSEC_PER_SEC, frame_duration_ns)); + atomic64_set(&irq_params->previous_timestamp, vblank->time); + } + + drm_dbg_vbl(drm_dev, + "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, + vrr_active); + + /* + * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver + * vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are + * not used. Run the full handler here. + */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { + dm_crtc_high_irq_handler(adev, acrtc); + return; + } + + /* DCE only below. */ + + /* Core vblank handling is done here after end of front-porch in + * vrr mode, as vblank timestamping will give valid results + * while now done after front-porch. This will also deliver + * page-flip completion events that have been queued to us + * if a pageflip happened inside front-porch. + */ + if (vrr_active && acrtc->dm_irq_params.stream) { + bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; + bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; + bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state + == VRR_STATE_ACTIVE_VARIABLE; + + amdgpu_dm_crtc_handle_vblank(acrtc); + + /* BTR processing for pre-DCE12 ASICs */ + if (adev->family < AMDGPU_FAMILY_AI) { + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); + mod_freesync_handle_v_update( + adev->dm.freesync_module, + acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params); + + if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { + schedule_dc_vmin_vmax(adev, + acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params.adjust); + } + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); + } + } +} + +/** + * dm_crtc_high_irq() - Handles CRTC interrupt + * @interrupt_params: used for determining the CRTC instance + * + * Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler. + * + * Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is + * driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq(). + */ +static void dm_crtc_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_crtc *acrtc; + + acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); + if (!acrtc) + return; + + dm_crtc_high_irq_handler(adev, acrtc); +} + #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) /** * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for @@ -3298,6 +3335,13 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, */ if (!dc_interrupt_set(adev->dm.dc, irq_source, enable)) drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis"); + + } else if (acrtc && state->stream_status[i].plane_count != 0) { + /* DCN only needs to toggle VUPDATE_NO_LOCK */ + rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, enable); + if (rc) + drm_warn(adev_to_drm(adev), "Failed to %sable vupdate interrupt\n", + enable ? "en" : "dis"); } } @@ -4866,38 +4910,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) * for acknowledging and handling. */ - /* Use VSTARTUP interrupt */ - for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP; - i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1; - i++) { - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq); - - if (r) { - drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 || - int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) { - drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_crtc_high_irq, c_irq_params)) - return -ENOMEM; - } - /* Use otg vertical line interrupt */ #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) { @@ -4969,37 +4981,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) return -ENOMEM; } - /* Use GRPH_PFLIP interrupt */ - for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT; - i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1; - i++) { - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST || - int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) { - drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_pflip_high_irq, c_irq_params)) - return -ENOMEM; - } - /* HPD */ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT, &adev->hpd_irq); @@ -9710,14 +9691,22 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, drm_crtc_vblank_on_config(&acrtc->base, &config); - /* Allow RX6xxx, RX7700, RX7800 GPUs to call amdgpu_irq_get.*/ + /* + * Since pflip_high_irq is no longer registered for DCN, grab an + * extra reference to vupdate irq instead to workaround this + * issue: + * https://gitlab.freedesktop.org/drm/amd/-/work_items/3936 + * + * The callbacks to drm_vblank_on/off should really take care of + * this though. + */ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { case IP_VERSION(3, 0, 0): case IP_VERSION(3, 0, 2): case IP_VERSION(3, 0, 3): case IP_VERSION(3, 2, 0): - if (amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type)) - drm_err(dev, "DM_IRQ: Cannot get pageflip irq!\n"); + if (amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type)) + drm_err(dev, "DM_IRQ: Cannot get vupdate irq!\n"); #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) if (amdgpu_irq_get(adev, &adev->vline0_irq, irq_type)) drm_err(dev, "DM_IRQ: Cannot get vline0 irq!\n"); @@ -9735,8 +9724,8 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, if (amdgpu_irq_put(adev, &adev->vline0_irq, irq_type)) drm_err(dev, "DM_IRQ: Cannot put vline0 irq!\n"); #endif - if (amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type)) - drm_err(dev, "DM_IRQ: Cannot put pageflip irq!\n"); + if (amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type)) + drm_err(dev, "DM_IRQ: Cannot put vupdate irq!\n"); } drm_crtc_vblank_off(&acrtc->base); @@ -9749,6 +9738,10 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev, int irq_type = amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id); + /* GRPH_PFLIP is not used on DCN; nothing to reapply. */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) + return; + /** * This reads the current state for the IRQ and force reapplies * the setting to hardware. @@ -10081,9 +10074,13 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm, struct dm_crtc_state *old_state, struct dm_crtc_state *new_state) { + struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev); bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state); bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state); + /* Only DCE gates vupdate on VRR, keep it enabled for DCN */ + bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0; + if (!old_vrr_active && new_vrr_active) { /* Transition VRR inactive -> active: * While VRR is active, we must not disable vblank irq, as a @@ -10093,7 +10090,8 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm, * We also need vupdate irq for the actual core vblank handling * at end of vblank. */ - WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0); + if (vrr_gates_vupdate) + WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0); WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0); drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n", __func__, new_state->base.crtc->base.id); @@ -10109,7 +10107,8 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm, /* Transition VRR active -> inactive: * Allow vblank irq disable again for fixed refresh rate. */ - WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0); + if (vrr_gates_vupdate) + WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0); drm_crtc_vblank_put(new_state->base.crtc); drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n", __func__, new_state->base.crtc->base.id); @@ -10280,6 +10279,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state); bool cursor_update = false; bool pflip_present = false; + bool immediate_flip = false; bool dirty_rects_changed = false; bool updated_planes_and_streams = false; struct { @@ -10444,6 +10444,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, acrtc_state->update_type == UPDATE_TYPE_FAST && get_mem_type(old_plane_state->fb) == get_mem_type(fb); + immediate_flip |= bundle->flip_addrs[planes_count].flip_immediate; + timestamp_ns = ktime_get_ns(); bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000); bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count]; @@ -10636,6 +10638,29 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) amdgpu_dm_commit_cursors(state); + /* + * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK. + * However, an immediate (tearing / async) flip is latched by HW right + * away and does not wait for the next vupdate, so deliver its + * completion event here after programming. + * + * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so + * this is DCN-only. + */ + if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) { + spin_lock_irqsave(&pcrtc->dev->event_lock, flags); + if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED && + acrtc_attach->event) { + drm_crtc_accurate_vblank_count(&acrtc_attach->base); + drm_crtc_send_vblank_event(&acrtc_attach->base, + acrtc_attach->event); + acrtc_attach->event = NULL; + drm_crtc_vblank_put(&acrtc_attach->base); + acrtc_attach->pflip_status = AMDGPU_FLIP_NONE; + } + spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); + } + cleanup: kfree(bundle); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 3dcedaa67ed8..b43cd68cde67 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -274,7 +274,14 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) drm_crtc_vblank_restore(crtc); } - if (dc_supports_vrr(dm->dc->ctx->dce_version)) { + /* + * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver + * vblank and pageflip completion events, so enable it whenever vblank + * is enabled. On DCE, vupdate is only needed in VRR mode. + */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { + rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable); + } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) { if (enable) { /* vblank irq on -> Only need vupdate irq in vrr mode */ if (amdgpu_dm_crtc_vrr_active(acrtc_state)) @@ -285,39 +292,46 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) } } - if (rc) - return rc; - - /* crtc vblank or vstartup interrupt */ - if (enable) { - rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type); - drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc); - } else { - rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type); - drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc); - } - if (rc) return rc; /* - * hubp surface flip interrupt - * - * We have no guarantee that the frontend index maps to the same - * backend index - some even map to more than one. - * - * TODO: Use a different interrupt or check DC itself for the mapping. + * VLINE0 (crtc_irq) and GRPH_PFLIP (pageflip_irq) are only used on + * DCE. On DCN, vblank and pageflip completion are delivered from + * VUPDATE_NO_LOCK (enabled above), so don't touch them here. */ - if (enable) { - rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type); - drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc); - } else { - rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type); - drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc); - } + if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { + /* crtc vblank or vstartup interrupt */ + if (enable) { + rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc); + } - if (rc) - return rc; + if (rc) + return rc; + + /* + * hubp surface flip interrupt + * + * We have no guarantee that the frontend index maps to the same + * backend index - some even map to more than one. + * + * TODO: Use a different interrupt or check DC itself for the mapping. + */ + if (enable) { + rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc); + } + + if (rc) + return rc; + } #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) /* crtc vline0 interrupt, only available on DCN+ */ -- cgit v1.2.3 From 48ab86360af117123eb1b15e38f068acf3826400 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Fri, 12 Jun 2026 15:49:03 -0400 Subject: drm/amd/display: check GRPH_FLIP status before sending event [Why] After unifying DCN interrupt sources under VUPDATE_NO_LOCK, we have two remaining issues to clean up: 1. On DCN, flip completion is now delivered from VUPDATE_NO_LOCK (dm_crtc_high_irq_handler) instead of GRPH_PFLIP. But VUPDATE_NO_LOCK fires every frame, regardless of whether a flip has latched. 2. There is a window during commit where a flip is armed (pflip_status = SUBMITTED) but not yet programmed into HW. If the VUPDATE_NO_LOCK fires in that window, its handler would deliver a flip event to userspace before HW has latched to it. If userspace then renders to what it believes is now the back buffer (but HW is still latched to it!), it will cause display corruption. This issue seemed to have been introduced by: commit 1159898a88db ("drm/amd/display: Handle commit plane with no FB.") Enabling replay or psr extended the duration of this window, and hence made corruption more likely to be observed. [How] * Move acrtc->event/pflip_status arming to after update_planes_and_stream_adapter() has programmed the flip into HW. This closes the window where pflip_status is SUBMITTED but the flip is not yet programmed. * Add dc_get_flip_pending_on_otg(), which reads the HUBP flip-pending status straight from HW for the pipe(s) bound to an OTG instance. It is keyed only by otg_inst and does not take or mutate a dc_plane_state, so it is safe to call from the OTG interrupt handler without racing a concurrent commit that may be modifying plane state. * Optimistically query for flip-pending after programming, in the event that HW latched to the new fb between programming start and arming event. If it latched, send the vblank event immediately, rather than wait for the next vblank IRQ. * In the VUPDATE_NO_LOCK handler, only deliver flip completion once dc_get_flip_pending_on_otg() reports the flip is no longer pending. Otherwise leave the flip armed and retry on the next vupdate. * For DCE, maintain the existing behavior of arming flips before programming, and relying on GRPH_FLIP to fire at HW latch. v2: * Drop flip_programmed completion object, instead move event/pflip_status arming after programming. * For DCN, optimistically query for flip pending immediately after programming, and if it latched, send event right away. v3: * Fix event timestamps on optimistic flip latch detection, where it's possible for it to run *before* the vupdate IRQ updates the timestamp. * Add more docstrings for DCN vblank handling. * Clean up if conditions in dm_arm_vblank_event(). * Code style cleanup on braces surrounding multi-line statements. Fixes: 9b47278cec98 ("drm/amd/display: temp w/a for dGPU to enter idle optimizations") Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/3787 Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141 Assisted-by: Copilot:claude-opus-4.8 Tested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Leo Li Signed-off-by: Alex Deucher (cherry picked from commit f64a9be5653689ff43e148cd8a6483077488c8e5) Cc: stable@vger.kernel.org # 8382cd234981: drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 191 +++++++++++++++++----- drivers/gpu/drm/amd/display/dc/core/dc.c | 45 +++++ drivers/gpu/drm/amd/display/dc/dc.h | 1 + 3 files changed, 197 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 39c0ea1f50b1..10105341e6db 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -681,13 +681,30 @@ static void dm_crtc_high_irq_handler(struct amdgpu_device *adev, * Deliver pageflip completion events (DCN only). * * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch - * point. Deliver any pending pageflip completion event from here. + * point. Deliver any pending pageflip completion event from here, + * once HW has consumed the new address (the OTG no longer reports a + * pending flip). * - * NOTE: This can deliver an event for a flip that was armed but not yet - * programmed into HW; that race is closed in a follow-up change by - * checking the programmed flip status. + * Also handle the case here where there aren't any active planes and + * DCN HUBP may be clock-gated, so the flip-pending status may be + * undefined. */ - if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) { + if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED && + acrtc->event) { + + if (!dc_get_flip_pending_on_otg(adev->dm.dc, acrtc->otg_inst)) { + drm_crtc_send_vblank_event(&acrtc->base, acrtc->event); + acrtc->event = NULL; + drm_crtc_vblank_put(&acrtc->base); + acrtc->pflip_status = AMDGPU_FLIP_NONE; + } + /* + * If the flip is still pending, leave it armed and + * retry on the next vupdate. + */ + } else if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED && + acrtc->dm_irq_params.active_planes == 0) { + if (acrtc->event) { drm_crtc_send_vblank_event(&acrtc->base, acrtc->event); acrtc->event = NULL; @@ -10257,6 +10274,28 @@ static void amdgpu_dm_enable_self_refresh(struct amdgpu_display_manager *dm, } } +static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc, + struct dm_crtc_state *acrtc_state, + bool pflip_update, + bool cursor_update) +{ + assert_spin_locked(&acrtc->base.dev->event_lock); + + if (!acrtc->base.state->event || acrtc_state->active_planes == 0) + return; + + if (pflip_update) { + drm_crtc_vblank_get(&acrtc->base); + WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE); + /* Arm flip completion handling and event delivery after programming. */ + prepare_flip_isr(acrtc); + } else if (cursor_update) { + drm_crtc_vblank_get(&acrtc->base); + acrtc->event = acrtc->base.state->event; + acrtc->base.state->event = NULL; + } +} + static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, struct drm_device *dev, struct amdgpu_display_manager *dm, @@ -10280,6 +10319,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, bool cursor_update = false; bool pflip_present = false; bool immediate_flip = false; + bool flip_latched_during_prog = false; bool dirty_rects_changed = false; bool updated_planes_and_streams = false; struct { @@ -10514,39 +10554,24 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, usleep_range(1000, 1100); } - /** - * Prepare the flip event for the pageflip interrupt to handle. - * - * This only works in the case where we've already turned on the - * appropriate hardware blocks (eg. HUBP) so in the transition case - * from 0 -> n planes we have to skip a hardware generated event - * and rely on sending it from software. - */ - if (acrtc_attach->base.state->event && - acrtc_state->active_planes > 0) { - drm_crtc_vblank_get(pcrtc); - - spin_lock_irqsave(&pcrtc->dev->event_lock, flags); - - WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE); - prepare_flip_isr(acrtc_attach); - - spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); - } - if (acrtc_state->stream) { if (acrtc_state->freesync_vrr_info_changed) bundle->stream_update.vrr_infopacket = &acrtc_state->stream->vrr_infopacket; } - } else if (cursor_update && acrtc_state->active_planes > 0) { - spin_lock_irqsave(&pcrtc->dev->event_lock, flags); - if (acrtc_attach->base.state->event) { - drm_crtc_vblank_get(pcrtc); - acrtc_attach->event = acrtc_attach->base.state->event; - acrtc_attach->base.state->event = NULL; + } + + /* + * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for + * event delivery. Only GRPH_FLIP handler can send pflip events, and it + * only fires if HW latched to the flip. Maintain legacy behavior by + * arming event before programming. + */ + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { + scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + dm_arm_vblank_event(acrtc_attach, acrtc_state, + pflip_present, cursor_update); } - spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); } /* Update the planes if changed or disable if we don't have any. */ @@ -10639,17 +10664,103 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, amdgpu_dm_commit_cursors(state); /* - * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK. - * However, an immediate (tearing / async) flip is latched by HW right - * away and does not wait for the next vupdate, so deliver its - * completion event here after programming. + * DCN specific vblank handling + * ============================ + * + * With the event_lock held, arm the vblank event, and determine whether + * deliver it immediately, or in VUPDATE_NO_LOCK IRQ (i.e. HW latch + * point) handler. Do this *after* programming so that the IRQ handler + * will not deliver the event before HW laches onto the programmed + * values: + * + * Commit thread IRQ handler HW + * ----------------------------------------------------------------- + * arm_vblank_event() + * vupdate() + * vupdate_handler() + * cook_timestamp() + * # prev flip already latched, + * # so flip_latched == true. + * if event_armed && flip_latched: + * send_vblank_event() + * # sent before latch, **BAD!** + * hw_program() + * vupdate() + * **latch** + * + * There's a consequence of arming after: it's possible for HW to latch + * between start of HW programming and acrtc->event/pflip_status arming. + * When this happens, the IRQ handler will send the event on the next + * immediate latch point, even though HW has already latched. This is + * handled by optimistically checking for HW latch after programming, + * and if latched, send the event immediately: * - * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so - * this is DCN-only. + * Commit thread IRQ handler HW + * ----------------------------------------------------------------- + * hw_program() + * vupdate() + * **latch** + * vupdate_handler() + * cook_timestamp() + * # event_armed == false + * # **no event sent!** + * arm_vblank_event() + * if flip_latched: + * **send_vblank_event()** + * disarm_vblank_event() + * + * The IRQ handler is expected to cook the timestamp, but we need to + * cook the timestamp before optimistic sending as well. That's because + * the following sequence is possible: + * + * Commit thread IRQ handler HW + * ----------------------------------------------------------------- + * hw_program() + * arm_vblank_event() + * vupdate() + * **latch** + * if flip_latched: + * # Need cook before send! + * **cook_timestamp()** + * send_vblank_event() + * disarm_vblank_event() + * vupdate_handler() + * cook_timestamp() + * # event_armed == false + * # no event sent! + * + * Cooking twice is OK, since DRM scanout accurate timestamps report A) + * the previous vactive start if currently in vactive, or B) the next + * vactive start if currently in vblank (see &get_vblank_counter). 'A)' + * is what we want for the optimistic send, and for 'B)', we'll cook a + * timestamp no later than the next IRQ handler run. + * + * The more correct fix is to wrap programming and arming with the + * event_lock and thus serializing it with the IRQ handler. However, + * there are various sleep-waits within + * update_planes_and_stream_adapter() that makes spin locking illegal. + * And on full updates, it can take 1-2 frame-times to return (see + * commit_planes_for_stream). + * + * On DCE, GRPH_PFLIP IRQ is used and takes care of this. */ - if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) { + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) { spin_lock_irqsave(&pcrtc->dev->event_lock, flags); - if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED && + + if (updated_planes_and_streams) { + flip_latched_during_prog = + !dc_get_flip_pending_on_otg(dm->dc, acrtc_attach->otg_inst); + } + + dm_arm_vblank_event(acrtc_attach, acrtc_state, + pflip_present, cursor_update); + + /* + * Deliver the event immediately on immediate flip, or on a + * update that has already latched. + */ + if ((immediate_flip || flip_latched_during_prog) && + acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED && acrtc_attach->event) { drm_crtc_accurate_vblank_count(&acrtc_attach->base); drm_crtc_send_vblank_event(&acrtc_attach->base, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 175106cce5a4..e25b94b65dac 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -6165,6 +6165,51 @@ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src) dal_irq_service_ack(dc->res_pool->irqs, src); } +/* + * dc_get_flip_pending_on_otg() - Check if a GRPH_FLIP is still pending on OTG + * + * @dc: display core context @otg_inst: OTG instance to query + * + * Reads the HUBP flip-pending status for the pipe(s) bound to @otg_inst, + * returning true if any of them has not yet latched its programmed surface + * address. + * + * Unlike dc_plane_get_status(), this does not take or mutate a dc_plane_state, + * so it is safe to call from interrupt context without racing a concurrent + * commit that may be updating plane state. + * + * Return: true if a flip is still pending on the OTG, false otherwise. + */ +bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst) +{ + bool flip_pending = false; + int i; + + if (!dc || !dc->current_state) + return false; + + dc_exit_ips_for_hw_access(dc); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { + struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; + struct hubp *hubp = pipe_ctx->plane_res.hubp; + + if (!pipe_ctx->plane_state || !pipe_ctx->stream_res.tg) + continue; + + if (pipe_ctx->stream_res.tg->inst != otg_inst) + continue; + + if (hubp && hubp->funcs->hubp_is_flip_pending && + hubp->funcs->hubp_is_flip_pending(hubp)) { + flip_pending = true; + break; + } + } + + return flip_pending; +} + void dc_power_down_on_boot(struct dc *dc) { if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW && diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index ebdcbfa7db80..1dc85f6b6689 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -2883,6 +2883,7 @@ enum dc_irq_source dc_interrupt_to_irq_source( uint32_t ext_id); bool dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable); void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src); +bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst); enum dc_irq_source dc_get_hpd_irq_source_at_index( struct dc *dc, uint32_t link_index); -- cgit v1.2.3 From f39283eab44fb7e304677b2aa1d8c38f151b6566 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Mon, 4 May 2026 14:09:49 -0400 Subject: Revert "drm/amd/display: Restore 5s vbl offdelay for NV3x+ DGPUs" Now that proper fixes have been found, let's revert this workaround. This reverts commit a1fc7bf6677eb547167cb72b3bcafdc34b976692. Tested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Leo Li Signed-off-by: Alex Deucher (cherry picked from commit f64a9be5653689ff43e148cd8a6483077488c8e5) Cc: stable@vger.kernel.org # 8382cd234981: drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock Cc: stable@vger.kernel.org # 48ab86360af1: drm/amd/display: check GRPH_FLIP status before sending event Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 10105341e6db..9c564cd5edee 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -9675,21 +9675,9 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, if (acrtc_state) { timing = &acrtc_state->stream->timing; - if (amdgpu_ip_version(adev, DCE_HWIP, 0) >= - IP_VERSION(3, 2, 0) && - !(adev->flags & AMD_IS_APU)) { - /* - * DGPUs NV3x and newer that support idle optimizations - * experience intermittent flip-done timeouts on cursor - * updates. Restore 5s offdelay behavior for now. - * - * Discussion on the issue: - * https://lore.kernel.org/amd-gfx/20260217191632.1243826-1-sysdadmin@m1k.cloud/ - */ - config.offdelay_ms = 5000; - config.disable_immediate = false; - } else if (amdgpu_ip_version(adev, DCE_HWIP, 0) < - IP_VERSION(3, 5, 0)) { + if (amdgpu_ip_version(adev, DCE_HWIP, 0) < + IP_VERSION(3, 5, 0) || + !(adev->flags & AMD_IS_APU)) { /* * Older HW and DGPU have issues with instant off; * use a 2 frame offdelay. -- cgit v1.2.3 From 523307b8516fc740895238af8473aa0630b3e088 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 11 Jul 2026 16:36:55 +0900 Subject: ntfs: preserve RECALL_ON_OPEN on WSL special-file reparse points When creating a WSL special file (socket, fifo, character or block device), __ntfs_create() sets FILE_ATTRIBUTE_RECALL_ON_OPEN in ni->flags as valid_reparse_data() requires for these tags. This flag is intentionally absent from $FILE_NAME, so the subsequent reload ni->flags = fn->file_attributes; drops it from ni->flags, the authoritative copy written back to $STANDARD_INFORMATION. The on-disk file_attributes becomes 0x00000404 instead of 0x00040404, and after a remount valid_reparse_data() rejects the reparse point while fsck reports "$REPARSE_POINT data is corrupted". Preserve the RECALL_ON_OPEN bit across the reload. Symlinks do not set that bit, so they are unaffected. Fixes: af0db57d4293 ("ntfs: update inode operations") Signed-off-by: Namjae Jeon --- fs/ntfs/namei.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 5ff25e9aaa32..cd403b1d99ee 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -685,7 +685,8 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d mutex_unlock(&dir_ni->mrec_lock); mutex_unlock(&ni->mrec_lock); - ni->flags = fn->file_attributes; + ni->flags = fn->file_attributes | + (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN); /* Set the sequence number. */ vi->i_generation = ni->seq_no; set_nlink(vi, 1); -- cgit v1.2.3 From ef19a9cf037957fe3a35df8355c76ff0a63a0436 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 15:31:37 -0700 Subject: ata: sata_mv: accept 1 or 2 resources in platform probe Board files in arch/arm/plat-orion, arch/arm/mach-dove, arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the "sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). Those devices are rejected with -EINVAL, so SATA no longer probes on legacy Marvell Orion/Kirkwood-style boards. Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 resources (legacy, IRQ supplied as a second resource) so both probing paths work. Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_mv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 41647a56a9f4..365454390d7e 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4027,7 +4027,7 @@ static int mv_platform_probe(struct platform_device *pdev) /* * Simple resource validation .. */ - if (unlikely(pdev->num_resources != 1)) { + if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) { dev_err(&pdev->dev, "invalid number of resources\n"); return -EINVAL; } -- cgit v1.2.3 From 4d99a91574c420decab56cc880fad0dc15b8a7a3 Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Fri, 17 Jul 2026 23:55:26 +0530 Subject: ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources() On phy_init() failure the error path fallsthrough to disable_rsts, which deasserts the controller reset and then enters disable_phys calling phy_power_off() on PHYs that were never powered on. That corrupts the PHY power_count and triggers an extra runtime PM put. Use a separate exit_phys path that unwinds with phy_exit() only and falls through to disable_clks while the controller remains in reset. Reserve phy_power_off() for the phy_power_on() failure path only, and skip masked-out ports in both unwind loops. On phy_power_on() failure re-assert the controller reset before disabling clocks and regulators, matching the teardown order used by ahci_platform_enable_resources() and ahci_platform_disable_resources(). Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support") Signed-off-by: Radhey Shyam Pandey Signed-off-by: Damien Le Moal --- drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c index 2d6a08c23d6a..2961e53288f4 100644 --- a/drivers/ata/ahci_ceva.c +++ b/drivers/ata/ahci_ceva.c @@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) rc = phy_init(hpriv->phys[i]); if (rc) - goto disable_rsts; + goto exit_phys; } /* De-assert the controller reset */ @@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) return 0; -disable_rsts: - ahci_platform_deassert_rsts(hpriv); - disable_phys: while (--i >= 0) { + if (ahci_ignore_port(hpriv, i)) + continue; + phy_power_off(hpriv->phys[i]); phy_exit(hpriv->phys[i]); } + ahci_platform_assert_rsts(hpriv); + goto disable_clks; + +exit_phys: + while (--i >= 0) { + if (ahci_ignore_port(hpriv, i)) + continue; + + phy_exit(hpriv->phys[i]); + } disable_clks: ahci_platform_disable_clks(hpriv); -- cgit v1.2.3 From 477869bfafea65492d23de62c1b5208147c09dd2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Jul 2026 10:43:25 -1000 Subject: sched_ext: Reject setting disallow from init_task outside the enable path The p->scx.disallow revert assumes the root enable path, where the switching loop reads the reverted policy right afterwards and leaves the task off SCX. The sub-scheduler disable path also reaches it when re-initializing the returned tasks on a root parent. Nothing reads the policy there: the task is enabled on root anyway and keeps running on the ext class with a silently rewritten policy. Kill the sched instead, matching the fork and non-root branches, and update the disallow documentation, which equated !fork with the load path and pointed at a stale debugfs path for nr_rejected. Fixes: 337ec00b1d9c ("sched_ext: Implement cgroup sub-sched enabling and disabling") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- include/linux/sched/ext.h | 10 +++++----- kernel/sched/ext/ext.c | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/sched/ext.h b/include/linux/sched/ext.h index 20b2343aa344..87e353f7e011 100644 --- a/include/linux/sched/ext.h +++ b/include/linux/sched/ext.h @@ -244,11 +244,11 @@ struct sched_ext_entity { * to %SCHED_EXT with -%EACCES. * * Can be set from ops.init_task() while the BPF scheduler is being - * loaded (!scx_init_task_args->fork). If set and the task's policy is - * already %SCHED_EXT, the task's policy is rejected and forcefully - * reverted to %SCHED_NORMAL. The number of such events are reported - * through /sys/kernel/debug/sched_ext::nr_rejected. Setting this flag - * during fork is not allowed. + * loaded. If set and the task's policy is already %SCHED_EXT, the + * task's policy is rejected and forcefully reverted to %SCHED_NORMAL. + * The number of such events are reported through + * /sys/kernel/sched_ext/nr_rejected. Setting this flag from any other + * ops.init_task() invocation, such as during fork, fails the scheduler. */ bool disallow; /* reject switching into SCX */ diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index e3fa7b2fac9d..46f135bddd46 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -3606,6 +3606,9 @@ static int __scx_init_task(struct scx_sched *sch, struct task_struct *p, bool fo } else if (unlikely(fork)) { scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] during fork", p->comm, p->pid); + } else if (unlikely(scx_enable_state() != SCX_ENABLING)) { + scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] outside the enable path", + p->comm, p->pid); } else { struct rq *rq; struct rq_flags rf; -- cgit v1.2.3 From 5f8b69642d18e1f3e11996707842ac530444e959 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Jul 2026 10:44:02 -1000 Subject: sched_ext: Take cgroup_lock() first in scx_cgroup_lock() scx_cgroup_lock() write-locks scx_cgroup_ops_rwsem and then takes cgroup_lock(), which can deadlock through kernfs: scx enable/disable cgroup rmdir cpu.weight write ------------------ ------------ ---------------- cgroup_lock() percpu_down_write(rwsem) cgroup_lock() kernfs_get_active() percpu_down_read(rwsem) kernfs_drain() The enable path waits for the rmdir to release cgroup_mutex. The rmdir, deactivating the cpu controller's files, waits in kernfs_drain() for the write's active reference. The write, in scx_group_set_weight(), waits for the rwsem behind the pending writer. Take cgroup_lock() first. The set_* paths take no cgroup locks inside the read side, so a pending write-lock then only waits for read sections that always run to completion, and no dependency from the rwsem back to cgroup_mutex remains. Fixes: a5bd6ba30b33 ("sched_ext: Use cgroup_lock/unlock() to synchronize against cgroup operations") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 46f135bddd46..58898cd0727b 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -4580,20 +4580,25 @@ static struct cgroup *root_cgroup(void) return &cgrp_dfl_root.cgrp; } +/* + * cgroup_lock() must nest outside the rwsem write side: a writer waiting + * for cgroup_mutex deadlocks with cgroup teardown, which holds it while + * draining a set_* file write blocked on the rwsem behind the writer. + */ static void scx_cgroup_lock(void) { + cgroup_lock(); #ifdef CONFIG_EXT_GROUP_SCHED percpu_down_write(&scx_cgroup_ops_rwsem); #endif - cgroup_lock(); } static void scx_cgroup_unlock(void) { - cgroup_unlock(); #ifdef CONFIG_EXT_GROUP_SCHED percpu_up_write(&scx_cgroup_ops_rwsem); #endif + cgroup_unlock(); } #else /* CONFIG_EXT_GROUP_SCHED || CONFIG_EXT_SUB_SCHED */ static inline struct cgroup *root_cgroup(void) { return NULL; } -- cgit v1.2.3 From 8c13364db9c9a43ed286f3a8d0fb9477b1adc43c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Jul 2026 10:45:23 -1000 Subject: sched_ext: Skip sub-disable teardown for never-linked sub-schedulers A sub-scheduler enable can fail before scx_link_sched() links the sched into the hierarchy, e.g. when the parent is already being disabled, and cleanup still runs the full scx_sub_disable(). That is racy against root disable: drain_descendants() is the only ordering between a sub's disable-time task walk and root disable's all-task teardown, and an unlinked sub is invisible to it. Root's teardown can thus run between the never-linked sub's drain and its walk, exiting every task to no scheduler. The walk then trips the membership WARN and re-homes the exited tasks onto the dying hierarchy, a use-after-free. Skip the cgroup ownership reset and the task walk if @sch was never linked, indicated by the empty ->sibling as unlinking only happens later in the same function. The membership WARN remains valid: a linked sub is always waited on by an ancestor's drain. Fixes: 337ec00b1d9c ("sched_ext: Implement cgroup sub-sched enabling and disabling") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 58898cd0727b..98dd7df88db4 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -5937,6 +5937,15 @@ static void scx_sub_disable(struct scx_sched *sch) percpu_down_write(&scx_fork_rwsem); scx_cgroup_lock(); + /* + * An enable that failed before scx_link_sched() never owned a cgroup or + * task and won't be waited on by an ancestor's drain_descendants(). + * Nothing to reparent and walking the tasks can misbehave as the task + * ownership invariant (either owned by self or parent) does not hold. + */ + if (list_empty(&sch->sibling)) + goto dump; + set_cgroup_sched(sch_cgroup(sch), parent); scx_task_iter_start(&sti, sch->cgrp); @@ -5949,8 +5958,8 @@ static void scx_sub_disable(struct scx_sched *sch) continue; /* - * By the time control reaches here, all descendant schedulers - * should already have been disabled. + * By the time control reaches here, all linked descendant + * schedulers should have been disabled. */ WARN_ON_ONCE(!scx_task_on_sched(sch, p)); @@ -6017,6 +6026,7 @@ static void scx_sub_disable(struct scx_sched *sch) } scx_task_iter_stop(&sti); +dump: scx_disable_dump(sch); scx_cgroup_unlock(); -- cgit v1.2.3 From 5cdc928598095b2c7d5f265e5f21eadd1634bfbe Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Jul 2026 10:46:04 -1000 Subject: sched_ext: Don't enable non-ext tasks in the sub-sched task loops Root enable and scx_post_fork() enable a task only if it's on the ext class. Tasks on other classes, possible under an SCX_OPS_SWITCH_PARTIAL root, are left READY and enabled by switching_to_scx() when they switch over. The sub enable-commit pass and the sub-disable re-home loop enable unconditionally, so a fair-class READY task in the subtree becomes ENABLED while not on sched_ext. A later switch to SCHED_EXT then trips the task state validation WARN (ENABLED with the previous state not READY) and calls ops.enable() a second time. Gate scx_enable_task() on the task's class in both loops. Fixes: 337ec00b1d9c ("sched_ext: Implement cgroup sub-sched enabling and disabling") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 98dd7df88db4..18183062f751 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -6010,15 +6010,22 @@ static void scx_sub_disable(struct scx_sched *sch) /* * $p is initialized for $parent and still attached to * @sch. Disable and exit for @sch, switch over to - * $parent, override the state to READY to account for - * $p having already been initialized, and then enable. + * $parent and override the state to READY to account + * for $p having already been initialized. */ scx_disable_and_exit_task(sch, p); scx_set_task_state(p, SCX_TASK_INIT_BEGIN); scx_set_task_state(p, SCX_TASK_INIT); scx_set_task_sched(p, parent); scx_set_task_state(p, SCX_TASK_READY); - scx_enable_task(parent, p); + + /* + * A task on a non-ext class, possible under an + * %SCX_OPS_SWITCH_PARTIAL root, stays READY and is + * enabled by switching_to_scx() if it switches over. + */ + if (p->sched_class == &ext_sched_class) + scx_enable_task(parent, p); } task_rq_unlock(rq, p, &rf); @@ -7726,10 +7733,14 @@ static void scx_sub_enable_workfn(struct kthread_work *work) /* * $p is now only initialized for @sch and READY, which - * is what we want. Assign it to @sch and enable. + * is what we want. Assign it to @sch and, if it's on + * the ext class, enable. A non-ext task, possible under + * an %SCX_OPS_SWITCH_PARTIAL root, stays READY and is + * enabled by switching_to_scx() if it switches over. */ scx_set_task_sched(p, sch); - scx_enable_task(sch, p); + if (p->sched_class == &ext_sched_class) + scx_enable_task(sch, p); p->scx.flags &= ~SCX_TASK_SUB_INIT; } -- cgit v1.2.3 From 02bbbf05d19f49c5cc9f249dd8844b2a7f2a2b8c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 16 Jul 2026 08:49:06 +0200 Subject: ALSA: hda/realtek: Add quirk for HP Pavilion x360 HP Pavilion x360 sets a bogus PCI SSID (103c:0000), hence the driver picks up a wrong quirk entry, resulting in an almost silent output. And yet, the existing quirk for x390 doesn't seem sufficing, and we need the extra setup for the amp. This patch adds the quirk entry for the codec SSID (103c:8486) to initialize the amp via COEF verbs and chains to the existing quirk for another x360 model to address the silent output. Closes: https://lore.kernel.org/CAF2ktaUW2oaWwGazGtJQ3o1JyE2R4O2xPd-Dchr=qqi7_QRruQ@mail.gmail.com Reviewed-by: Cezary Rojewski Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260716064916.540616-1-tiwai@suse.de --- sound/hda/codecs/realtek/alc269.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 92feb2df0007..6c87e2513fe7 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -4019,6 +4019,7 @@ enum { ALC256_FIXUP_HUAWEI_MACH_WX9_PINS, ALC298_FIXUP_HUAWEI_MBX_STEREO, ALC295_FIXUP_HP_X360, + ALC295_FIXUP_HP_PAVILION_X360, ALC221_FIXUP_HP_HEADSET_MIC, ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, ALC295_FIXUP_HP_AUTO_MUTE, @@ -5343,6 +5344,19 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3 }, + [ALC295_FIXUP_HP_PAVILION_X360] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* force amp gain and processing state */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x7770 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0d }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3000 }, + {} + }, + .chained = true, + .chain_id = ALC295_FIXUP_HP_X360 + }, [ALC221_FIXUP_HP_HEADSET_MIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -7059,6 +7073,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3), + HDA_CODEC_QUIRK(0x103c, 0x8486, "HP Pavilion x360", ALC295_FIXUP_HP_PAVILION_X360), SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), -- cgit v1.2.3 From 880c43b185ca52239e75bc546cc4f4d9154d0fed Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 14 Jul 2026 07:52:35 +0900 Subject: rust: time: fix as_micros_ceil() to round correctly for negative Delta The ceiling-division idiom `(n + d - 1) / d` only produces the correct result when `n` is non-negative. For example, if n = -1000 (exactly -1us), the old code computed (-1000 + 999) / 1000 == 0 instead of -1. For negative n, truncating division already rounds towards positive infinity, so no bias is needed in that case. Fixes: fae0cdc12340 ("rust: time: Introduce Delta type") Signed-off-by: FUJITA Tomonori Acked-by: Andreas Hindborg Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260713225235.3243480-1-tomo@flapping.org Signed-off-by: Miguel Ojeda --- rust/kernel/time.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 363e93cbb139..b8463823aed9 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -441,15 +441,22 @@ impl Delta { /// to the value in the [`Delta`]. #[inline] pub fn as_micros_ceil(self) -> i64 { + let n = self.as_nanos(); + let n = if n >= 0 { + n.saturating_add(NSEC_PER_USEC - 1) + } else { + n + }; + #[cfg(CONFIG_64BIT)] { - self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC + n / NSEC_PER_USEC } #[cfg(not(CONFIG_64BIT))] // SAFETY: It is always safe to call `ktime_to_us()` with any value. unsafe { - bindings::ktime_to_us(self.as_nanos().saturating_add(NSEC_PER_USEC - 1)) + bindings::ktime_to_us(n) } } -- cgit v1.2.3 From acad742714bdc70e7fd7f234323807c596828213 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 17 Jul 2026 17:33:36 +0300 Subject: wifi: iwlwifi: bound aligned TLV advance in FW parser Validate ALIGN(tlv_len, 4) against remaining parser length before consuming bytes from the firmware image. This avoids length underflow on malformed TLVs. Assisted-by: GitHubCopilot:GPT-5.3-Codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717173215.393c286488f9.Ia39144dc3ca334325ee4eacb7420901e2446fc23@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 842586d4fc5c..001895f4f4bd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -804,6 +804,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, u32 build, paging_mem_size; int num_of_cpus; bool usniffer_req = false; + size_t aligned_tlv_len; if (len < sizeof(*ucode)) { IWL_ERR(drv, "uCode has invalid length: %zd\n", len); @@ -852,8 +853,16 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, len, tlv_len); return -EINVAL; } - len -= ALIGN(tlv_len, 4); - data += sizeof(*tlv) + ALIGN(tlv_len, 4); + + aligned_tlv_len = ALIGN(tlv_len, 4); + if (len < aligned_tlv_len) { + IWL_ERR(drv, "invalid aligned TLV len: %zd/%zu\n", + len, aligned_tlv_len); + return -EINVAL; + } + + len -= aligned_tlv_len; + data += sizeof(*tlv) + aligned_tlv_len; switch (tlv_type) { case IWL_UCODE_TLV_INST: -- cgit v1.2.3 From 856ab29632c507d51d7bfe86100389d05d4d9a7f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 17 Jul 2026 17:33:37 +0300 Subject: wifi: iwlwifi: dbg-tlv: bound aligned TLV walk length Validate ALIGN(tlv_len, 4) before advancing through external debug TLVs to prevent parser length underflow. Assisted-by: GitHubCopilot:GPT-5.3-Codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717173215.e08d6550c6ec.Iad64190a7d5cded553aff41973120396aef1b557@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index d021b24d04d6..b1a55909f0d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -475,6 +475,7 @@ static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data, { const struct iwl_ucode_tlv *tlv; u32 tlv_len; + size_t aligned_tlv_len; while (len >= sizeof(*tlv)) { len -= sizeof(*tlv); @@ -487,8 +488,16 @@ static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data, len, tlv_len); return -EINVAL; } - len -= ALIGN(tlv_len, 4); - data += sizeof(*tlv) + ALIGN(tlv_len, 4); + + aligned_tlv_len = ALIGN(tlv_len, 4); + if (len < aligned_tlv_len) { + IWL_ERR(trans, "invalid aligned TLV len: %zd/%zu\n", + len, aligned_tlv_len); + return -EINVAL; + } + + len -= aligned_tlv_len; + data += sizeof(*tlv) + aligned_tlv_len; iwl_dbg_tlv_alloc(trans, tlv, true); } -- cgit v1.2.3 From 954e821f42aaca56073ca830c5fd4bcf1a89048c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 17 Jul 2026 17:33:38 +0300 Subject: wifi: iwlwifi: acpi: validate WGDS table revision index Check tbl_rev bounds before BIT(tbl_rev) to avoid undefined shifts when firmware reports an invalid revision value. Assisted-by: GitHubCopilot:GPT-5.3-Codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717173215.52a01f841f2a.Ic0131eaac31d9ff71b169138d9b0865cb39b44a9@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index bf0f851a9075..430a7642e01a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -865,6 +865,11 @@ int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) min_size, max_size, &tbl_rev); if (!IS_ERR(wifi_pkg)) { + if (tbl_rev < 0 || + tbl_rev >= BITS_PER_BYTE * + sizeof(rev_data[idx].revisions)) + continue; + if (!(BIT(tbl_rev) & rev_data[idx].revisions)) continue; -- cgit v1.2.3 From 9ba1e0a4060e5a4802ccb9c9defa946c1aa6c629 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 17 Jul 2026 17:33:39 +0300 Subject: wifi: iwlwifi: uefi: bound PPAG revision bitmap shift Validate revision is below 32 before BIT(revision) in PPAG parsing. Assisted-by: GitHubCopilot:GPT-5.3-Codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717173215.d116dd2efdc1.I3c6cae5cb9d0acc2d94544bc755b0754a91b10ba@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 2ef0a7a920ad..014f22c42f41 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -700,7 +700,7 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) return -EINVAL; parse_table: - if (!(BIT(data->revision) & valid_rev)) { + if (data->revision >= 32 || !(BIT(data->revision) & valid_rev)) { ret = -EINVAL; IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n", -- cgit v1.2.3 From d13d5d299c11b7bd3362d5692c56225d9e176664 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 17 Jul 2026 17:33:40 +0300 Subject: wifi: iwlwifi: validate SEC_RT TLV minimum size Reject firmware section TLVs that are shorter than the offset field before subtracting sizeof(offset) from the section size. This prevents size underflow for malformed TLVs. Assisted-by: GitHubCopilot:GPT-5.3-Codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260717173215.17b040b27edc.I6b32d1e9ad707417e2e604f08a63582456209372@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 001895f4f4bd..7f1515dfc5bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -492,7 +492,7 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces, * Gets uCode section from tlv. */ static int iwl_store_ucode_sec(struct fw_img_parsing *img, - const void *data, int size) + const void *data, size_t size) { struct fw_sec *sec; const struct fw_sec_parsing *sec_parse; @@ -501,6 +501,9 @@ static int iwl_store_ucode_sec(struct fw_img_parsing *img, if (WARN_ON(!img || !data)) return -EINVAL; + if (size < sizeof(sec_parse->offset)) + return -EINVAL; + sec_parse = (const struct fw_sec_parsing *)data; alloc_size = sizeof(*img->sec) * (img->sec_counter + 1); -- cgit v1.2.3 From af01dab0c39a7aefc47a109201e4134ea6bd3005 Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Thu, 16 Jul 2026 16:25:11 +0800 Subject: hwmon: (pmbus/max34440) block unsupported VIN and IIN limit registers MAX34451 and ADPM chips do not support standard PMBus VIN/IIN limit registers, manufacturer specific min/max registers, or undercurrent or undertemperature fault limits. STATUS_BYTE and STATUS_OTHER are also not available. Accessing these non-existent registers during driver initialization triggers a CML error and asserts ALERT. Handled by blocking these functions during read/write. Fixes: 7a001dbab4ad ("hwmon: (pmbus/max34440) Add support for MAX34451.") Fixes: 629cf8f6c23a ("hwmon: (pmbus/max34440) Add support for ADPM12160") Fixes: 2e0b52f1ae88 ("hwmon: (pmbus/max34440): add support adpm12200") Fixes: 479bfeba2eb6 ("hwmon: (pmbus/max34440): add support adpm12250") Signed-off-by: Alexis Czezar Torreno Link: https://lore.kernel.org/r/20260716-max34451_fixes-v1-1-a941b27eaecb@analog.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/max34440.c | 80 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index 74876d2207fb..e56057e9273c 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -88,6 +88,33 @@ static int max34440_read_word_data(struct i2c_client *client, int page, ret = pmbus_read_word_data(client, page, phase, data->iout_oc_warn_limit); break; + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_MFR_VIN_MIN: + case PMBUS_MFR_VIN_MAX: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_MFR_IIN_MAX: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_MFR_MAX_TEMP_1: + /* + * MAX34451/ADPM family do not support VIN/IIN limit registers, + * manufacturer-specific min/max registers, or undercurrent/ + * undertemperature fault limits. Accessing these triggers CML + * error and asserts ALERT. + */ + if (data->id == max34451 || data->id == adpm12160 || + data->id == adpm12200 || data->id == adpm12250) + return -ENXIO; + ret = -ENODATA; + break; case PMBUS_VIRT_READ_VOUT_MIN: ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_VOUT_MIN); @@ -244,6 +271,51 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) return ret; } +static int max34451_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max34440_data *data = to_max34440_data(info); + + switch (reg) { + case PMBUS_STATUS_BYTE: + case PMBUS_STATUS_OTHER: + /* + * MAX34451/ADPM family do not support STATUS_BYTE or + * STATUS_OTHER registers. Accessing them triggers CML + * error and asserts ALERT. + */ + if (data->id == max34451 || data->id == adpm12160 || + data->id == adpm12200 || data->id == adpm12250) + return -ENXIO; + return -ENODATA; + default: + return -ENODATA; + } +} + +static int max34451_write_byte_data(struct i2c_client *client, int page, + int reg, u8 byte) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max34440_data *data = to_max34440_data(info); + + switch (reg) { + case PMBUS_STATUS_BYTE: + case PMBUS_STATUS_OTHER: + /* + * MAX34451/ADPM family do not support STATUS_BYTE or + * STATUS_OTHER registers. Writing to them triggers CML + * error and asserts ALERT. + */ + if (data->id == max34451 || data->id == adpm12160 || + data->id == adpm12200 || data->id == adpm12250) + return -ENXIO; + return -ENODATA; + default: + return -ENODATA; + } +} + static int max34451_set_supported_funcs(struct i2c_client *client, struct max34440_data *data) { @@ -363,7 +435,9 @@ static struct pmbus_driver_info max34440_info[] = { .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT, .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34451_read_byte_data, .read_word_data = max34440_read_word_data, + .write_byte_data = max34451_write_byte_data, .write_word_data = max34440_write_word_data, }, [adpm12200] = { @@ -399,7 +473,9 @@ static struct pmbus_driver_info max34440_info[] = { .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, .func[14] = PMBUS_HAVE_IOUT, .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34451_read_byte_data, .read_word_data = max34440_read_word_data, + .write_byte_data = max34451_write_byte_data, .write_word_data = max34440_write_word_data, }, [adpm12250] = { @@ -433,7 +509,9 @@ static struct pmbus_driver_info max34440_info[] = { .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, .func[14] = PMBUS_HAVE_IOUT, .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34451_read_byte_data, .read_word_data = max34440_read_word_data, + .write_byte_data = max34451_write_byte_data, .write_word_data = max34440_write_word_data, }, [max34440] = { @@ -581,7 +659,9 @@ static struct pmbus_driver_info max34440_info[] = { .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34451_read_byte_data, .read_word_data = max34440_read_word_data, + .write_byte_data = max34451_write_byte_data, .write_word_data = max34440_write_word_data, .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, -- cgit v1.2.3 From e741d13cc2abfc6fccebe2008057aa52e285223e Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sat, 11 Jul 2026 09:42:07 +0200 Subject: hwmon: (asus-ec-sensors) fix looping over banks while reading from EC Do not assume there are only bank 0 and bank 1 available, just use '!=' for bank comparison. Fixes: d0ddfd241e57 ("hwmon: (asus-ec-sensors) add driver for ASUS EC") Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260711074217.554656-1-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/asus-ec-sensors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 29a23484cbe7..4de6045d1b68 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -1189,7 +1189,7 @@ static int asus_ec_block_read(const struct device *dev, } for (ireg = 0; ireg < ec->nr_registers; ireg++) { reg_bank = register_bank(ec->registers[ireg]); - if (reg_bank < bank) { + if (reg_bank != bank) { continue; } ec_read(register_index(ec->registers[ireg]), -- cgit v1.2.3 From 60710b2af13b81da71b429d3f8b19dd70310729d Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sun, 12 Jul 2026 13:05:03 +0200 Subject: hwmon: (asus-ec-sensors) fix EC read intervals Take INITIAL_JIFFIES into account when setting up next update time. Fixes: d0ddfd241e57 ("hwmon: (asus-ec-sensors) add driver for ASUS EC") Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260712110650.1240071-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/asus-ec-sensors.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 4de6045d1b68..f8756c564aa1 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -1009,7 +1009,7 @@ struct ec_sensors_data { /* sorted list of unique register banks */ u8 banks[ASUS_EC_MAX_BANK + 1]; /* in jiffies */ - unsigned long last_updated; + u64 next_update; struct lock_data lock_data; /* number of board EC sensors */ u8 nr_sensors; @@ -1278,13 +1278,12 @@ static int get_cached_value_or_update(const struct device *dev, int sensor_index, struct ec_sensors_data *state, s32 *value) { - if (time_after(jiffies, state->last_updated + HZ)) { + if (time_after64(get_jiffies_64(), state->next_update)) { if (update_ec_sensors(dev, state)) { dev_err(dev, "update_ec_sensors() failure\n"); return -EIO; } - - state->last_updated = jiffies; + state->next_update = get_jiffies_64() + HZ; } *value = state->sensors[sensor_index].cached_value; @@ -1402,6 +1401,7 @@ static int asus_ec_probe(struct platform_device *pdev) if (!ec_data) return -ENOMEM; + ec_data->next_update = INITIAL_JIFFIES; dev_set_drvdata(dev, ec_data); ec_data->board_info = pboard_info; -- cgit v1.2.3 From 9813c1f49efeadbcb17e4a41972350ac783f9cac Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sun, 12 Jul 2026 15:05:05 +0200 Subject: hwmon: (asus-ec-sensors) add missed handle for ENOMEM Add missing return value check in the setup function. Fixes: d0ddfd241e57 ("hwmon: (asus-ec-sensors) add driver for ASUS EC") Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260712130602.1256700-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/asus-ec-sensors.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index f8756c564aa1..01a022b3ee82 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -1495,9 +1495,11 @@ static int asus_ec_probe(struct platform_device *pdev) if (!nr_count[type]) continue; - asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev, - nr_count[type], type, - hwmon_attributes[type]); + status = asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev, + nr_count[type], type, + hwmon_attributes[type]); + if (status) + return status; *ptr_asus_ec_ci++ = asus_ec_hwmon_chan++; } -- cgit v1.2.3 From 432a9b2780c0a01caf547bd1fc2fcf28aeb8d173 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 19 Jul 2026 08:45:40 -0500 Subject: ublk: wait on ublk_dev_ready() instead of ub->completion ub->completion is only re-armed by a successful START_USER_RECOVERY. If the ublk server sends END_USER_RECOVERY without one - e.g. its START failed with -EBUSY and the error was ignored - the wait is satisfied by the stale completion of the previous recovery cycle, and the device is marked LIVE and the requeue list kicked while the FETCH stream is still running and ubq->canceling is still set. The kick redispatches a previously requeued request, __ublk_queue_rq_common() sees ->canceling and parks it again via __ublk_abort_rq(), and after the last FETCH clears ->canceling nothing ever kicks the requeue list again: the request is stranded there while holding its tag. If it is the flush machinery's flush_rq, every subsequent fsync piles up in uninterruptible sleep and teardown hangs on tag draining. This matches a report of a lost PREFLUSH with ext4 on top of ublk after daemon crash recovery. ub->completion is an edge-triggered latch used as a proxy for the level condition "every queue has fetched all I/O commands", which can regress (F_BATCH's UNPREP, daemon death) and whose re-arm can be skipped. Drop it and wait on the real condition instead: the new helper ublk_wait_dev_ready_and_lock() waits on ublk_dev_ready() via wait_var_event_interruptible(), woken from ublk_mark_io_ready(), then re-checks it under ub->mutex, waiting again on regression, and returns with the mutex held and readiness guaranteed. Readiness becomes true in the same ub->mutex critical section that clears the last queue's ->canceling, so END_USER_RECOVERY marks the device LIVE and kicks the requeue list strictly after ->canceling clears. The wait stays interruptible, so a server whose daemon died can still be signalled out. For ublk_ctrl_start_dev() this replaces the fail-fast -EINVAL on an F_BATCH ready->UNPREP regression with waiting until the device is ready again. Reported-by: George Salisbury Fixes: 728cbac5fe21 ("ublk: move device reset into ublk_ch_release()") Cc: stable@vger.kernel.org Signed-off-by: Ming Lei Link: https://patch.msgid.link/20260719134540.120269-1-tom.leiming@gmail.com Signed-off-by: Jens Axboe --- drivers/block/ublk_drv.c | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index c2c11f2a01e7..4ca6ec738c93 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -327,7 +327,6 @@ struct ublk_device { struct ublk_params params; - struct completion completion; u32 nr_queue_ready; bool unprivileged_daemons; struct mutex cancel_mutex; @@ -3054,12 +3053,12 @@ static void ublk_mark_io_ready(struct ublk_device *ub, u16 q_id, if (ublk_dev_ready(ub)) { /* * All queues ready - clear device-level canceling flag - * and complete the recovery/initialization. + * and wake ublk_dev_ready() waiters. */ mutex_lock(&ub->cancel_mutex); ub->canceling = false; mutex_unlock(&ub->cancel_mutex); - complete_all(&ub->completion); + wake_up_var(&ub->nr_queue_ready); } } @@ -4273,7 +4272,6 @@ static int ublk_init_queues(struct ublk_device *ub) goto fail; } - init_completion(&ub->completion); return 0; fail: @@ -4417,6 +4415,26 @@ static bool ublk_validate_user_pid(struct ublk_device *ub, pid_t ublksrv_pid) return ub->ublksrv_tgid == ublksrv_pid; } +/* + * Wait until all queues have fetched their I/O commands, and return with + * ub->mutex held and readiness guaranteed: then every queue's ->canceling + * is cleared. Ready may regress between wakeup and mutex_lock() (F_BATCH + * UNPREP, daemon death), so re-check it under the mutex and wait again. + */ +static int ublk_wait_dev_ready_and_lock(struct ublk_device *ub) +{ + while (true) { + if (wait_var_event_interruptible(&ub->nr_queue_ready, + ublk_dev_ready(ub))) + return -EINTR; + + mutex_lock(&ub->mutex); + if (ublk_dev_ready(ub)) + return 0; + mutex_unlock(&ub->mutex); + } +} + static int ublk_ctrl_start_dev(struct ublk_device *ub, const struct ublksrv_ctrl_cmd *header) { @@ -4499,15 +4517,10 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub, }; } - if (wait_for_completion_interruptible(&ub->completion) != 0) + if (ublk_wait_dev_ready_and_lock(ub)) return -EINTR; - if (!ublk_validate_user_pid(ub, ublksrv_pid)) - return -EINVAL; - - mutex_lock(&ub->mutex); - /* device may become not ready in case of F_BATCH */ - if (!ublk_dev_ready(ub)) { + if (!ublk_validate_user_pid(ub, ublksrv_pid)) { ret = -EINVAL; goto out_unlock; } @@ -5071,7 +5084,6 @@ static int ublk_ctrl_start_recovery(struct ublk_device *ub) goto out_unlock; } pr_devel("%s: start recovery for dev id %d\n", __func__, ub->ub_number); - init_completion(&ub->completion); ret = 0; out_unlock: mutex_unlock(&ub->mutex); @@ -5087,16 +5099,17 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub, pr_devel("%s: Waiting for all FETCH_REQs, dev id %d...\n", __func__, header->dev_id); - if (wait_for_completion_interruptible(&ub->completion)) + if (ublk_wait_dev_ready_and_lock(ub)) return -EINTR; pr_devel("%s: All FETCH_REQs received, dev id %d\n", __func__, header->dev_id); - if (!ublk_validate_user_pid(ub, ublksrv_pid)) - return -EINVAL; + if (!ublk_validate_user_pid(ub, ublksrv_pid)) { + ret = -EINVAL; + goto out_unlock; + } - mutex_lock(&ub->mutex); if (ublk_nosrv_should_stop_dev(ub)) goto out_unlock; -- cgit v1.2.3 From f6d6a4147ace0c417035f65b021027c209c75190 Mon Sep 17 00:00:00 2001 From: "Daniel C. Ribeiro" Date: Sun, 19 Jul 2026 06:00:37 -0300 Subject: ALSA: usb-audio: Add FIXED_RATE quirk for JBL Quantum650 Wireless JBL Quantum650 Wireless (0ecb:2125) requires the same workaround that was used for JBL Quantum610 and Quantum810 for limiting the sample rate. Without it, the capture (microphone) stream fails to work. Setting the QUIRK_FLAG_FIXED_RATE flag, as done for the sibling models, makes both playback and capture work correctly. Signed-off-by: Daniel C. Ribeiro Link: https://patch.msgid.link/20260719090037.40149-1-dcoutinho.96@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 41149561aa06..0de7c9d94259 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2346,6 +2346,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ QUIRK_FLAG_FIXED_RATE), + DEVICE_FLG(0x0ecb, 0x2125, /* JBL Quantum650 Wireless */ + QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0fd9, 0x0008, /* Hauppauge HVR-950Q */ -- cgit v1.2.3 From 1590cf0329716306e948a8fc29f1d3ee87d3989f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 19 Jul 2026 13:54:41 -0700 Subject: Linux 7.2-rc4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bfb47ad8cb9a..11539c3fd405 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc4 NAME = Baby Opossum Posse # *DOCUMENTATION* -- cgit v1.2.3 From 8e957e4907c58e9ca944f98799524f2bbb9cf68a Mon Sep 17 00:00:00 2001 From: Haidar Lee Date: Wed, 15 Jul 2026 14:04:40 +0800 Subject: ASoC: tas2562: fix DVC coefficient write order The TAS2562 applies the 32-bit digital volume coefficient to the playback path when the last byte, DVC_CFG4 (book 0 page 2 reg 0x0F), is written. tas2562_volume_control_put() wrote DVC_CFG4 first and DVC_CFG1 (the MSB) last, so every volume change latched a value made of the previous coefficient's upper three bytes combined with the new LSB; the remaining bytes only took effect on the next volume change. In practice the control was unusable: the first setting after power-on always played at roughly 0 dB no matter what value was requested (the chip's default upper bytes were still latched), and most subsequent changes muted the output entirely or produced a distorted, over-unity gain. Verified on a TAS2562 (ADLINK OSM-520 / MT8189 board) by tracing the I2C writes with ftrace and by writing the same coefficients manually in both byte orders: written MSB-first the register block behaves exactly as the driver expects, LSB-first reproduces the broken behaviour. Write the bytes MSB first with DVC_CFG4 last so the complete new coefficient is latched atomically. Fixes: bf726b1c86f2 ("ASoC: tas2562: Add support for digital volume control") Cc: stable@vger.kernel.org Signed-off-by: Haidar Lee Link: https://patch.msgid.link/20260715-tas2562-dvc-fix-v1-1-072b13901b20@adlinktech.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index e1d62f30418a..ec32ef0afd7e 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -475,20 +475,27 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, u32 reg_val; reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, - (reg_val & 0xff)); - if (ret) - return ret; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, - ((reg_val >> 8) & 0xff)); + /* + * The device applies the 32-bit coefficient to the playback path on + * the write to DVC_CFG4 (the LSB, book 0 page 2 reg 0x0F), so the + * bytes must be written MSB first and DVC_CFG4 last. Writing CFG4 + * first latches a mix of the previous coefficient's upper bytes and + * the new LSB instead of the requested value. + */ + ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, + ((reg_val >> 24) & 0xff)); if (ret) return ret; ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, ((reg_val >> 16) & 0xff)); if (ret) return ret; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, - ((reg_val >> 24) & 0xff)); + ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, + ((reg_val >> 8) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, + (reg_val & 0xff)); if (ret) return ret; -- cgit v1.2.3 From bdb0fd6de403fcea7b85dc9d38f0a571583ebe80 Mon Sep 17 00:00:00 2001 From: Haidar Lee Date: Wed, 15 Jul 2026 14:04:41 +0800 Subject: ASoC: tas2562: fix broken entries in the volume lookup table The float_vol_db_lookup table is supposed to hold round(10^(dB/20) * 2^30) for every 2 dB step from -110 dB to 0 dB, which is 56 entries, but it only has 55: the -90 dB entry duplicates the -92 dB value (0x0000695b) and the -20 dB entry (0x06666666) is missing altogether. As a result every step between -90 dB and -22 dB is off by 2 dB, and the control's maximum raw value of 110 indexes one element past the end of the array. Replace the duplicated -90 dB entry with the correct value 0x000084a3 and add the missing -20 dB entry, bringing the table to the full 56 entries so index 55 (raw value 110, 0 dB) is in range again. Fixes: bf726b1c86f2 ("ASoC: tas2562: Add support for digital volume control") Cc: stable@vger.kernel.org Signed-off-by: Haidar Lee Link: https://patch.msgid.link/20260715-tas2562-dvc-fix-v1-2-072b13901b20@adlinktech.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index ec32ef0afd7e..cdd695c3807e 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -32,15 +32,16 @@ static const unsigned int float_vol_db_lookup[] = { 0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151, 0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b, -0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, +0x000084a3, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, 0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f, 0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7, 0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d, 0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a, 0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27, 0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68, -0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362, -0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000 +0x06666666, 0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, +0x143d1362, 0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, +0x40000000 }; struct tas2562_data { -- cgit v1.2.3 From 8fb41964f7e4e4207c8999af2056894caa7a252a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 15 Jul 2026 21:18:09 +0100 Subject: ASoC: tas2562: Validate values for volume writes tas2562_volume_control_put() does not do any validation of the control value written by userspace, it uses it to look up a value in a fixed size array which can easily be overflowed and then writes whatever value it gets back to the device. Add validation that we are loading a value we have in the array. Cc: stable@vger.kernel.org Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-asoc-tas2562-put-retval-v1-1-97bf467c924e@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index e1d62f30418a..40b7803c68be 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -471,10 +471,14 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); - int ret; + int ret, index; u32 reg_val; - reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; + index = ucontrol->value.integer.value[0] / 2; + if (index < 0 || index >= ARRAY_SIZE(float_vol_db_lookup)) + return -EINVAL; + + reg_val = float_vol_db_lookup[index]; ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, (reg_val & 0xff)); if (ret) -- cgit v1.2.3 From 9a9269dbbf1aba4bf329f770d6c473dc9cfb29e8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 15 Jul 2026 21:18:10 +0100 Subject: ASoC: tas2562: Fix event generation for volume control ALSA put() operations should return 0 for noop updates and 1 if the value of the control changed, this is used by the ALSA core to generate events to userspace. tas2562_volume_control_put() does not implement this, it just writes whatever value userspace wrote to the device and returns 0 regardless of what the previous value was. Fix this by suppressing writes if the value is unchanged and returning 1 if the writes succeed. Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-asoc-tas2562-put-retval-v1-2-97bf467c924e@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 40b7803c68be..d3d0aaa0f607 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -474,6 +474,9 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, int ret, index; u32 reg_val; + if (tas2562->volume_lvl == ucontrol->value.integer.value[0]) + return 0; + index = ucontrol->value.integer.value[0] / 2; if (index < 0 || index >= ARRAY_SIZE(float_vol_db_lookup)) return -EINVAL; @@ -498,7 +501,7 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, tas2562->volume_lvl = ucontrol->value.integer.value[0]; - return 0; + return 1; } /* Digital Volume Control. From 0 dB to -110 dB in 1 dB steps */ -- cgit v1.2.3 From 58b638e7c4559b35367a25e319adfe91e8f5ebe7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 15 Jul 2026 21:18:11 +0100 Subject: ASoC: tas2562: Fix default digital volume The tas2562 digital volume is spread over four registers and is implemented as lookups into a table so the driver stores the value for the userspace control in the driver data. This defaults to 0 due to kzalloc() but the register default is 0x40400000 which maps onto something a bit over the largest value defined in the lookup table. While it's not an exact match update the default to the largest value, avoiding user surprise due to a sudden change on first write. Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-asoc-tas2562-put-retval-v1-3-97bf467c924e@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index d3d0aaa0f607..e5fba2195a24 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -739,6 +739,8 @@ static int tas2562_probe(struct i2c_client *client) data->client = client; data->dev = &client->dev; data->model_id = (uintptr_t)i2c_get_match_data(client); + /* Register default is 0x40400000, this is closest */ + data->volume_lvl = (ARRAY_SIZE(float_vol_db_lookup) - 1) * 2; tas2562_parse_dt(data); -- cgit v1.2.3 From 8e29ed571788f41d1121e1e515ba8745695a38dc Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Sat, 18 Jul 2026 16:09:49 +0800 Subject: ASoC: amd: yc: Add DMI quirk for ASUS EXPERTBOOK PM1403CDA Add a DMI quirk for the Asus Vivobook Pro 15 M6500RE fixing the issue where the internal microphone was not detected. https://bugzilla.kernel.org/show_bug.cgi?id=220806 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260718080949.157230-1-zhangheng@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index af1dce9c9b8a..2f54d4e30539 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -829,6 +829,14 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "PM1403CDA"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6500RE_M6500RE"), + } + }, + {} }; -- cgit v1.2.3 From a88df1d92fcf23d848e5fa6d7d2a1c38fd0174ed Mon Sep 17 00:00:00 2001 From: Zhengyu He Date: Wed, 15 Jul 2026 20:52:15 +0800 Subject: spi: spacemit: Correct TX FIFO slot calculation In k1_spi_write, the count variable is intended to represent the number of slots available for writing into the TX FIFO. The current implementation uses FIELD_GET(SSP_STATUS_TFL, val) in an attempt to determine this count, but this register field returns the number of occupied slots, not the available space. The previous implementation attempted to handle this via a ternary operator (? : K1_SPI_FIFO_SIZE), which incorrectly assumed that the hardware returned 0 when the FIFO was empty (meaning all slots were available), leading to incorrect accounting of the buffer space. Fix this by calculating the free slots: count = K1_SPI_FIFO_SIZE - FIELD_GET(SSP_STATUS_TFL, val); The associated comment has been updated to reflect the logic change: The old comment reflected an incorrect assumption about the hardware behavior, which was the root cause of the previous buggy logic. This patch accurately and concisely describes the purpose of the new calculation. Signed-off-by: Peixin Xie Signed-off-by: Zhengyu He Link: https://patch.msgid.link/20260715-k1-spi-tx-fifo-fix-v1-for-next-v1-1-02024223b08a@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-spacemit-k1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c index 215fe66d27b4..0faf7ffed676 100644 --- a/drivers/spi/spi-spacemit-k1.c +++ b/drivers/spi/spi-spacemit-k1.c @@ -494,8 +494,8 @@ static bool k1_spi_write(struct k1_spi_driver_data *drv_data, u32 val) { unsigned int count; - /* Get the number of open slots in the FIFO; zero means all */ - count = FIELD_GET(SSP_STATUS_TFL, val) ? : K1_SPI_FIFO_SIZE; + /* Get the number of free slots in the FIFO */ + count = K1_SPI_FIFO_SIZE - FIELD_GET(SSP_STATUS_TFL, val); /* * Limit how much we try to send at a time, to reduce the -- cgit v1.2.3 From 890b4253134f3a39883af7d5bea67af9c494c56d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Jul 2026 10:47:57 +0800 Subject: ASoC: fsl_asrc: fix m2m_init error path to use goto instead of bare return When fsl_asrc_m2m_init() fails in fsl_asrc_probe(), the code did a bare return ret, bypassing pm_runtime_disable() in err_pm_get_sync. Use goto err_pm_get_sync to ensure proper cleanup on failure. Fixes: 286d658477a4 ("ASoC: fsl_asrc: register m2m platform device") Cc: stable@vger.kernel.org Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260715024758.1252801-2-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 5fda9b647c70..f23c21032287 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1419,7 +1419,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) ret = fsl_asrc_m2m_init(asrc); if (ret) { dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); - return ret; + goto err_pm_get_sync; } return 0; -- cgit v1.2.3 From a54bc0eef90ea760039c14bb7f3b5db42529f84d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Jul 2026 10:47:58 +0800 Subject: ASoC: fsl_easrc: fix m2m_init error path to use goto instead of bare return When fsl_asrc_m2m_init() fails in fsl_easrc_probe(), the code did a bare return ret, bypassing pm_runtime_disable() in err_pm_disable. Use goto err_pm_disable to ensure proper cleanup on failure. Fixes: b62eaff0650d ("ASoC: fsl_easrc: register m2m platform device") Cc: stable@vger.kernel.org Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260715024758.1252801-3-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_easrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 114a6c0b6b73..8535ef844ce0 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -2269,7 +2269,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) ret = fsl_asrc_m2m_init(easrc); if (ret) { dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); - return ret; + goto err_pm_disable; } return 0; -- cgit v1.2.3 From 41e116ad01d8a704883187743b52d57e11bc3ef0 Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Sun, 19 Jul 2026 23:11:11 +1000 Subject: m68k: coldfire: fix breakage of missed IO access update Fix the last remaining breakage caused by missing a SoC IO access update. Commit e1f3a00670d1 ("m68k: coldfire: use ColdFire specifc IO access in SoC code") missed this read16() call which should be mcf_read16(). 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/202607180731.U4tiwFcQ-lkp@intel.com/ Signed-off-by: Greg Ungerer --- arch/m68k/coldfire/m528x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/m68k/coldfire/m528x.c b/arch/m68k/coldfire/m528x.c index 3383b1ba106a..f9874bba9e45 100644 --- a/arch/m68k/coldfire/m528x.c +++ b/arch/m68k/coldfire/m528x.c @@ -110,7 +110,7 @@ void wildfiremod_halt(void) printk(KERN_INFO "WildFireMod hibernating...\n"); /* Set portE.5 to Digital IO */ - mcf_write16(read16(MCFGPIO_PEPAR) & ~(1 << (5 * 2)), MCFGPIO_PEPAR); + mcf_write16(mcf_read16(MCFGPIO_PEPAR) & ~(1 << (5 * 2)), MCFGPIO_PEPAR); /* Make portE.5 an output */ mcf_write8(mcf_read8(MCFGPIO_PDDR_E) | (1 << 5), MCFGPIO_PDDR_E); -- cgit v1.2.3 From 488f4902e1deba4d507b4b8c25547a366f5dac63 Mon Sep 17 00:00:00 2001 From: Wendy Liang Date: Sat, 18 Jul 2026 01:34:09 -0700 Subject: accel/amdxdna: Fix command timeout race When two commands enter aie2_sched_job_timedout() concurrently, both check the timeout detection state. The first scheduler thread observes tdr_status as SIGNALED and updates it to WAIT. The second thread then observes the updated state instead of the original SIGNALED state, which may cause the command timeout to be handled incorrectly. Replace tdr_status with last_signal_ts, which records the timestamp of the last driver signal. Timeout detection now only reads last_signal_ts and never modifies it, allowing multiple serialized detect() calls under dev_lock to evaluate the same signal timestamp independently. If there is not any new job scheduled or completed within tdr_timeout_ms, the command will timeout. Fixes: 9022f010977f ("accel/amdxdna: Check for device hang on job timeout") Signed-off-by: Wendy Liang Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260718083409.1825940-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 26 ++++++++++++++++++-------- drivers/accel/amdxdna/aie2_pci.c | 1 + drivers/accel/amdxdna/aie2_pci.h | 7 +------ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 101f324ee178..4b3a62aa8798 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -43,20 +43,22 @@ struct aie2_ctx_health { static inline void aie2_tdr_signal(struct amdxdna_dev *xdna) { - WRITE_ONCE(xdna->dev_handle->tdr_status, AIE2_TDR_SIGNALED); + WRITE_ONCE(xdna->dev_handle->last_signal_ts, jiffies); } static bool aie2_tdr_detect(struct amdxdna_dev *xdna) { struct amdxdna_dev_hdl *ndev = xdna->dev_handle; + unsigned long last = READ_ONCE(ndev->last_signal_ts); - if (READ_ONCE(ndev->tdr_status) == AIE2_TDR_WAIT) { - XDNA_ERR(xdna, "TDR timeout detected"); - return true; - } + if (!tdr_timeout_ms) + return false; + + if (!time_after(jiffies, last + msecs_to_jiffies(tdr_timeout_ms))) + return false; - WRITE_ONCE(ndev->tdr_status, AIE2_TDR_WAIT); - return false; + XDNA_ERR(xdna, "TDR timeout detected"); + return true; } static void aie2_cmd_release(struct kref *ref) @@ -434,6 +436,12 @@ out: mmput(job->mm); fence = ERR_PTR(ret); } else { + /* + * Command is successfully posted to hardware, update the + * tdr timestamp. The total pending commands are limited. + * So there will not be a case that driver keeps posting + * commands without getting any hardware respond. + */ aie2_tdr_signal(hwctx->client->xdna); } trace_xdna_job(sched_job, hwctx->name, "sent to device", @@ -658,7 +666,9 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) const struct drm_sched_init_args args = { .ops = &sched_ops, .credit_limit = HWCTX_MAX_CMDS, - .timeout = msecs_to_jiffies(tdr_timeout_ms), + .timeout = tdr_timeout_ms ? + msecs_to_jiffies(tdr_timeout_ms) : + MAX_SCHEDULE_TIMEOUT, .name = "amdxdna_js", .dev = xdna->ddev.dev, }; diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index 22f66c7f534d..daec1f6b4907 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -420,6 +420,7 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) goto stop_fw; } + WRITE_ONCE(ndev->last_signal_ts, jiffies); ndev->dev_status = AIE2_DEV_START; return 0; diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h index 77648cc548b6..ea1dac106400 100644 --- a/drivers/accel/amdxdna/aie2_pci.h +++ b/drivers/accel/amdxdna/aie2_pci.h @@ -143,11 +143,6 @@ struct aie2_exec_msg_ops { u32 (*get_chain_msg_op)(u32 cmd_op); }; -enum aie2_tdr_status { - AIE2_TDR_WAIT, - AIE2_TDR_SIGNALED, -}; - struct amdxdna_dev_hdl { struct aie_device aie; const struct amdxdna_dev_priv *priv; @@ -179,7 +174,7 @@ struct amdxdna_dev_hdl { u32 hwctx_num; struct amdxdna_async_error last_async_err; - enum aie2_tdr_status tdr_status; + unsigned long last_signal_ts; }; struct aie2_hw_ops { -- cgit v1.2.3 From f6f5ee2aa33b350c671721b965251c42cebb962e Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Thu, 16 Jul 2026 13:25:23 +0800 Subject: smb: client: validate DFS referral PathConsumed parse_dfs_referrals() validates that the response contains the fixed referral entry array and, on for-next, the per-referral string offsets. However, the response also contains a PathConsumed value that is later used for DFS path parsing. If a malformed response provides a PathConsumed value larger than the search name, later DFS parsing can advance beyond the end of the path. Validate PathConsumed against the search name length before storing it in the parsed referral. Fixes: 4ecce920e13a ("CIFS: move DFS response parsing out of SMB1 code") Reviewed-by: Paulo Alcantara (Red Hat) Signed-off-by: Yichong Chen Signed-off-by: Steve French --- fs/smb/client/misc.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index e4bac2a0b85d..b9c59b2cf76a 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -682,6 +682,8 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, int i, rc = 0; char *data_end; struct dfs_referral_level_3 *ref; + unsigned int path_consumed; + size_t search_name_len; if (rsp_size < sizeof(*rsp)) { cifs_dbg(VFS | ONCE, @@ -728,6 +730,7 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, rc = -ENOMEM; goto parse_DFS_referrals_exit; } + search_name_len = strlen(searchName); /* collect necessary data from referrals */ for (i = 0; i < *num_of_nodes; i++) { @@ -736,21 +739,34 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, struct dfs_info3_param *node = (*target_nodes)+i; node->flags = le32_to_cpu(rsp->DFSFlags); + path_consumed = le16_to_cpu(rsp->PathConsumed); if (is_unicode) { - __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, - GFP_KERNEL); - if (tmp == NULL) { + size_t search_name_utf16_len = search_name_len * 2 + 2; + __le16 *tmp; + + if (path_consumed > search_name_utf16_len) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + tmp = kmalloc(search_name_utf16_len, GFP_KERNEL); + if (!tmp) { rc = -ENOMEM; goto parse_DFS_referrals_exit; } - cifsConvertToUTF16((__le16 *) tmp, searchName, + cifsConvertToUTF16((__le16 *)tmp, searchName, PATH_MAX, nls_codepage, remap); - node->path_consumed = cifs_utf16_bytes(tmp, - le16_to_cpu(rsp->PathConsumed), - nls_codepage); + node->path_consumed = cifs_utf16_bytes(tmp, path_consumed, + nls_codepage); kfree(tmp); - } else - node->path_consumed = le16_to_cpu(rsp->PathConsumed); + } else { + if (path_consumed > search_name_len) { + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + node->path_consumed = path_consumed; + } node->server_type = le16_to_cpu(ref->ServerType); node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); -- cgit v1.2.3 From f8cf09a53a0dc1da298e9dd0ba5f21710cf119d6 Mon Sep 17 00:00:00 2001 From: Jay Vadayath Date: Fri, 17 Jul 2026 17:21:22 -0700 Subject: smb: client: bound dirent name against end of SMB response in cifs_filldir cifs_filldir() copies the entry name out of an SMB1 TRANS2_FIND_FIRST / FIND_NEXT response using a length (de.namelen) supplied by the server. The kmalloc'd SMB response buffer is bounded, but nothing checks that de.name + de.namelen still lies inside that buffer before the eventual filldir64() -> verify_dirent_name() -> memchr() reads namelen bytes. A hostile SMB1 server that returns an oversized FileNameLength in a directory entry therefore causes memchr() to read past the end of the response slab buffer. Reachable from any user who can list a directory on a CIFS mount served by an attacker-controlled server (getdents64() on the mounted directory): BUG: KASAN: slab-out-of-bounds in memchr+0x71/0x80 Read of size 1 at addr ffff88800e0640cc by task poc/115 Call Trace: dump_stack_lvl+0x64/0x80 print_report+0xce/0x620 kasan_report+0xec/0x120 memchr+0x71/0x80 filldir64+0x4c/0x6a0 cifs_filldir.constprop.0+0x9bb/0x1e00 cifs_readdir+0x2101/0x3380 iterate_dir+0x19c/0x520 __x64_sys_getdents64+0x126/0x210 do_syscall_64+0x107/0x5a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Pass the end-of-response pointer down to cifs_filldir() and reject entries whose name would extend past that boundary. This bug was discovered by Artiphishell's vTriage pipeline, which generated a userspace reproducer (an emulated hostile SMB1 server plus a getdents64() client) that reliably triggers the KASAN report on an unpatched kernel. The fix below was drafted with the Claude coding assistant; a userspace reproducer is available on request. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Jay Vadayath Signed-off-by: Steve French --- fs/smb/client/readdir.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index ee5996e6d7d8..32a75afca8f5 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -952,7 +952,7 @@ static bool cifs_dir_emit(struct dir_context *ctx, static int cifs_filldir(char *find_entry, struct file *file, struct dir_context *ctx, char *scratch_buf, unsigned int max_len, - struct cached_fid *cfid) + char *end_of_smb, struct cached_fid *cfid) { struct cifsFileInfo *file_info = file->private_data; struct super_block *sb = file_inode(file)->i_sb; @@ -974,6 +974,11 @@ static int cifs_filldir(char *find_entry, struct file *file, return -EINVAL; } + if (de.name + de.namelen > end_of_smb) { + cifs_dbg(VFS, "search entry name extends past end of SMB\n"); + return -EINVAL; + } + /* skip . and .. since we added them first */ if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode)) return 0; @@ -1194,7 +1199,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) */ *tmp_buf = 0; rc = cifs_filldir(current_entry, file, ctx, - tmp_buf, max_len, cfid); + tmp_buf, max_len, end_of_smb, cfid); if (rc) { if (rc > 0) rc = 0; -- cgit v1.2.3 From 70d28bfcd6224eed75986b3b987b997e59643fa4 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 20 Jul 2026 09:09:55 +0200 Subject: ALSA: timer: don't re-enter an instance callback that is still running The userspace-driven timer (utimer) TRIGGER ioctl calls snd_timer_interrupt() directly with no serialization, so two threads triggering the same utimer can run snd_timer_interrupt() on one snd_timer concurrently. snd_timer_process_callbacks() drops timer->lock around each instance callback and marks the in-flight callback with the single SNDRV_TIMER_IFLG_CALLBACK bit; snd_timer_close_locked() waits on that bit to drain an in-flight callback before freeing the instance. The bit cannot represent two concurrent callbacks: when a second interrupt re-queues an instance whose callback is still running, both run at once, the first to finish clears the bit, and the close-path drain then frees the instance (and its callback_data) while the other callback is still live - a use-after-free reachable by any user able to open /dev/snd/timer, both via a user timer instance and via a sequencer queue timer bound to the utimer. snd_timer_interrupt() sets IFLG_CALLBACK before dropping timer->lock, so a concurrent interrupt already observes it under the lock. Skip re-queuing an instance (and its slaves) to the ack/sack list while its callback is in flight; the accumulated pticks are delivered on the next tick, so no event is lost. Fixes: 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers") Cc: stable@vger.kernel.org Suggested-by: Takashi Iwai Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/6F9B6501-8E65-4265-B02C-7EFB240D1664@doyensec.com --- sound/core/timer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 51c6ac4df9f4..0cd01ee72226 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -927,12 +927,15 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ack_list_head = &timer->ack_list_head; else ack_list_head = &timer->sack_list_head; - if (list_empty(&ti->ack_list)) + /* don't requeue an instance whose callback is still running */ + if (list_empty(&ti->ack_list) && + !(ti->flags & SNDRV_TIMER_IFLG_CALLBACK)) list_add_tail(&ti->ack_list, ack_list_head); list_for_each_entry(ts, &ti->slave_active_head, active_list) { ts->pticks = ti->pticks; ts->resolution = resolution; - if (list_empty(&ts->ack_list)) + if (list_empty(&ts->ack_list) && + !(ts->flags & SNDRV_TIMER_IFLG_CALLBACK)) list_add_tail(&ts->ack_list, ack_list_head); } } -- cgit v1.2.3 From bdefe1346a8e6b8dc8593406dc2617e985fcbcab Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Mon, 20 Jul 2026 09:14:12 +0200 Subject: ALSA: timer: drain a slave's callback before its master detaches it snd_timer_close_locked() drains the closing instance's own in-flight callback (IFLG_CALLBACK) before freeing it, but not its slaves'. When a master instance is closed, remove_slave_links() clears each slave's ->timer; the slave's own close then reads timer == NULL and takes the branch that skips the drain entirely (snd_timer_stop_slave() also no-ops on a NULL timer). So a slave whose callback is still running when the master is closed is freed underneath the live callback, leading to use-after-free. Drain the slaves too before remove_slave_links() severs them. snd_timer_stop() has already taken this instance off the active list, so no new slave callback can be queued. Take the slaves off the ack list so a pending one can't fire either, then wait for any that is already in flight. Fixes: 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/D26598EB-DBF7-4D76-9F71-8E4BD59822D4@doyensec.com --- sound/core/timer.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 0cd01ee72226..07ef127b29e1 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -489,10 +489,20 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri, snd_timer_stop(timeri); if (timer) { + struct snd_timer_instance *slave; + bool busy; + timer->num_instances--; - /* wait, until the active callback is finished */ + /* unqueue then drain the slaves' callbacks before remove_slave_links() severs them */ spin_lock_irq(&timer->lock); - while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { + list_for_each_entry(slave, &timeri->slave_list_head, open_list) + list_del_init(&slave->ack_list); + for (;;) { + busy = timeri->flags & SNDRV_TIMER_IFLG_CALLBACK; + list_for_each_entry(slave, &timeri->slave_list_head, open_list) + busy |= slave->flags & SNDRV_TIMER_IFLG_CALLBACK; + if (!busy) + break; spin_unlock_irq(&timer->lock); udelay(10); spin_lock_irq(&timer->lock); -- cgit v1.2.3 From b7e53968cb8882c2d276429ea8550848a4940874 Mon Sep 17 00:00:00 2001 From: Guanghui Yang <3497809730@qq.com> Date: Sun, 12 Jul 2026 03:42:56 +0000 Subject: xfs: propagate errors from xfs_rtginode_load xfs_rtginode_ensure() treats every xfs_rtginode_load() error other than -ENOENT as success. This can leave the realtime group inode unset after an I/O, allocation, or corruption error. Growfs then continues as though the inode had been loaded. Only -ENOENT means that the inode needs to be created. Return all other errors to the growfs caller. Fixes: ae897e0bed0f ("xfs: support creating per-RTG files in growfs") Cc: stable@vger.kernel.org Signed-off-by: Guanghui Yang <3497809730@qq.com> Reviewed-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_rtalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 7a3f97686989..84efe5a8fb11 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -737,7 +737,7 @@ xfs_rtginode_ensure( xfs_trans_cancel(tp); if (error != -ENOENT) - return 0; + return error; return xfs_rtginode_create(rtg, type, true); } -- cgit v1.2.3 From a411ea4a87162898d2a0547fdfb721ddb7626be3 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Thu, 16 Jul 2026 08:39:59 +0530 Subject: drm/i915/backlight: Remove DP_EDP_BACKLIGHT_AUX_ENABLE_CAP check for DPCD backlight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out some panels allow only AUX based backlight by just setting the DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP and not setting the DP_EDP_BACKLIGHT_AUX_ENABLE_CAP. If we make DP_EDP_BACKLIGHT_AUX_ENABLE_CAP a necessity for AUX based DPCD backlight these panels loose the ability to manipulate backlight via AUX, especially ones with no PWM controller. Remove this check from function so that panels who do not advertise DP_EDP_BACKLIGHT_AUX_ENABLE_CAP but advertise DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP are able to manipulate backlight again. Fixes: ed8be780bdbc ("drm/i915/backlight: Fix VESA backlight possible check condition") Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16507 Signed-off-by: Suraj Kandpal Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260716030959.436430-1-suraj.kandpal@intel.com (cherry picked from commit 7d594b24c915afb4b0c5fb8875403253daef5b24) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index 7a6c07f6aaeb..266e042e0023 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -615,12 +615,7 @@ check_if_vesa_backlight_possible(struct intel_dp *intel_dp) int ret; u8 bit_min, bit_max; - /* - * Since we only support Fully AUX Based VESA Backlight interface make sure - * backlight enable is possible via AUX along with backlight adjustment - */ - if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP && - intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) + if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP)) return false; ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min); -- cgit v1.2.3 From 361f533a2dce2c2841fe4dc0c9d85a67117edf95 Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sat, 18 Jul 2026 15:01:31 +0530 Subject: phy: qcom: m31-eusb2: Fix return value of init call The init call currently returns success irrespective of any failures during repeater init or clock enablement. Return appropriate error value in the init call failure path. Fixes: 9c8504861cc4 ("phy: qcom: Add M31 based eUSB2 PHY driver") Signed-off-by: Krishna Kurapati Link: https://patch.msgid.link/20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c index 0bec8657149c..275df38256de 100644 --- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c @@ -221,7 +221,7 @@ disable_repeater: disable_vreg: regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs); - return 0; + return ret; } static int m31eusb2_phy_exit(struct phy *uphy) -- cgit v1.2.3 From be2b5b17b7053fee142939076746d26b2d6c9702 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 14 Jul 2026 19:41:20 +0400 Subject: phy: rockchip: naneng-combphy: Always configure SSC spread direction Commit 0b31f297557f ("phy: rockchip: naneng-combphy: Consolidate SSC configuration") moved the SSC spread spectrum direction setup into the new rk_combphy_common_cfg_ssc() helper. That helper returns early when the 'rockchip,enable-ssc' property is absent, whereas the equivalent RK3568_PHYREG32 direction writes previously ran unconditionally in the per-type switch statements, independent of whether SSC modulation was actually enabled. As no in-tree board sets 'rockchip,enable-ssc', this changed the behavior at least for USB3 on RK3576, which now fails to bring up the link. USB 2.0 still enumerates, but USB 3.0 does not, and the SuperSpeed root port floods the log every second with: usb usb2-port1: Cannot enable. Maybe the USB cable is bad? This was observed on two different RK3576 devices with a CoreChips SL6341 USB 2.0/3.0 hub connected to the USB DRD controller running in host mode. Perform the SSC direction writes for PCIe/USB3 (and SATA) before the enable_ssc check so that they always run, as they did before the consolidation. Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/CAKTNdwH_ZMQa-97h+tqdsWqXKtorkFF9wHAMn60-8ZGKuze_Mg@mail.gmail.com/ Fixes: 0b31f297557f ("phy: rockchip: naneng-combphy: Consolidate SSC configuration") Signed-off-by: Alexey Charkov Tested-by: Liu Changjie Link: https://patch.msgid.link/20260714-naneng-ssc-fix-v1-1-1c40a58061ae@flipper.net Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-naneng-combphy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c index 2b0f152f5470..7843356a4dd4 100644 --- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -452,9 +452,6 @@ static void rk_combphy_common_cfg_ssc(struct rockchip_combphy_priv *priv, unsign struct device_node *np = priv->dev->of_node; u32 val; - if (!priv->enable_ssc) - return; - /* Set SSC downward spread spectrum for PCIe and USB3 */ if (priv->type == PHY_TYPE_PCIE || priv->type == PHY_TYPE_USB3) { val = FIELD_PREP(RK3568_PHYREG32_SSC_MASK, RK3568_PHYREG32_SSC_DOWNWARD); @@ -471,6 +468,9 @@ static void rk_combphy_common_cfg_ssc(struct rockchip_combphy_priv *priv, unsign RK3568_PHYREG32); } + if (!priv->enable_ssc) + return; + /* Enable SSC */ val = readl(priv->mmio + RK3568_PHYREG8); val |= RK3568_PHYREG8_SSC_EN; -- cgit v1.2.3 From b52c5103f64ee825996ca1ab8df7283cde8c5f86 Mon Sep 17 00:00:00 2001 From: Qing Ming Date: Sat, 23 May 2026 16:15:43 +0800 Subject: ovpn: avoid putting unrelated P2P peer on socket release ovpn_peer_release_p2p() is called when an OVPN UDP socket is being destroyed. It checks the currently published P2P peer and releases it only if that peer still uses the socket being destroyed. A peer replacement can publish a new peer before the old UDP socket is destroyed. When the old socket destruction path runs afterwards, ovpn_peer_release_p2p() observes the new peer through ovpn->peer. Since the new peer uses a different socket, the function takes the socket mismatch branch. That branch still calls ovpn_peer_put(peer). At this point, however, peer is the currently published replacement peer, not the peer associated with the socket being destroyed. Dropping its reference can free it while ovpn->peer still points to it, leading to later use-after-free accesses from the peer and socket cleanup paths. KASAN reports this as a slab-use-after-free on the kmalloc-1k ovpn_peer object. In the reproducer, the object is allocated from ovpn_peer_new() via ovpn_nl_peer_new_doit(), and freed through ovpn_peer_release_rcu() from RCU callback processing. Observed access sites include ovpn_peer_remove(), ovpn_socket_release(), ovpn_nl_peer_del_notify(), and unlock_ovpn(). Fix this by returning from the socket mismatch branch without putting the peer. Fixes: f6226ae7a0cd ("ovpn: introduce the ovpn_socket object") Signed-off-by: Qing Ming Reviewed-by: Simon Horman Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a09d61296425..1844d97154ce 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -1167,7 +1167,6 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk, ovpn_sock = rcu_access_pointer(peer->sock); if (!ovpn_sock || ovpn_sock->sk != sk) { spin_unlock_bh(&ovpn->lock); - ovpn_peer_put(peer); return; } } -- cgit v1.2.3 From 63bbe18fc03062f483c627838a566a707b62da79 Mon Sep 17 00:00:00 2001 From: Pavitra Jha Date: Sat, 23 May 2026 05:02:43 -0400 Subject: ovpn: fix peer refcount leak in TCP error paths When either the TCP RX or TX error path calls ovpn_peer_hold() followed by schedule_work(&peer->tcp.defer_del_work), and the work item is already pending from the other path, schedule_work() returns false and the work runs only once. Since ovpn_tcp_peer_del_work() calls ovpn_peer_put() exactly once, the extra reference taken by the losing path is never dropped, leaking the peer object. The race window: CPU0 (strparser/RX error): CPU1 (tcp_tx_work/TX error): ovpn_peer_hold() <- refcnt+1 ovpn_peer_hold() <- refcnt+2 schedule_work() <- queued schedule_work() <- NO-OP (work already pending) ovpn_tcp_peer_del_work runs: ovpn_peer_del() ovpn_peer_put() <- refcnt+1 <- peer never freed Fix by checking the return value of schedule_work() in both paths and calling ovpn_peer_put() to drop the extra reference if the work was already pending. ovpn_peer_hold() is kept unconditional in the TX path as it cannot fail at that point. Fixes: a6a5e87b3ee4 ("ovpn: avoid sleep in atomic context in TCP RX error path") Cc: stable@vger.kernel.org Signed-off-by: Pavitra Jha Reviewed-by: Sabrina Dubroca Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/tcp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 433bd07a4f1b..0af14055c39a 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -151,7 +151,8 @@ err: /* take reference for deferred peer deletion. should never fail */ if (WARN_ON(!ovpn_peer_hold(peer))) goto err_nopeer; - schedule_work(&peer->tcp.defer_del_work); + if (!schedule_work(&peer->tcp.defer_del_work)) + ovpn_peer_put(peer); ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); err_nopeer: kfree_skb(skb); @@ -283,7 +284,8 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk) * stream therefore we abort the connection */ ovpn_peer_hold(peer); - schedule_work(&peer->tcp.defer_del_work); + if (!schedule_work(&peer->tcp.defer_del_work)) + ovpn_peer_put(peer); /* we bail out immediately and keep tx_in_progress set * to true. This way we prevent more TX attempts -- cgit v1.2.3 From a4710ae2e7e322fdaefb4be8604228279cfaf48c Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Sat, 23 May 2026 20:38:27 +0545 Subject: ovpn: hold peer before scheduling keepalive work ovpn_peer_keepalive_send() passes its peer reference to ovpn_xmit_special(), which ultimately drops it. The keepalive scheduler currently queues the work first and takes the reference only after schedule_work() reports that the work was queued. Once schedule_work() queues the item, another CPU may run the worker before the caller gets to ovpn_peer_hold(). In that case the worker can consume a reference that was not acquired for it, corrupting the peer lifetime accounting. Take the peer reference before queueing the work and drop it again when the work was already pending. Fixes: 3ecfd9349f40 ("ovpn: implement keepalive mechanism") Cc: stable@vger.kernel.org Signed-off-by: Shuvam Pandey Reviewed-by: Sabrina Dubroca Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 1844d97154ce..2b6096d8b1cc 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -1284,8 +1284,10 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer, netdev_dbg(peer->ovpn->dev, "sending keepalive to peer %u\n", peer->id); - if (schedule_work(&peer->keepalive_work)) - ovpn_peer_hold(peer); + if (WARN_ON(!ovpn_peer_hold(peer))) + return 0; + if (!schedule_work(&peer->keepalive_work)) + ovpn_peer_put(peer); } if (next_run1 < next_run2) -- cgit v1.2.3 From 0bd9cfebc1c91e1066e56d6261b99691b9df6008 Mon Sep 17 00:00:00 2001 From: longlong yan Date: Wed, 3 Jun 2026 15:27:41 +0800 Subject: selftests/net: ovpn: fix getaddrinfo memory leak in ovpn_parse_remote() The ovpn_parse_remote() function has two memory management issues: 1. When both 'host' and 'vpnip' are non-NULL, the first getaddrinfo() allocation is leaked because 'result' is overwritten by the second getaddrinfo() call without freeing the first allocation. 2. When both 'host' and 'vpnip' are NULL, 'result' is an uninitialized stack variable passed to freeaddrinfo(), which is undefined behavior. Fix by initializing 'result' to NULL and calling freeaddrinfo() after the first getaddrinfo() result is consumed. Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module") Signed-off-by: longlong yan Signed-off-by: Antonio Quartulli --- tools/testing/selftests/net/ovpn/ovpn-cli.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c index d40953375c86..f4effa7580c0 100644 --- a/tools/testing/selftests/net/ovpn/ovpn-cli.c +++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c @@ -1785,7 +1785,7 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host, const char *service, const char *vpnip) { int ret; - struct addrinfo *result; + struct addrinfo *result = NULL; struct addrinfo hints = { .ai_family = ovpn->sa_family, .ai_socktype = SOCK_DGRAM, @@ -1809,6 +1809,8 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host, } memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen); + freeaddrinfo(result); + result = NULL; } if (vpnip) { -- cgit v1.2.3 From e1ad6fe5db719874efa45b2caf9934552e09fc43 Mon Sep 17 00:00:00 2001 From: Marco Baffo Date: Mon, 8 Jun 2026 16:04:46 +0200 Subject: ovpn: fix use after free in unlock_ovpn() unlock_ovpn() iterates over the release_list using llist_for_each_entry() and drops the peer reference inside the loop body via ovpn_peer_put(). If this drops the last reference, the peer is eventually freed. However, llist_for_each_entry() reads peer->release_entry.next in the loop advance expression, which runs after the body. By that time the peer may have already been freed, resulting in a use after free when advancing to the next list entry. Fix this by using llist_for_each_entry_safe(), which caches the next pointer before executing the loop body. Fixes: 80747caef33d ("ovpn: introduce the ovpn_peer object") Signed-off-by: Marco Baffo Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 2b6096d8b1cc..8fdbb5050690 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -26,11 +26,12 @@ static void unlock_ovpn(struct ovpn_priv *ovpn, struct llist_head *release_list) __releases(&ovpn->lock) { - struct ovpn_peer *peer; + struct ovpn_peer *peer, *next; spin_unlock_bh(&ovpn->lock); - llist_for_each_entry(peer, release_list->first, release_entry) { + llist_for_each_entry_safe(peer, next, release_list->first, + release_entry) { ovpn_socket_release(peer); ovpn_peer_put(peer); } -- cgit v1.2.3 From 17e2030f37600994440f875dc410615d5c66ee6d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 14 Jul 2026 15:36:41 +0800 Subject: drm/imagination: acquire vm_ctx->lock before mapping memory to GPU VM The drm gpuvm code doesn't protect find operation against map operation, and the driver needs to ensure a map operation shouldn't happen when a find operation is in progress. In some cases a find operation will be in progress when doing map/unmap operations, and the find operation will do a NULL pointer dereference. An example of the stack trace of such NULL dereference is shown below: ``` Unable to handle kernel access to user memory without uaccess routines at virtual address 0000000000000010 [] drm_gpuva_find+0x28/0x6c [drm_gpuvm] [] pvr_vm_unmap+0x34/0x68 [powervr] [] pvr_ioctl_vm_unmap+0x2e/0x50 [powervr] [] drm_ioctl_kernel+0x8e/0xdc [] drm_ioctl+0x1be/0x3e0 [] __riscv_sys_ioctl+0xba/0xc4 [] do_trap_ecall_u+0x23e/0x3f4 [] handle_exception+0x168/0x174 ``` As all occurences of drm_gpuva_find*() are already guarded by vm_ctx->lock, make pvr_vm_map() to acquire this lock to prevent disturbing any find operation. This fixes the NULL deference problem in drm_gpuva_find*(). Cc: stable@vger.kernel.org Fixes: ff5f643de0bf ("drm/imagination: Add GEM and VM related code") Fixes: 4bc736f890ce ("drm/imagination: vm: make use of GPUVM's drm_exec helper") Signed-off-by: Icenowy Zheng Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260714073641.1935075-1-zhengxingda@iscas.ac.cn Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_vm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c index 396d349fb6ce..ceb78694cd98 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -747,6 +747,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, pvr_gem_object_get(pvr_obj); + mutex_lock(&vm_ctx->lock); err = drm_gpuvm_exec_lock(&vm_exec); if (err) goto err_cleanup; @@ -756,6 +757,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, drm_gpuvm_exec_unlock(&vm_exec); err_cleanup: + mutex_unlock(&vm_ctx->lock); pvr_vm_bind_op_fini(&bind_op); return err; -- cgit v1.2.3 From 52beeed5e5d257e1da3e2d2f2fb25bc1e3cdb6d2 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:52:20 +0800 Subject: USB: serial: mxuport: validate firmware header size mxuport_probe() reads version bytes at fixed offsets after request_firmware() succeeds. Firmware loading success does not prove that the blob reaches the highest version offset. Reject short firmware images before reading the version bytes. This is source-level parser hardening; no affected device or crash was observed. Reviewed-by: Andrew Lunn Signed-off-by: Pengpeng Hou Fixes: ee467a1f2066 ("USB: serial: add Moxa UPORT 12XX/14XX/16XX driver") Signed-off-by: Johan Hovold --- drivers/usb/serial/mxuport.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index e3c5a1b97542..088d5dd8abb5 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -1080,6 +1080,13 @@ static int mxuport_probe(struct usb_serial *serial, /* Use the firmware already in the device */ err = 0; } else { + if (fw_p->size <= VER_ADDR_3) { + dev_err(&serial->interface->dev, + "Firmware %s is too short\n", buf); + err = -EINVAL; + goto out; + } + local_ver = ((fw_p->data[VER_ADDR_1] << 16) | (fw_p->data[VER_ADDR_2] << 8) | fw_p->data[VER_ADDR_3]); -- cgit v1.2.3 From f7e6287ccd3abeed9e638b581dc3fdf742106ba3 Mon Sep 17 00:00:00 2001 From: Marco Baffo Date: Mon, 8 Jun 2026 16:06:42 +0200 Subject: ovpn: use monotonic clock for peer keepalive timeouts Replace ktime_get_real_seconds() with the monotonic ktime_get_boottime_seconds() to ensure the keepalive mechanism is robust against system clock modifications. Right now, the driver uses ktime_get_real_seconds() to track peer timeouts, relying on the system wall-clock. An administrative time adjustment or an NTP sync that steps the clock forward can cause `now' to instantly exceed `last_recv + timeout'. When this occurs, the driver artificially expires healthy peers. Depending on the OpenVPN user-space configuration, this triggers a premature tunnel restart (if --keepalive or --ping-restart is used) or a complete disconnection of the client (if --ping-exit is used). Fixes: 3ecfd9349f40 ("ovpn: implement keepalive mechanism") Signed-off-by: Marco Baffo Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 4 ++-- drivers/net/ovpn/peer.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index a6b777a9c2d9..9a66d693039a 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -142,7 +142,7 @@ void ovpn_decrypt_post(void *data, int ret) } /* keep track of last received authenticated packet for keepalive */ - WRITE_ONCE(peer->last_recv, ktime_get_real_seconds()); + WRITE_ONCE(peer->last_recv, ktime_get_boottime_seconds()); rcu_read_lock(); sock = rcu_dereference(peer->sock); @@ -294,7 +294,7 @@ void ovpn_encrypt_post(void *data, int ret) ovpn_peer_stats_increment_tx(&peer->link_stats, orig_len); /* keep track of last sent packet for keepalive */ - WRITE_ONCE(peer->last_sent, ktime_get_real_seconds()); + WRITE_ONCE(peer->last_sent, ktime_get_boottime_seconds()); /* skb passed down the stack - don't free it */ skb = NULL; err_unlock: diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 8fdbb5050690..a21d02ac715e 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -45,7 +45,7 @@ static void unlock_ovpn(struct ovpn_priv *ovpn, */ void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout) { - time64_t now = ktime_get_real_seconds(); + time64_t now = ktime_get_boottime_seconds(); netdev_dbg(peer->ovpn->dev, "scheduling keepalive for peer %u: interval=%u timeout=%u\n", @@ -1359,7 +1359,7 @@ void ovpn_peer_keepalive_work(struct work_struct *work) { struct ovpn_priv *ovpn = container_of(work, struct ovpn_priv, keepalive_work.work); - time64_t next_run = 0, now = ktime_get_real_seconds(); + time64_t next_run = 0, now = ktime_get_boottime_seconds(); LLIST_HEAD(release_list); spin_lock_bh(&ovpn->lock); -- cgit v1.2.3 From 70e76e700fc6c46afb4e17aec099a1ea089b4a22 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:58:26 +0800 Subject: hwmon: occ: validate poll response sensor blocks The OCC poll response parser walks a counted list of sensor data blocks. It used the static backing-array capacity as the parse boundary, but a transport response makes only data_length bytes current and valid. A truncated response can therefore make the parser consume a block header or block extent outside the current response. Use data_length as the parent boundary, prove the fixed poll header and each current block header before reading them, and prove the complete block before advancing. Keep parsed sensor metadata local until the complete response has passed validation, then publish it. Propagate malformed-response errors before publishing the OCC as active. Fixes: aa195fe49b03 ("hwmon (occ): Parse OCC poll response") Signed-off-by: Pengpeng Hou Link: https://lore.kernel.org/r/20260720115826.14813-1-pengpeng@iscas.ac.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/occ/common.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index e18e80e832fd..175208d712b0 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -1052,32 +1052,49 @@ static int occ_setup_sensor_attrs(struct occ *occ) } /* only need to do this once at startup, as OCC won't change sensors on us */ -static void occ_parse_poll_response(struct occ *occ) +static int occ_parse_poll_response(struct occ *occ) { unsigned int i, old_offset, offset = 0, size = 0; + u16 data_length; struct occ_sensor *sensor; - struct occ_sensors *sensors = &occ->sensors; + struct occ_sensors parsed = {}; + struct occ_sensors *sensors = &parsed; struct occ_response *resp = &occ->resp; struct occ_poll_response *poll = (struct occ_poll_response *)&resp->data[0]; struct occ_poll_response_header *header = &poll->header; struct occ_sensor_data_block *block = &poll->block; + data_length = get_unaligned_be16(&resp->data_length); + if (data_length < sizeof(*header) || data_length > OCC_RESP_DATA_BYTES) { + dev_err(occ->bus_dev, "invalid OCC poll response length %u\n", + data_length); + return -EMSGSIZE; + } + dev_info(occ->bus_dev, "OCC found, code level: %.16s\n", header->occ_code_level); for (i = 0; i < header->num_sensor_data_blocks; ++i) { block = (struct occ_sensor_data_block *)((u8 *)block + offset); + if (size + sizeof(*header) + sizeof(block->header) > + data_length) { + dev_err(occ->bus_dev, + "truncated OCC sensor block header\n"); + return -EMSGSIZE; + } + old_offset = offset; offset = (block->header.num_sensors * block->header.sensor_length) + sizeof(block->header); - size += offset; /* validate all the length/size fields */ - if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) { - dev_warn(occ->bus_dev, "exceeded response buffer\n"); - return; + if (size + sizeof(*header) + offset > data_length) { + dev_err(occ->bus_dev, + "exceeded OCC poll response length\n"); + return -EMSGSIZE; } + size += offset; dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n", old_offset, offset - 1, block->header.eye_catcher, @@ -1107,6 +1124,9 @@ static void occ_parse_poll_response(struct occ *occ) dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size, sizeof(*header), size + sizeof(*header)); + occ->sensors = parsed; + + return 0; } int occ_active(struct occ *occ, bool active) @@ -1138,10 +1158,12 @@ int occ_active(struct occ *occ, bool active) goto unlock; } - occ->active = true; occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; - occ_parse_poll_response(occ); + rc = occ_parse_poll_response(occ); + if (rc) + goto unlock; + occ->active = true; rc = occ_setup_sensor_attrs(occ); if (rc) { dev_err(occ->bus_dev, -- cgit v1.2.3 From 6fcd91ce2a0787cd4bdf6a0b3cd4884566a3cdba Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:48:17 +0800 Subject: USB: serial: io_ti: reject oversized boot-mode firmware do_boot_mode() copies the firmware payload, excluding its four-byte prefix, into a fixed 15.5 KiB staging buffer. check_fw_sanity() already proves that the image contains its seven-byte header and validates the declared image length and checksum, but it does not impose this boot-mode destination limit. Reject images whose payload does not fit before allocating and filling the staging buffer. Fixes: d12b219a228e ("edgeport-ti: use request_firmware()") Signed-off-by: Pengpeng Hou Signed-off-by: Johan Hovold --- drivers/usb/serial/io_ti.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 07c0eff3bef4..cc28f5869a3e 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1464,6 +1464,12 @@ static int do_boot_mode(struct edgeport_serial *serial, /* Allocate a 15.5k buffer + 3 byte header */ buffer_size = (((1024 * 16) - 512) + sizeof(struct ti_i2c_image_header)); + if (fw->size - 4 > buffer_size) { + dev_err(dev, "%s - firmware image is too large\n", + __func__); + return -EINVAL; + } + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) return -ENOMEM; -- cgit v1.2.3 From 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 20 Jul 2026 16:09:50 +0530 Subject: ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard. The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check: if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98095->mclk holding an error pointer with no indication to the caller. This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field. Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers. Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") Signed-off-by: Uday Khare Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98095.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index ced9bd4d94da..130b6694a634 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1987,8 +1987,9 @@ static int max98095_probe(struct snd_soc_component *component) int ret = 0; max98095->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (IS_ERR(max98095->mclk)) + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; /* reset the codec, the DSP core, and disable all interrupts */ max98095_reset(component); -- cgit v1.2.3 From cf616096a0f3a2b60f7d68b6b39674a6867ded9c Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Tue, 30 Jun 2026 14:31:00 -0400 Subject: NFS: Pin the 'struct nfs_server' during a FREE_STATEID call Dan Aloni reports that he was able to hit a use-after-free bug if a FREE_STATEID operation gets delayed for whatever reason. Fix this by bumping the refcount of the 'struct nfs_server' object for the duration of the FREE_STATEID so it doesn't get cleaned up from underneath us while operations are still in flight. Reported-by: Dan Aloni Fixes: 7c1d5fae4a87 ("NFSv4: Convert nfs41_free_stateid to use an asynchronous RPC call") Tested-by: Dan Aloni Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1360409d8de9..71b6ab863b39 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -10364,6 +10364,7 @@ static void nfs41_free_stateid_release(void *calldata) struct nfs_free_stateid_data *data = calldata; struct nfs_client *clp = data->server->nfs_client; + nfs_sb_deactive(data->server->super); nfs_put_client(clp); kfree(calldata); } @@ -10405,6 +10406,10 @@ static int nfs41_free_stateid(struct nfs_server *server, if (!refcount_inc_not_zero(&clp->cl_count)) return -EIO; + if (!nfs_sb_active(server->super)) { + nfs_put_client(clp); + return -EIO; + } nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID, &task_setup.rpc_client, &msg); -- cgit v1.2.3 From 4aeb63d5ac2dba2a474e7b64d60776d9dd1c6cd2 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Tue, 30 Jun 2026 15:39:45 -0400 Subject: NFS: Decrement refcounts if allocating nfs_free_stateid_data fails I noticed that we were immediately exiting this function if the allocation fails, leaving the client and server object refcounts bumped. Fix this by creating a common exit point to clean up dangling references. Fixes: 576acc259146 ("nfs4: take a reference on the nfs_client when running FREE_STATEID") Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 71b6ab863b39..7d98e9a98580 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -10403,21 +10403,22 @@ static int nfs41_free_stateid(struct nfs_server *server, struct nfs_free_stateid_data *data; struct rpc_task *task; struct nfs_client *clp = server->nfs_client; + int ret = -EIO; if (!refcount_inc_not_zero(&clp->cl_count)) - return -EIO; - if (!nfs_sb_active(server->super)) { - nfs_put_client(clp); - return -EIO; - } + return ret; + if (!nfs_sb_active(server->super)) + goto out_put_clp; nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID, &task_setup.rpc_client, &msg); dprintk("NFS call free_stateid %p\n", stateid); data = kmalloc_obj(*data); - if (!data) - return -ENOMEM; + if (!data) { + ret = -ENOMEM; + goto out_put_server; + } data->server = server; nfs4_stateid_copy(&data->args.stateid, stateid); @@ -10433,6 +10434,11 @@ static int nfs41_free_stateid(struct nfs_server *server, rpc_put_task(task); stateid->type = NFS4_FREED_STATEID_TYPE; return 0; +out_put_server: + nfs_sb_deactive(server->super); +out_put_clp: + nfs_put_client(clp); + return ret; } static void -- cgit v1.2.3 From 6c7b7a07db47df7745d30f4bca795f3bb5976b33 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 20 Jul 2026 10:16:28 -0500 Subject: Add missing git branch info for cifs and ksmbd to MAINTAINERS file cifs client and ksmbd server were missing the git branch info in the MAINTAINERS file. They just were showing the git tree. Signed-off-by: Steve French --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index a674e36529f7..276abc0ad458 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6478,7 +6478,7 @@ L: linux-cifs@vger.kernel.org L: samba-technical@lists.samba.org (moderated for non-subscribers) S: Supported W: https://wiki.samba.org/index.php/LinuxCIFS -T: git https://git.samba.org/sfrench/cifs-2.6.git +T: git https://git.samba.org/sfrench/cifs-2.6.git for-next F: Documentation/admin-guide/cifs/ F: fs/smb/client/ F: fs/smb/common/ @@ -14142,7 +14142,7 @@ R: Sergey Senozhatsky R: Tom Talpey L: linux-cifs@vger.kernel.org S: Maintained -T: git https://git.samba.org/ksmbd.git +T: git https://git.samba.org/ksmbd.git ksmbd-for-next F: Documentation/filesystems/smb/ksmbd.rst F: fs/smb/common/ F: fs/smb/server/ -- cgit v1.2.3 From a792ce0fad61a70793ec565743f11d6ca534de59 Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 20 Jul 2026 16:12:54 +0530 Subject: ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard. The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check: if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98090->mclk holding an error pointer with no indication to the caller. This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field. Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers. Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") Signed-off-by: Uday Khare Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index da416329b038..57bd5a1c34c8 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2423,8 +2423,9 @@ static int max98090_probe(struct snd_soc_component *component) dev_dbg(component->dev, "max98090_probe\n"); max98090->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (IS_ERR(max98090->mclk)) + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; max98090->component = component; -- cgit v1.2.3 From 57441577bac3637473da2c9644336eaa0ac5732f Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 26 May 2026 19:24:47 +0530 Subject: drm/xe/madvise: Skip invalidation for purgeable state updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Purgeable state updates only change VMA/BO metadata. They do not zap PTEs when switching between DONTNEED and WILLNEED. PTEs are zapped later if the BO is actually purged. xe_vm_invalidate_madvise_range() waits on the VM dma-resv before checking vma->skip_invalidation. Since purgeable madvise marks all affected VMAs to skip invalidation, this wait is unnecessary and can stall on unrelated in-flight work. Skip the invalidate path entirely for purgeable state updates. v2: - Replace inline 'args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE' check with a small helper madvise_range_needs_invalidation(). (Himal) Suggested-by: Matthew Brost Cc: Matthew Brost Cc: Thomas Hellström Cc: Himal Prasad Ghimiray Signed-off-by: Arvind Yadav Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260526135447.2973029-1-arvind.yadav@intel.com Signed-off-by: Tejas Upadhyay Fixes: ada7486c5668 ("drm/xe: Implement madvise ioctl for xe") Cc: # v6.18+ (cherry picked from commit 134377098b9c14abd31c3bcac00c9653f0f0c4c3) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_vm_madvise.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index 246fe1843142..0474768a38aa 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -332,6 +332,20 @@ static int xe_vm_invalidate_madvise_range(struct xe_vm *vm, u64 start, u64 end) return err; } +/** + * madvise_range_needs_invalidation() - Check whether madvise needs invalidation + * @args: madvise ioctl arguments + * + * Purgeable state updates only touch VMA/BO metadata. PTEs stay valid and are + * zapped only if the BO is later purged. + * + * Return: true when the update needs PTE invalidation. + */ +static bool madvise_range_needs_invalidation(const struct drm_xe_madvise *args) +{ + return args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE; +} + static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madvise *args) { if (XE_IOCTL_DBG(xe, !args)) @@ -708,8 +722,9 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args, &details); - err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, - madvise_range.addr + args->range); + if (madvise_range_needs_invalidation(args)) + err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, + madvise_range.addr + args->range); if (madvise_range.has_svm_userptr_vmas) xe_svm_notifier_unlock(vm); -- cgit v1.2.3 From 1d26f125501f3fbe6c259ab75bf6516299a0bf0e Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sat, 18 Jul 2026 02:05:59 +0100 Subject: regulator: mt6358: use regmap helper to read fixed LDO calibration The "fixed" LDOs with output voltage calibration use mt6358_get_buck_voltage_sel as their get_voltage_sel op, but the MT6358_REG_FIXED and MT6366_REG_FIXED entries do not populate da_vsel_reg/da_vsel_mask. The op therefore reads register 0x0 with a zero mask and shifts the result by ffs(0) - 1 = -1, which is undefined behaviour and gets flagged by UBSAN on every boot on MT6366 boards: UBSAN: shift-out-of-bounds in drivers/regulator/mt6358-regulator.c:384:38 shift exponent -1 is negative Call trace: mt6358_get_buck_voltage_sel+0xc8/0x120 regulator_get_voltage_rdev+0x70/0x170 set_machine_constraints+0x504/0xc38 regulator_register+0x324/0xc68 Besides the undefined shift, the returned selector is always 0, so the actual calibration offset programmed in _ANA_CON0 is never reported. The descriptor already carries the correct vsel_reg/vsel_mask (the ANA_CON0 calibration field), matching the regulator_set_voltage_sel_regmap op already in use. Read the selector back through regulator_get_voltage_sel_regmap instead. Fixes: cf08fa74c716 ("regulator: mt6358: Add output voltage fine tuning to fixed regulators") Signed-off-by: Daniel Golle Reviewed-by: Chen-Yu Tsai Tested-by: Chen-Yu Tsai Link: https://patch.msgid.link/dcd98d81dede338c9bbb9700a9613c848b702e49.1784336005.git.daniel@makrotopia.org Signed-off-by: Mark Brown --- drivers/regulator/mt6358-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/mt6358-regulator.c b/drivers/regulator/mt6358-regulator.c index f2bb3c1523ca..d6a0ec406b07 100644 --- a/drivers/regulator/mt6358-regulator.c +++ b/drivers/regulator/mt6358-regulator.c @@ -492,7 +492,7 @@ static const struct regulator_ops mt6358_volt_fixed_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .get_voltage_sel = mt6358_get_buck_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, -- cgit v1.2.3 From b5fa40226e71c17847b9ff2816c6ca4133d0d994 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 20:31:20 -0700 Subject: fscrypt: Add missing superblock check in find_or_insert_direct_key() The legacy 'fscrypt_direct_keys' table caches master keys that are used by v1 encryption policies that have FSCRYPT_POLICY_FLAG_DIRECT_KEY. It's just a global table for all filesystems (since the keys can be provided by the legacy process-subscribed keyrings mechanism, which makes it difficult to reuse super_block::s_master_keys). The entries in it ('struct fscrypt_direct_key') do contain a super_block pointer, though, for passing to fscrypt_destroy_inline_crypt_key() when the last inode that references the key is evicted. However, when finding the fscrypt_direct_key for an inode, we weren't actually comparing the super_block pointer. As a result, inodes with different super_blocks could point to the same fscrypt_direct_key. That could extend the lifetime of a fscrypt_direct_key beyond the super_block it points to, causing a use-after-free later. Fix this by creating distinct fscrypt_direct_key structs for distinct super_block structs. Note that this problem doesn't exist in the v2 policy equivalent ("per-mode keys"), since the data structures there are per super_block. Fixes: 22e9947a4b2b ("fscrypt: stop holding extra request_queue references") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260717044303.425265-1-ebiggers%40kernel.org Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260719033120.122120-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/keysetup_v1.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index e6e527c73f16..7e3a58dc4b56 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -147,13 +147,19 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert, if (memcmp(ci->ci_policy.v1.master_key_descriptor, dk->dk_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0) continue; + /* The sb is used at eviction time, so it must be the same. */ + if (ci->ci_inode->i_sb != dk->dk_sb) + continue; if (ci->ci_mode != dk->dk_mode) continue; if (!fscrypt_is_key_prepared(&dk->dk_key, ci)) continue; if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize)) continue; - /* using existing tfm with same (descriptor, mode, raw_key) */ + /* + * Use an existing prepared key with the same (descriptor, sb, + * mode, inlinecrypt, raw_key) combination. + */ refcount_inc(&dk->dk_refcount); spin_unlock(&fscrypt_direct_keys_lock); free_direct_key(to_insert); -- cgit v1.2.3 From 6fe4e4b8259e1330945b5f3c9476e08473b8e0e8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Jul 2026 22:56:02 -0700 Subject: fscrypt: Avoid dynamic allocation in fscrypt_get_devices() When a blk_crypto_key starts being used or is evicted, fs/crypto/ calls fscrypt_get_devices() to get the filesystem's list of block devices, then iterates over them and calls blk_crypto_config_supported(), blk_crypto_start_using_key(), or blk_crypto_evict_key() on each one. Currently, the block device pointers are placed in a dynamically allocated array. This dynamic allocation is problematic because: - It can fail, especially at the fscrypt_destroy_inline_crypt_key() call site when it's invoked for inode eviction under direct reclaim. - fscrypt_destroy_inline_crypt_key() doesn't handle the failure. It just zeroizes and frees the blk_crypto_key without calling blk_crypto_evict_key(). That causes a use-after-free. For now, let's fix this in the straightforward and easily-backportable way by switching to an on-stack array. Currently the fscrypt multi-device functionality is used only by f2fs, which has a hardcoded limit of 8 block devices. An on-stack array works fine for that. (Of course, this solution won't scale up to large number of block devices. For that we'd need a different solution, like moving the block device iteration into the filesystem. Or in the case of btrfs, which will only support blk-crypto-fallback, we should make it just call blk-crypto-fallback directly, so the block devices won't be needed.) Fixes: 22e9947a4b2b ("fscrypt: stop holding extra request_queue references") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260713023708.9245-1-ebiggers%40kernel.org Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260719055602.78828-1-ebiggers@kernel.org Signed-off-by: Eric Biggers --- fs/crypto/inline_crypt.c | 57 +++++++++++++++++------------------------------- fs/f2fs/super.c | 25 +++++++++++---------- include/linux/fscrypt.h | 18 ++++++++------- 3 files changed, 44 insertions(+), 56 deletions(-) diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 47324062fee5..66b9c9150fed 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -22,22 +22,14 @@ #include "fscrypt_private.h" -static struct block_device **fscrypt_get_devices(struct super_block *sb, - unsigned int *num_devs) +static unsigned int +fscrypt_get_devices(struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]) { - struct block_device **devs; - - if (sb->s_cop->get_devices) { - devs = sb->s_cop->get_devices(sb, num_devs); - if (devs) - return devs; - } - devs = kmalloc_obj(*devs); - if (!devs) - return ERR_PTR(-ENOMEM); + if (sb->s_cop->get_devices) + return sb->s_cop->get_devices(sb, devs); devs[0] = sb->s_bdev; - *num_devs = 1; - return devs; + return 1; } static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_inode_info *ci) @@ -96,7 +88,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, const struct inode *inode = ci->ci_inode; struct super_block *sb = inode->i_sb; struct blk_crypto_config crypto_cfg; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; @@ -135,20 +127,15 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci, crypto_cfg.key_type = is_hw_wrapped_key ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; - devs = fscrypt_get_devices(sb, &num_devs); - if (IS_ERR(devs)) - return PTR_ERR(devs); - + num_devs = fscrypt_get_devices(sb, devs); for (i = 0; i < num_devs; i++) { if (!blk_crypto_config_supported(devs[i], &crypto_cfg)) - goto out_free_devs; + return 0; } fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg); ci->ci_inlinecrypt = true; -out_free_devs: - kfree(devs); return 0; } @@ -164,7 +151,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, enum blk_crypto_key_type key_type = is_hw_wrapped ? BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW; struct blk_crypto_key *blk_key; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; int err; @@ -182,17 +169,12 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, } /* Start using blk-crypto on all the filesystem's block devices. */ - devs = fscrypt_get_devices(sb, &num_devs); - if (IS_ERR(devs)) { - err = PTR_ERR(devs); - goto fail; - } + num_devs = fscrypt_get_devices(sb, devs); for (i = 0; i < num_devs; i++) { err = blk_crypto_start_using_key(devs[i], blk_key); if (err) break; } - kfree(devs); if (err) { fscrypt_err(inode, "error %d starting to use blk-crypto", err); goto fail; @@ -210,20 +192,21 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb, struct fscrypt_prepared_key *prep_key) { struct blk_crypto_key *blk_key = prep_key->blk_key; - struct block_device **devs; + struct block_device *devs[FSCRYPT_MAX_DEVICES]; unsigned int num_devs; unsigned int i; if (!blk_key) return; - /* Evict the key from all the filesystem's block devices. */ - devs = fscrypt_get_devices(sb, &num_devs); - if (!IS_ERR(devs)) { - for (i = 0; i < num_devs; i++) - blk_crypto_evict_key(devs[i], blk_key); - kfree(devs); - } + /* + * Evict the key from all the filesystem's block devices. + * This *must* be done before the key is freed. + */ + num_devs = fscrypt_get_devices(sb, devs); + for (i = 0; i < num_devs; i++) + blk_crypto_evict_key(devs[i], blk_key); + kfree_sensitive(blk_key); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2b8d96411156..9760e4efeffe 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -3749,24 +3749,27 @@ static bool f2fs_has_stable_inodes(struct super_block *sb) return true; } -static struct block_device **f2fs_get_devices(struct super_block *sb, - unsigned int *num_devs) +static unsigned int +f2fs_get_devices(struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]) { struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct block_device **devs; + int ndevs; int i; - if (!f2fs_is_multi_device(sbi)) - return NULL; + static_assert(MAX_DEVICES <= FSCRYPT_MAX_DEVICES); - devs = kmalloc_objs(*devs, sbi->s_ndevs); - if (!devs) - return ERR_PTR(-ENOMEM); + if (!f2fs_is_multi_device(sbi)) { + devs[0] = sb->s_bdev; + return 1; + } + ndevs = sbi->s_ndevs; + if (WARN_ON_ONCE(ndevs > FSCRYPT_MAX_DEVICES)) + ndevs = FSCRYPT_MAX_DEVICES; - for (i = 0; i < sbi->s_ndevs; i++) + for (i = 0; i < ndevs; i++) devs[i] = FDEV(i).bdev; - *num_devs = sbi->s_ndevs; - return devs; + return ndevs; } static const struct fscrypt_operations f2fs_cryptops = { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 54712ec61ffb..f6b235cd72b4 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -57,6 +57,9 @@ struct fscrypt_name { /* Maximum value for the third parameter of fscrypt_operations.set_context(). */ #define FSCRYPT_SET_CONTEXT_MAX_SIZE 40 +/* Maximum supported number of block devices per filesystem */ +#define FSCRYPT_MAX_DEVICES 8 + #ifdef CONFIG_FS_ENCRYPTION /* Crypto operations for filesystems */ @@ -181,21 +184,20 @@ struct fscrypt_operations { bool (*has_stable_inodes)(struct super_block *sb); /* - * Return an array of pointers to the block devices to which the - * filesystem may write encrypted file contents, NULL if the filesystem - * only has a single such block device, or an ERR_PTR() on error. + * Retrieve the list of block devices to which the filesystem may write + * encrypted file contents. * - * On successful non-NULL return, *num_devs is set to the number of - * devices in the returned array. The caller must free the returned - * array using kfree(). + * This writes the block_device pointers to @devs and returns the count + * (between 1 and FSCRYPT_MAX_DEVICES inclusively). * * If the filesystem can use multiple block devices (other than block * devices that aren't used for encrypted file contents, such as * external journal devices), and wants to support inline encryption, * then it must implement this function. Otherwise it's not needed. */ - struct block_device **(*get_devices)(struct super_block *sb, - unsigned int *num_devs); + unsigned int (*get_devices)( + struct super_block *sb, + struct block_device *devs[FSCRYPT_MAX_DEVICES]); }; int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name, -- cgit v1.2.3 From 5c3f8dac531b454bf67b6ee3c2aac89f0aaaef74 Mon Sep 17 00:00:00 2001 From: Nikita Maksimov Date: Mon, 20 Jul 2026 21:02:14 +0300 Subject: ALSA: hda/realtek: Fix speakers on Lunnen Ground 14 The firmware on the Lunnen Ground 14 marks pin 0x1b as unused even though the internal speakers are connected to it. As a result, the speakers are not detected. Add a pin configuration quirk for PCI subsystem ID 2782:a212 to configure pin 0x1b as an internal speaker. The pin configuration was tested on a Lunnen Ground 14 (DMI product LL4FA) with an ALC269VC codec. The internal speakers and microphone work as expected. Cc: stable@vger.kernel.org Signed-off-by: Nikita Maksimov Link: https://patch.msgid.link/20260720180214.73770-1-nickstogramm@yandex.ru 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 6c87e2513fe7..9c3f08f9f328 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3939,6 +3939,7 @@ enum { ALC269_FIXUP_DMIC_THINKPAD_ACPI, ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, ALC269VC_FIXUP_INFINIX_Y4_MAX, + ALC269VC_FIXUP_LUNNEN_GROUND_14, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -4408,6 +4409,13 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC269VC_FIXUP_LUNNEN_GROUND_14] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170150 }, /* internal speaker */ + { } + }, + }, [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8013,6 +8021,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), + SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), -- cgit v1.2.3 From 740b3c6780ec1f65aba2bc99a3f41d70bdb13477 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Mon, 20 Jul 2026 20:37:02 +0800 Subject: ALSA: hda/realtek: Add HDA_CODEC_QUIRK for Samsung 750XBE/730XBE Add a codec SSID quirk for Samsung ELECTRONICS 750XBE/730XBE using HDA_CODEC_QUIRK() instead of SND_PCI_QUIRK(), because the alsa-info report from this device does not expose a PCI subsystem ID, only the HDA codec subsystem ID (0x144d:0xc824) is available. This applies ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET to fix sound being very low and distorted on the headphone jack of this system. Reported-by: Caio Ramos Link: https://bugzilla.kernel.org/show_bug.cgi?id=208663 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260720123702.799474-1-zhangheng@kylinos.cn 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 9c3f08f9f328..5840afbcf949 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7656,6 +7656,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_AMP), + HDA_CODEC_QUIRK(0x144d, 0xc824, "Samsung ELECTRONICS (750XBE/730XBE)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_AMP), SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc876, "Samsung 730QED (NP730QED-KA2US)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), -- cgit v1.2.3 From b6016332b8899a9775addf9b630b0a53a849c8ed Mon Sep 17 00:00:00 2001 From: Philipp Oster Date: Mon, 20 Jul 2026 11:41:51 +0200 Subject: ALSA: hda/tas2781: clear cali_data.total_sz when calibration read fails tas2563_save_calibration() assigns cali_data.total_sz before it reads the per-device calibration data from EFI, but its error paths return without clearing it again. cali_data.cali_reg_array is left all zero, because the function returns before the register addresses are assigned. On the first playback tasdev_load_calibrated_data() does if (!data || !cali_data->total_sz) return; which passes, since total_sz is still non-zero. It then issues five 4-byte bulk writes to p->r0_reg, p->r0_low_reg, p->invr0_reg, p->pow_reg and p->tlimit_reg, all of which are 0. Register 0 decodes to book 0 / page 0 / register 0x00, so the auto-incrementing block write zeroes registers 0x00 to 0x03. Register 0x03 is PB_CFG1, which holds AMP_LEVEL, so the amplifier gain is set to its minimum and the speaker stays silent. This is reproducible on a Lenovo Yoga 7 14ARB7 (two TAS2563 on I2C, ACPI INT8866) whose factory calibration was never written to UEFI, so the EFI read fails with EFI_NOT_FOUND. The two woofers driven by the amplifiers are silent while the tweeters driven directly by the ALC287 play. Reading the amplifier registers over i2c shows PWR_CTL = 0x00 (active) and the TDM slots correctly programmed by the RCA profile, but PB_CFG1 = 0x00. With this change PB_CFG1 keeps its power-on default of 0x20 and both woofers play. tas2781_save_calibration() in tas2781_hda.c already clears total_sz on failure; do the same for the TAS2563 variant. Signed-off-by: Philipp Oster Link: https://patch.msgid.link/20260720-tas2781-calfix-v1-1-3a5fa6ad90bc@outlook.de Signed-off-by: Takashi Iwai --- sound/hda/codecs/side-codecs/tas2781_hda_i2c.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 69a22fdfeedb..624db967f17b 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -354,6 +354,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h) if (ret < 0 || ret >= sizeof(var8) - 1) { dev_err(p->dev, "%s: Read %s failed\n", __func__, var8); + cd->total_sz = 0; return -EINVAL; } /* @@ -371,6 +372,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h) dev_warn(p->dev, "Dev %d: Caldat[%d] read failed %ld\n", i, j, status); + cd->total_sz = 0; return -EINVAL; } bedata = cpu_to_be32(*(uint32_t *)&data[offset]); @@ -382,6 +384,7 @@ static int tas2563_save_calibration(struct tas2781_hda *h) if (cd->total_sz != offset) { dev_err(p->dev, "%s: tot_size(%lu) and offset(%u) mismatch\n", __func__, cd->total_sz, offset); + cd->total_sz = 0; return -EINVAL; } -- cgit v1.2.3 From db3dbdfea1b8f38774419c5c2c14e4b81c48708d Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 14 Jun 2026 22:38:06 +0900 Subject: of: reserved_mem: prevent OOB when too many dynamic regions are defined On boot, fdt_scan_reserved_mem() saves each dynamically-placed /reserved-memory subnode into a local array of size MAX_RESERVED_REGIONS. If the device tree defines more than MAX_RESERVED_REGIONS dynamically-placed regions, fdt_scan_reserved_mem() writes past the end of the local array. Add a bounds check that logs an error and skips the excess regions, restoring the original behavior. Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed") Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/of/of_reserved_mem.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 82222bd45ac6..42e3e2d8a2b8 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -359,6 +359,7 @@ int __init fdt_scan_reserved_mem(void) err = __reserved_mem_reserve_reg(child, uname); if (!err) count++; + /* * Save the nodes for the dynamically-placed regions * into an array which will be used for allocation right @@ -366,10 +367,17 @@ int __init fdt_scan_reserved_mem(void) * or marked as no-map. This is done to avoid dynamically * allocating from one of the statically-placed regions. */ - if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) { - dynamic_nodes[dynamic_nodes_cnt] = child; - dynamic_nodes_cnt++; + if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL)) + continue; + + if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) { + pr_err("too many defined dynamic regions, skip '%s'\n", + uname); + continue; } + + dynamic_nodes[dynamic_nodes_cnt] = child; + dynamic_nodes_cnt++; } for (int i = 0; i < dynamic_nodes_cnt; i++) { const char *uname; -- cgit v1.2.3 From bbf5f639918dc011aaf60aab8480218758ee68c5 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 20 Jul 2026 14:36:49 +0200 Subject: binfmt_misc: set have_execfd only once the interpreter is opened load_misc_binary() raises bprm->have_execfd as soon as it sees the 'O' (or 'C') flag. This happens well before it opens the interpreter. If that open fails the flag stays set on the bprm. binfmt_misc is at the head of the format list so an interpreter open failure that returns -ENOEXEC lets the search fall through to a later format. This means it runs the matched binary directly having never staged an interpreter. So bprm->executable is NULL while have_execfd falsely claims a descriptor is present. Consequently, begin_new_exec() dereferences the missing executable: would_dump(bprm, bprm->executable); and NULL derefs. Had it not, the hand-off later in the same function would have failed anyway. FD_ADD(0, bprm->executable) rejects a NULL file with -ENOMEM. Both sites are past the point of no return so the exec cannot be unwound either way. This can be reached by unprivileged users as binfmt_misc can be mounted in user namespaces. So a user can register an 'O' entry whose interpreter lives on a FUSE mount, have the FUSE server fail the open with -ENOEXEC and execute a native ELF file that matches the entry. have_execfd only means anything alongside the executable it describes which is not set until the interpreter has been opened and staged. So lets raise it there, next to execfd_creds, which is already set at that point. An open failure now leaves it clear, so the fallback format derives credentials from the binary and emits no AT_EXECFD, as it would for any native exec. The argv rewrite load_misc_binary() performs before the open is still not undone. This means the binary sees the interpreter path in argv[0] and its own path in argv[1] but that predates this change and only became observable once the exec stopped faulting. Link: https://patch.msgid.link/20260720-beglichen-kognitiv-organismus-5e1e55326c56@brauner Fixes: bc2bf338d54b ("exec: Remove recursion from search_binary_handler") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 84349fcb93f1..5de615ca7a75 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -228,9 +228,6 @@ static int load_misc_binary(struct linux_binprm *bprm) goto ret; } - if (fmt->flags & MISC_FMT_OPEN_BINARY) - bprm->have_execfd = 1; - /* make argv[1] be the path to the binary */ retval = copy_string_kernel(bprm->interp, bprm); if (retval < 0) @@ -260,6 +257,8 @@ static int load_misc_binary(struct linux_binprm *bprm) goto ret; bprm->interpreter = interp_file; + if (fmt->flags & MISC_FMT_OPEN_BINARY) + bprm->have_execfd = 1; if (fmt->flags & MISC_FMT_CREDENTIALS) bprm->execfd_creds = 1; -- cgit v1.2.3 From e9027ffbf5a0f3c12ca8900822e884eae9f0821b Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Mon, 20 Jul 2026 00:24:27 +0800 Subject: Bluetooth: hci_sync: Protect UUID list traversal The hci_sync conversion moved class-of-device and EIR generation from an HCI request built under hdev->lock to asynchronous command sync work. The worker holds hdev->req_lock, but that lock does not serialize access to hdev->uuids against add_uuid() and remove_uuid(), which update the list under hdev->lock. The following interleaving can therefore occur: CPU0 (command sync work) CPU1 (management socket) fetch uuid from the list list_del(&uuid->list) kfree(uuid) read uuid->size KASAN reports the resulting use-after-free: BUG: KASAN: slab-use-after-free in eir_create+0xb8f/0xee0 Read of size 1 at addr ffff88810dbd8620 by task kworker/u17:0/87 Workqueue: hci0 hci_cmd_sync_work Call Trace: eir_create+0xb8f/0xee0 hci_update_eir_sync+0x1c0/0x330 hci_cmd_sync_work+0x13c/0x290 process_one_work+0x63a/0x1070 worker_thread+0x45b/0xd10 Allocated by task 86: __kasan_kmalloc+0x8f/0xa0 add_uuid+0x18a/0x4b0 hci_sock_sendmsg+0x1033/0x1ea0 Freed by task 92: __kasan_slab_free+0x43/0x70 kfree+0x131/0x3c0 remove_uuid+0x25e/0x560 hci_sock_sendmsg+0x1033/0x1ea0 Hold hdev->lock while generating and committing the class-of-device and EIR snapshots. Release it before sending an HCI command, so controller waits do not happen under the device lock. This protects all UUID list walks in these paths and restores the serialization lost in the command sync conversion. Fixes: 161510ccf91c ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 1") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 532534bc601c..c0b1fc293b49 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -929,12 +929,16 @@ int hci_update_eir_sync(struct hci_dev *hdev) memset(&cp, 0, sizeof(cp)); + hci_dev_lock(hdev); eir_create(hdev, cp.data); - if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) + if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) { + hci_dev_unlock(hdev); return 0; + } memcpy(hdev->eir, cp.data, sizeof(cp.data)); + hci_dev_unlock(hdev); return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp, HCI_CMD_TIMEOUT); @@ -966,6 +970,7 @@ int hci_update_class_sync(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) return 0; + hci_dev_lock(hdev); cod[0] = hdev->minor_class; cod[1] = hdev->major_class; cod[2] = get_service_classes(hdev); @@ -973,8 +978,12 @@ int hci_update_class_sync(struct hci_dev *hdev) if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) cod[1] |= 0x20; - if (memcmp(cod, hdev->dev_class, 3) == 0) + if (memcmp(cod, hdev->dev_class, 3) == 0) { + hci_dev_unlock(hdev); return 0; + } + + hci_dev_unlock(hdev); return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod, HCI_CMD_TIMEOUT); -- cgit v1.2.3 From c783399efc22d035443f1dfbf2a09bf9562aaa5e Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Mon, 20 Jul 2026 00:03:11 +0800 Subject: Bluetooth: RFCOMM: Fix session UAF in set_termios rfcomm_tty_set_termios() tests dlc->session without rfcomm_mutex and later passes the pointer to rfcomm_send_rpn(). The latter dereferences both session->initiator and session->sock. Meanwhile, krfcommd can unlink the DLC and free the session while holding rfcomm_mutex. The race can proceed as follows: TTY ioctl task krfcommd -------------- -------- load dlc->session enter rfcomm_send_rpn() lock rfcomm_mutex clear dlc->session free session unlock rfcomm_mutex read session->initiator KASAN reported: BUG: KASAN: slab-use-after-free in rfcomm_send_rpn+0x297/0x2a0 Read of size 4 at addr ffff88810012a850 by task poc/92 Call Trace: rfcomm_send_rpn+0x297/0x2a0 rfcomm_tty_set_termios+0x50d/0x850 tty_set_termios+0x596/0x950 set_termios+0x46a/0x6e0 tty_mode_ioctl+0x152/0xbd0 tty_ioctl+0x915/0x1240 __x64_sys_ioctl+0x134/0x1c0 Allocated by task 92: rfcomm_session_add+0x9e/0x2e0 rfcomm_dlc_open+0x8b1/0xe00 rfcomm_dev_activate+0x85/0x1a0 rfcomm_tty_open+0x90/0x280 Freed by task 68: kfree+0x131/0x3c0 rfcomm_session_del+0x119/0x180 rfcomm_run+0x737/0x4710 Add rfcomm_dlc_send_rpn(), which holds rfcomm_mutex while it verifies that the DLC is still attached and sends the RPN frame. Have the TTY path use the helper and drop its unlocked session check. This keeps the session valid through both the frame construction and socket send. Fixes: 3a5e903c09ae ("[Bluetooth]: Implement RFCOMM remote port negotiation") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/rfcomm.h | 3 +++ net/bluetooth/rfcomm/core.c | 17 +++++++++++++++++ net/bluetooth/rfcomm/tty.c | 7 +++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index feb6b3ae5e69..102c278e3584 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -226,6 +226,9 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, u8 bit_rate, u8 data_bits, u8 stop_bits, u8 parity, u8 flow_ctrl_settings, u8 xon_char, u8 xoff_char, u16 param_mask); +int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits, + u8 stop_bits, u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask); /* ---- RFCOMM DLCs (channels) ---- */ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index ebeae17b71d1..75f7512dec54 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1028,6 +1028,23 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, return rfcomm_send_frame(s, buf, ptr - buf); } +int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits, + u8 stop_bits, u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask) +{ + int err = -ENOTCONN; + + rfcomm_lock(); + if (d->session) + err = rfcomm_send_rpn(d->session, 1, d->dlci, bit_rate, + data_bits, stop_bits, parity, + flow_ctrl_settings, xon_char, xoff_char, + param_mask); + rfcomm_unlock(); + + return err; +} + static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) { struct rfcomm_hdr *hdr; diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 4b9a699ec59b..b2c1060394e6 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -858,7 +858,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, BT_DBG("tty %p termios %p", tty, old); - if (!dev || !dev->dlc || !dev->dlc->session) + if (!dev || !dev->dlc) return; /* Handle turning off CRTSCTS */ @@ -979,9 +979,8 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, } if (changes) - rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud, - data_bits, stop_bits, parity, - RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); + rfcomm_dlc_send_rpn(dev->dlc, baud, data_bits, stop_bits, parity, + RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); } static void rfcomm_tty_throttle(struct tty_struct *tty) -- cgit v1.2.3 From df541cd485ff80a5ddc579d99687bc7506df9851 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:47:17 +0800 Subject: Bluetooth: btusb: validate Realtek vendor event length btusb_recv_event_realtek() reads the event code at data[0] and the Realtek subevent code at data[2] before deciding whether to consume a vendor event as a coredump. For example, the two-byte event ff 00 contains a complete vendor-event header declaring zero parameters. The old classifier still reads a nonexistent third byte and can misclassify the event as a coredump if the adjacent byte is 0x34. Require the HCI event header and first parameter to be present before inspecting the Realtek subevent code. Short events continue through the normal HCI receive path, which owns their protocol validation. Fixes: 044014ce85a1 ("Bluetooth: btrtl: Add Realtek devcoredump support") Signed-off-by: Pengpeng Hou Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 08c0a99a62c5..8f7ed469cac6 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2782,7 +2782,9 @@ static int btusb_setup_realtek(struct hci_dev *hdev) static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb) { - if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) { + if (skb->len >= HCI_EVENT_HDR_SIZE + 1 && + skb->data[0] == HCI_VENDOR_PKT && + skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) { struct rtk_dev_coredump_hdr hdr = { .code = RTK_DEVCOREDUMP_CODE_MEMDUMP, }; -- cgit v1.2.3 From b056f21a38276ead20353d71d50a52206609d242 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 19 Jun 2026 18:18:21 -0700 Subject: power: supply: bd71828: add a terminating table border Fix a documentation build error by adding a bottom table border: Documentation/ABI/testing/sysfs-class-power-bd71828:1: ERROR: Malformed table. No bottom table border found. ============ =========================================== 1 automatic adjustment of input current limit 0 no adjustment of input current limit. This helps for more unusual power sources like solar modules. [docutils] Fixes: e92786dd86a2 ("power: supply: bd71828: sysfs for auto input current limitation") Signed-off-by: Randy Dunlap Acked-by: Matti Vaittinen Link: https://patch.msgid.link/20260620011821.3568674-1-rdunlap@infradead.org Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power-bd71828 | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/ABI/testing/sysfs-class-power-bd71828 b/Documentation/ABI/testing/sysfs-class-power-bd71828 index 2d451e1c8336..f9a325e005f3 100644 --- a/Documentation/ABI/testing/sysfs-class-power-bd71828 +++ b/Documentation/ABI/testing/sysfs-class-power-bd71828 @@ -10,3 +10,4 @@ Description: 0 no adjustment of input current limit. This helps for more unusual power sources like solar modules. + ============ =========================================== -- cgit v1.2.3 From 725668c6b6aa3971fe850659102c250d0d676e18 Mon Sep 17 00:00:00 2001 From: Jianing Li Date: Wed, 1 Jul 2026 14:10:42 +0800 Subject: power: supply: max17040: handle missing status supplier MAX17040 does not report charger state itself, so the driver forwards POWER_SUPPLY_PROP_STATUS to a supplier power supply. If no supplier is registered, power_supply_get_property_from_supplier() returns -ENODEV and leaves the output value untouched. max17040_get_property() currently ignores that error and returns success, so userspace can read an uninitialized status value from the battery power supply. This happens on systems that use the fuel gauge without a charger supplier relationship in firmware. Return POWER_SUPPLY_STATUS_UNKNOWN when no supplier provides STATUS, and propagate other supplier lookup errors. Fixes: f4b782af61ae ("power: max17040: pass status property from supplier") Cc: stable@vger.kernel.org # 6.7+ Signed-off-by: Jianing Li Link: https://patch.msgid.link/20260701061042.1008-1-m13940358460@163.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17040_battery.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index e94d53b36aa4..03ac569e185c 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -405,7 +405,11 @@ static int max17040_get_property(struct power_supply *psy, val->intval = chip->low_soc_alert; break; case POWER_SUPPLY_PROP_STATUS: - power_supply_get_property_from_supplier(psy, psp, val); + ret = power_supply_get_property_from_supplier(psy, psp, val); + if (ret == -ENODEV) + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + else if (ret) + return ret; break; case POWER_SUPPLY_PROP_TEMP: if (!chip->channel_temp) -- cgit v1.2.3 From f7b253a6e217f71d754d70c525e9b4c1dcbd4414 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sun, 12 Jul 2026 00:48:56 +0200 Subject: power: supply: macsmc: Support macOS 27 SMC firmware The SMC firmware included in macOS 27 changed the size of BCF0 key from 4 to 1 bytes. This key is used for indicating that battery state is critically low. In addition, B0RM key has changed endianness. Reviewed-by: Sven Peter Reviewed-by: Joshua Peisach Reviewed-by: Janne Grunau Cc: stable@vger.kernel.org Fixes: 0ebf821cf6c7 ("power: supply: Add macsmc-power driver for Apple Silicon") Signed-off-by: Sasha Finkelstein Link: https://patch.msgid.link/20260712-gate-power-v4-1-aa59c6583247@chaosmail.tech Signed-off-by: Sebastian Reichel --- drivers/power/supply/macsmc-power.c | 52 ++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c index ced07f71e0a8..ef735f86394e 100644 --- a/drivers/power/supply/macsmc-power.c +++ b/drivers/power/supply/macsmc-power.c @@ -86,6 +86,11 @@ struct macsmc_power { bool has_ch0i; /* Force discharge (Older firmware) */ bool has_ch0c; /* Inhibit charge (Older firmware) */ bool has_chte; /* Inhibit charge (Modern firmware) */ + /* + * Battery critical key is 1 byte and charge key is little endian + * (Modern firmware) + */ + bool fw_ge_27; u8 num_cells; int nominal_voltage_mv; @@ -273,6 +278,20 @@ static int macsmc_battery_get_date(const char *s, int *out) return 0; } +static int macsmc_battery_read_bcf0(struct macsmc_power *power, u32 *val) +{ + u8 tval = 0; + int ret; + + if (power->fw_ge_27) { + ret = apple_smc_read_u8(power->smc, SMC_KEY(BCF0), &tval); + *val = tval; + return ret; + } + + return apple_smc_read_u32(power->smc, SMC_KEY(BCF0), val); +} + static int macsmc_battery_get_capacity_level(struct macsmc_power *power) { bool flag; @@ -280,7 +299,7 @@ static int macsmc_battery_get_capacity_level(struct macsmc_power *power) int ret; /* Check for emergency shutdown condition */ - if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val) >= 0 && val) + if (macsmc_battery_read_bcf0(power, &val) >= 0 && val) return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; /* Check AC status for whether we could boot in this state */ @@ -303,6 +322,12 @@ static int macsmc_battery_get_capacity_level(struct macsmc_power *power) return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; } +static s16 macsmc_swap_b0rm(struct macsmc_power *power, s16 b0rm) +{ + /* B0RM was Big Endian, likely pass through from TI gas gauge */ + return power->fw_ge_27 ? b0rm : (s16)swab16(b0rm); +} + static int macsmc_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -397,8 +422,7 @@ static int macsmc_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CHARGE_NOW: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); - /* B0RM is Big Endian, likely pass through from TI gas gauge */ - val->intval = (s16)swab16(vu16) * 1000; + val->intval = macsmc_swap_b0rm(power, vu16) * 1000; break; case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); @@ -410,8 +434,7 @@ static int macsmc_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_ENERGY_NOW: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); - /* B0RM is Big Endian, likely pass through from TI gas gauge */ - val->intval = (s16)swab16(vu16) * power->nominal_voltage_mv; + val->intval = macsmc_swap_b0rm(power, vu16) * power->nominal_voltage_mv; break; case POWER_SUPPLY_PROP_TEMP: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16); @@ -577,7 +600,7 @@ static void macsmc_power_critical_work(struct work_struct *wrk) * Check if SMC flagged the battery as empty. * We trigger a graceful shutdown to let the OS save data. */ - if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &bcf0) == 0 && bcf0 != 0) { + if (macsmc_battery_read_bcf0(power, &bcf0) == 0 && bcf0 != 0) { power->orderly_shutdown_triggered = true; dev_crit(power->dev, "Battery critical (empty flag set). Triggering orderly shutdown.\n"); orderly_poweroff(true); @@ -616,6 +639,7 @@ static int macsmc_power_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; + struct apple_smc_key_info info; struct macsmc_power *power; bool has_battery = false; bool has_ac_adapter = false; @@ -714,6 +738,20 @@ static int macsmc_power_probe(struct platform_device *pdev) if (apple_smc_key_exists(smc, SMC_KEY(CH0I))) power->has_ch0i = true; + ret = apple_smc_get_key_info(power->smc, SMC_KEY(BCF0), &info); + if (ret) { + dev_err(&pdev->dev, "Failed to determine BCF0 key size\n"); + return ret; + } + if (info.size == 1) + power->fw_ge_27 = true; + else if (info.size == 4) + power->fw_ge_27 = false; + else { + dev_err(&pdev->dev, "Unexpected BCF0 key size %d\n", info.size); + return -EIO; + } + /* Reset "Optimised Battery Charging" flags to default state */ if (power->has_chte) apple_smc_write_u32(smc, SMC_KEY(CHTE), 0); @@ -766,7 +804,7 @@ static int macsmc_power_probe(struct platform_device *pdev) power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells; /* Enable critical shutdown notifications by reading status once */ - apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val32); + macsmc_battery_read_bcf0(power, &val32); psy_cfg.drv_data = power; power->batt = devm_power_supply_register(dev, &power->batt_desc, &psy_cfg); -- cgit v1.2.3 From ea3034b2b00fa50c8d2518d0804c9d427bbafa86 Mon Sep 17 00:00:00 2001 From: Gregory Price Date: Sat, 27 Jun 2026 16:22:43 -0400 Subject: mm/vmstat: fold stranded per-cpu node stats when a node comes online A per-node vmstat counter is pgdat->vm_stat[] plus per-cpu deltas. A balanced counter can sit split as global=+N / per-cpu=-N. The folds reconciling the split only walk online nodes, so when try_offline_node() marks a node offline the per-cpu deltas are stranded. A subsequent online resets the per-cpu area but not pgdat->vm_stat[], orphaning the +N permanently. All NR_VM_NODE_STAT_ITEMS are affected. The existing code zeroes the per-cpu counters and causes a permanent skew. Fold the stranded deltas instead, before the node rejoins the online set. The node is not online yet and the hotplug lock is held, so the remote access to per-cpu values is safe. Discovered when node compaction hung for a nearly empty node, as the math to determine throttling broke. Reproduced by repeated memory hotplug/unplug cycles on a node under pressure: NR_ISOLATED_ANON ratchets up and never returns to zero. Link: https://lore.kernel.org/20260627202243.758289-1-gourry@gourry.net Fixes: 75ef71840539 ("mm, vmstat: add infrastructure for per-node vmstats") Signed-off-by: Gregory Price Cc: Johannes Weiner Cc: Mel Gorman Cc: Mike Rapoport Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/mm_init.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 0f64909e8d20..498d62c4ece3 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -1540,7 +1540,7 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) { int nid = pgdat->node_id; enum zone_type z; - int cpu; + int cpu, i; pgdat_init_internals(pgdat); @@ -1558,10 +1558,17 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat) pgdat->node_start_pfn = 0; pgdat->node_present_pages = 0; - for_each_online_cpu(cpu) { - struct per_cpu_nodestat *p; + /* + * Hot-unplug can leave per-cpu vmstat deltas unfolded (folders skip + * offline nodes) - reconcile this at online. Foreign access to counters + * is safe: the node is not online yet and we hold the hotplug lock. + */ + for_each_possible_cpu(cpu) { + struct per_cpu_nodestat *p = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu); - p = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu); + for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) + if (p->vm_node_stat_diff[i]) + node_page_state_add(p->vm_node_stat_diff[i], pgdat, i); memset(p, 0, sizeof(*p)); } -- cgit v1.2.3 From 4fc089235378d1f9ddb6bcbe67c192ffdcaae0ed Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Mon, 29 Jun 2026 16:30:14 -0700 Subject: lib: test_hmm: use device devt for coherent device range selection Commit af69016dab96 ("lib: test_hmm: implement a device release method") moved the initial dmirror_allocate_chunk() call before cdev_device_add(). That means the struct cdev has not been added yet, so cdev_add() has not initialized mdevice->cdevice.dev. The coherent-device range selection uses the device minor to choose between spm_addr_dev0 and spm_addr_dev1. Reading MINOR(mdevice->cdevice.dev) before cdev_add() therefore always sees an uninitialized dev_t. As a result, both coherent devices select the same physical range, and adding the second device fails due to the overlapping dev_pagemap range. Use mdevice->device.devt instead. It is initialized in dmirror_device_init() before dmirror_allocate_chunk() is called and is the same dev_t later passed to cdev_device_add(). Link: https://lore.kernel.org/178277581197.172200.16265155329935822153.stgit@skinsburskii Fixes: af69016dab96 ("lib: test_hmm: implement a device release method") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Alistair Popple Cc: Balbir Singh Cc: Zenghui Yu (Huawei) Cc: Jason Gunthorpe Cc: Leon Romanovsky Cc: Signed-off-by: Andrew Morton --- lib/test_hmm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_hmm.c b/lib/test_hmm.c index 9c59d1ceb5b5..c4adbf98fac7 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -581,7 +581,7 @@ static int dmirror_allocate_chunk(struct dmirror_device *mdevice, devmem->pagemap.type = MEMORY_DEVICE_PRIVATE; break; case HMM_DMIRROR_MEMORY_DEVICE_COHERENT: - devmem->pagemap.range.start = (MINOR(mdevice->cdevice.dev) - 2) ? + devmem->pagemap.range.start = (MINOR(mdevice->device.devt) - 2) ? spm_addr_dev0 : spm_addr_dev1; devmem->pagemap.range.end = devmem->pagemap.range.start + -- cgit v1.2.3 From 4165b7d1c45c2da0dfefe528f8d1fb7d79f0d344 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Sun, 5 Jul 2026 06:12:31 -0700 Subject: userfaultfd: wait on source PMD during UFFDIO_MOVE move_pages_huge_pmd() snapshots src_pmdval under src_ptl, drops the lock, and, for migration entries, waits with pmd_migration_entry_wait(). Passing &src_pmdval is wrong. pmd_migration_entry_wait() must lock and re-read the real page-table PMD; on split-PMD-lock kernels, a stack address also resolves to the wrong lock. softleaf_entry_wait_on_locked() then waits without a folio reference, which is safe only while serialized against migration-entry removal by the real PT lock. Pass src_pmd, matching __handle_mm_fault() and hmm_vma_walk_pmd(). Link: https://lore.kernel.org/20260705131231.1499198-1-usama.arif@linux.dev Fixes: adef440691ba ("userfaultfd: UFFDIO_MOVE uABI") Reported-by: sashiko-bot Link: https://sashiko.dev/#/patchset/20260703173903.3789516-1-usama.arif%40linux.dev?part=8 Signed-off-by: Usama Arif Reviewed-by: Rik van Riel Reviewed-by: Baolin Wang Reviewed-by: Lance Yang Acked-by: David Hildenbrand (Arm) Reviewed-by: Lance Yang Reviewed-by: Lorenzo Stoakes Cc: Andrea Arcangeli Cc: Barry Song Cc: Dev Jain Cc: Johannes Weiner Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Shakeel Butt Cc: Zi Yan Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index b5d1e9d4463d..032702a4637b 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2774,7 +2774,7 @@ int move_pages_huge_pmd(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd, pm if (!pmd_trans_huge(src_pmdval)) { spin_unlock(src_ptl); if (pmd_is_migration_entry(src_pmdval)) { - pmd_migration_entry_wait(mm, &src_pmdval); + pmd_migration_entry_wait(mm, src_pmd); return -EAGAIN; } return -ENOENT; -- cgit v1.2.3 From 07b4377bdbe74a3ec0c8da5849d014f70e003384 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Tue, 7 Jul 2026 16:13:49 +0100 Subject: fs/proc/task_mmu: fix PAGEMAP_SCAN written state for unpopulated ptes PAGEMAP_SCAN reports an unpopulated pte differently depending on which path serves the request. The PAGE_IS_WRITTEN fast path in pagemap_scan_pmd_entry() reports a pte_none as written (and, under PM_SCAN_WP_MATCHING, arms a marker); pagemap_page_category() returns 0 for the same pte_none. A request that cannot take the fast path (an extra category bit, category_anyof_mask or category_inverted) therefore reports the pte as clean and skips arming it. A range that was populated and then MADV_DONTNEED'd reads as written via one mask and clean via another, and in the latter case is not re-armed for the next round -- an incremental-dump consumer (e.g. CRIU) using a richer mask drops the zapped range and stops tracking writes to it. Report pte_none as written in pagemap_page_category() too. A pte_none carries no uffd-wp marker, i.e. it is not write-protected -- the same condition under which the present and swap cases already report PAGE_IS_WRITTEN. The fast path applies no VMA test, so neither does this. The hugetlb and fully-unpopulated-PMD (no page table) scans have no PAGE_IS_WRITTEN fast path, so they do not exhibit the per-entry divergence and are left unchanged. Add a pagemap_ioctl selftest that populates a range, drops it with MADV_DONTNEED, and checks that the fast path and the generic (category_anyof_mask) path both report every page written. Link: https://lore.kernel.org/20260707151349.92143-1-kirill@shutemov.name Fixes: 12f6b01a0bcb ("fs/proc/task_mmu: add fast paths to get/clear PAGE_IS_WRITTEN flag") Signed-off-by: Kiryl Shutsemau Cc: Muhammad Usama Anjum Cc: David Hildenbrand Cc: Jann Horn Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Peter Xu Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Assisted-by: Claude:claude-fable-5 Cc: Signed-off-by: Andrew Morton --- fs/proc/task_mmu.c | 14 ++++++-- tools/testing/selftests/mm/pagemap_ioctl.c | 56 +++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index d32408f7cd5e..d45c729ab6bb 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2432,8 +2432,18 @@ static unsigned long pagemap_page_category(struct pagemap_scan_private *p, { unsigned long categories; - if (pte_none(pte)) - return 0; + if (pte_none(pte)) { + /* + * An unpopulated pte carries no uffd-wp marker, i.e. it is not + * write-protected, the same condition under which the present + * and swap cases below report PAGE_IS_WRITTEN. Report it here + * too so this generic path agrees with the PAGE_IS_WRITTEN fast + * path in pagemap_scan_pmd_entry(), which reports pte_none as + * written and, under PM_SCAN_WP_MATCHING, arms a marker. The + * fast path applies no VMA test, so neither does this. + */ + return PAGE_IS_WRITTEN; + } if (pte_present(pte)) { struct page *page; diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index 6f8971d5b3ce..f9bcff8e78fa 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -1051,6 +1051,57 @@ static void test_simple(void) ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__); } +/* + * A range that was populated and then MADV_DONTNEED'd is genuine pte_none + * with no uffd-wp marker. Such a pte must read the same regardless of which + * PAGEMAP_SCAN path serves the request: both the PAGE_IS_WRITTEN fast path and + * the generic path (reached e.g. via category_anyof_mask) must report every + * page written. + */ +static void unpopulated_scan_test(void) +{ + int npages = 16, i; + long mem_size = npages * page_size; + struct page_region regions[16]; + long fast = 0, slow = 0, ret; + char *mem; + + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (mem == MAP_FAILED) + ksft_exit_fail_msg("%s mmap failed\n", __func__); + + wp_init(mem, mem_size); + + /* Populate, then drop: the ptes become pte_none without a marker. */ + memset(mem, 1, mem_size); + if (madvise(mem, mem_size, MADV_DONTNEED)) + ksft_exit_fail_msg("%s MADV_DONTNEED failed\n", __func__); + + /* Fast path: category_mask == return_mask == PAGE_IS_WRITTEN. */ + ret = pagemap_ioctl(mem, mem_size, regions, npages, 0, 0, + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); + if (ret < 0) + ksft_exit_fail_msg("%s fast scan failed\n", __func__); + for (i = 0; i < ret; i++) + fast += LEN(regions[i]); + + /* Generic path: same query expressed via category_anyof_mask. */ + ret = pagemap_ioctl(mem, mem_size, regions, npages, 0, 0, + 0, PAGE_IS_WRITTEN, 0, PAGE_IS_WRITTEN); + if (ret < 0) + ksft_exit_fail_msg("%s generic scan failed\n", __func__); + for (i = 0; i < ret; i++) + slow += LEN(regions[i]); + + ksft_test_result(fast == npages && slow == npages, + "%s unpopulated ptes reported written by both paths (%ld, %ld of %d)\n", + __func__, fast, slow, npages); + + wp_free(mem, mem_size); + munmap(mem, mem_size); +} + int sanity_tests(void) { unsigned long long mem_size, vec_size; @@ -1559,7 +1610,7 @@ int main(int __attribute__((unused)) argc, char *argv[]) if (!hugetlb_setup_default(4)) ksft_print_msg("HugeTLB test will be skipped\n"); - ksft_set_plan(117); + ksft_set_plan(118); page_size = getpagesize(); hpage_size = read_pmd_pagesize(); @@ -1737,6 +1788,9 @@ int main(int __attribute__((unused)) argc, char *argv[]) /* 17. ZEROPFN tests */ zeropfn_tests(); + /* 18. Unpopulated pte scan-path consistency */ + unpopulated_scan_test(); + close(pagemap_fd); ksft_finished(); } -- cgit v1.2.3 From 63867c82d0c0c2d182016a32b1cc0103116b0ea5 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Mon, 6 Jul 2026 19:19:58 +0800 Subject: mm: migrate_device: fix pte_pfn/pte_dirty called on non-present PTE pte_pfn() and pte_dirty() have undefined behaviour when called on a non-present PTE. In migrate_vma_collect_pmd(), these functions may be invoked on non-present entries (e.g., device-private entries), leading to potential crashes from pte_pfn() or incorrect dirty folio accounting from pte_dirty(). Fix both by guarding with pte_present() checks. Link: https://lore.kernel.org/20260708003955.4024340-1-wangkefeng.wang@huawei.com Link: https://lore.kernel.org/20260706111958.3649651-1-wangkefeng.wang@huawei.com Fixes: fd35ca3d12cc ("mm/migrate_device.c: copy pte dirty bit to page") Fixes: 6c287605fd56 ("mm: remember exclusively mapped anonymous pages with PG_anon_exclusive") Signed-off-by: Kefeng Wang Reviewed-by: Balbir Singh Acked-by: Zi Yan Cc: Alistair Popple Cc: Byungchul Park Cc: David Hildenbrand Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Matthew Brost Cc: Rakie Kim Cc: Ying Huang Cc: Signed-off-by: Andrew Morton --- mm/migrate_device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/migrate_device.c b/mm/migrate_device.c index 554754eb26ff..908d2d4ec43a 100644 --- a/mm/migrate_device.c +++ b/mm/migrate_device.c @@ -401,7 +401,8 @@ again: bool anon_exclusive; pte_t swp_pte; - flush_cache_page(vma, addr, pte_pfn(pte)); + if (pte_present(pte)) + flush_cache_page(vma, addr, pte_pfn(pte)); anon_exclusive = folio_test_anon(folio) && PageAnonExclusive(page); if (anon_exclusive) { @@ -422,7 +423,7 @@ again: migrate->cpages++; /* Set the dirty flag on the folio now the pte is gone. */ - if (pte_dirty(pte)) + if (pte_present(pte) && pte_dirty(pte)) folio_mark_dirty(folio); /* Setup special migration page table entry */ -- cgit v1.2.3 From 83abe2fd5b3aeb3123b5408a5a91709c5538fb23 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Wed, 8 Jul 2026 10:01:10 +0100 Subject: mm/hugetlb: fix swap entry corruption when clearing uffd-wp at fork() copy_hugetlb_page_range() clears the uffd-wp bit of migration and hwpoison entries with huge_pte_clear_uffd_wp(), which operates on the present-PTE bit position. Swap entries keep the uffd-wp state elsewhere -- the migration branch reads and sets it with pte_swp_uffd_wp() and pte_swp_mkuffd_wp() -- and the present-PTE position falls into the swap payload. On x86-64 it lands in the inverted swap offset, where a naturally-aligned hugetlb PFN always has the affected bit set, so the clear advances the encoded PFN by two pages. No userfaultfd needs to be involved: the clear is guarded only by the child VMA not being uffd-wp registered, so a plain fork() with an in-flight hugetlb migration entry (or a poisoned hugetlb page) corrupts the entry copied into the child. Instrumenting the clear and forking after MADV_HWPOISON on a 2MB anon hugetlb page shows: offset before=120e00 offset after =120e02 The fallout is mostly latent: rmap walks match migration entries by folio range and remove_migration_pte() rebuilds the PTE from the folio, so a within-folio PFN skew heals once migration completes. But any path that re-encodes the corrupted offset -- e.g. hugetlb_change_protection() rewriting a writable migration entry via make_readable_migration_entry(swp_offset(entry)) -- propagates it. Migration entries legitimately carry uffd-wp, so clear it with pte_swp_clear_uffd_wp(), matching copy_nonpresent_pte() and move_huge_pte(). A hwpoison entry, on the other hand, never carries the uffd-wp bit: it is installed fresh by make_hwpoison_entry() (try_to_unmap_one() does not preserve uffd-wp on the hwpoison path) and hugetlb_change_protection() leaves hwpoison entries untouched. There was nothing to clear there, only the corruption, so drop the clear entirely. Link: https://lore.kernel.org/20260708090110.136162-1-kirill@shutemov.name Fixes: bc70fbf269fd ("mm/hugetlb: handle uffd-wp during fork()") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Closes: https://lore.kernel.org/all/20260703140011.99E601F000E9@smtp.kernel.org/ Suggested-by: David Hildenbrand Acked-by: David Hildenbrand (Arm) Assisted-by: Claude:claude-fable-5 Cc: Muchun Song Cc: Oscar Salvador Cc: Peter Xu Cc: Signed-off-by: Andrew Morton --- mm/hugetlb.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 571212b80835..bca2707d02e3 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -4917,8 +4917,12 @@ again: softleaf = softleaf_from_pte(entry); if (unlikely(softleaf_is_hwpoison(softleaf))) { - if (!userfaultfd_wp(dst_vma)) - entry = huge_pte_clear_uffd_wp(entry); + /* + * A hwpoison entry never carries the uffd-wp bit: it is + * installed fresh by make_hwpoison_entry() and + * hugetlb_change_protection() leaves it untouched, so + * there is nothing to clear for the child. + */ set_huge_pte_at(dst, addr, dst_pte, entry, sz); } else if (unlikely(softleaf_is_migration(softleaf))) { bool uffd_wp = pte_swp_uffd_wp(entry); @@ -4936,7 +4940,7 @@ again: set_huge_pte_at(src, addr, src_pte, entry, sz); } if (!userfaultfd_wp(dst_vma)) - entry = huge_pte_clear_uffd_wp(entry); + entry = pte_swp_clear_uffd_wp(entry); set_huge_pte_at(dst, addr, dst_pte, entry, sz); } else if (unlikely(pte_is_marker(entry))) { const pte_marker marker = copy_pte_marker(softleaf, dst_vma); -- cgit v1.2.3 From 7441d6348c70738e9ed307510db171c7a9b3f4bf Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Thu, 9 Jul 2026 01:49:54 +0530 Subject: mm/util: don't read __page_2 for order-1 folios in snapshot_page() snapshot_page() currently reads __page_2 after checking nr_pages > 1, but it should only do so when nr_pages > 2. If an order-1 folio is allocated at the end of a vmemmap section, __page_2 will not exist and reading it will cause a fault. During DLPAR memory remove on a 22 TB ppc64le LPAR, snapshot_page() oopsed on the page isolation path while reading an order-1 folio's __page_2 from an adjacent absent section (unmapped vmemmap). Fix this to avoid reading memmap that doesn't exist (e.g., a vmemmap hole). Link: https://lore.kernel.org/20260708201954.686111-1-aboorvad@linux.ibm.com Fixes: 31a31da8a618 ("mm: move _pincount in folio to page[2] on 32bit") Signed-off-by: Aboorva Devarajan Reported-by: Sourabh Jain Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Reviewed-by: Matthew Wilcox (Oracle) Reviewed-by: Luiz Capitulino Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: "Ritesh Harjani (IBM)" Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: # v6.15+ Signed-off-by: Andrew Morton --- mm/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/util.c b/mm/util.c index af2c2103f0d9..34cb43b3eaa4 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1353,7 +1353,7 @@ again: if (ps->idx < MAX_FOLIO_NR_PAGES) { memcpy(&ps->folio_snapshot, foliop, 2 * sizeof(struct page)); nr_pages = folio_nr_pages(&ps->folio_snapshot); - if (nr_pages > 1) + if (nr_pages > 2) memcpy(&ps->folio_snapshot.__page_2, &foliop->__page_2, sizeof(struct page)); set_ps_flags(ps, foliop, page); -- cgit v1.2.3 From 89b1b79c308818a715e75f28744b70d8940a07c9 Mon Sep 17 00:00:00 2001 From: Zi Yan Date: Thu, 9 Jul 2026 15:12:01 -0400 Subject: mm/percpu-km: fix bitmap overflow and accounting in pcpu_create_chunk() In pcpu_create_chunk(), nr_pages is the total contiguous backing allocation, i.e., nr_units * pcpu_unit_pages, but pcpu_chunk_populated() uses it to set chunk->populated, whose size is pcpu_unit_pages, bitmap. Since bit N in chunk->populated means page offset N inside every unit is backed. When nr_units > 1, the function writes beyond chunk->populated. Fix it by using chunk->nr_pages. It also fixes the global pcpu_nr_empty_pop_pages accounting, since pcpu_balance_free() only iterates up to chunk->nr_pages. Commit a63d4ac4ab609 ("percpu: make percpu-km set chunk->populated bitmap properly") introduced the bitmap overflow issue. Later, commit b539b87fed37f ("percpu: implmeent pcpu_nr_empty_pop_pages and chunk->nr_populated") added pcpu_nr_empty_pop_pages and caused the accounting issue. Link: https://lore.kernel.org/20260709-fix-pcpu_create_chunk-in-percpu-km-v1-1-1f64745a84cc@nvidia.com Fixes: a63d4ac4ab609 ("percpu: make percpu-km set chunk->populated bitmap properly") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260703-keep-subpage-private-zero-at-free-v2-0-2970fe777dd6%40nvidia.com?part=1 Assisted-by: Codex:GPT-5 Signed-off-by: Zi Yan Acked-by: Dennis Zhou Cc: Christoph Lameter Cc: Tejun Heo Cc: Zi Yan Cc: Signed-off-by: Andrew Morton --- mm/percpu-km.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/percpu-km.c b/mm/percpu-km.c index 4efa74a495cb..dc096b5a6ce4 100644 --- a/mm/percpu-km.c +++ b/mm/percpu-km.c @@ -75,7 +75,7 @@ static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp) chunk->base_addr = page_address(pages); spin_lock_irqsave(&pcpu_lock, flags); - pcpu_chunk_populated(chunk, 0, nr_pages); + pcpu_chunk_populated(chunk, 0, chunk->nr_pages); spin_unlock_irqrestore(&pcpu_lock, flags); pcpu_stats_chunk_alloc(); -- cgit v1.2.3 From df8ce7ab48d01ac4f247599b35f0506d95ff57e1 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Fri, 10 Jul 2026 12:05:12 +0800 Subject: ocfs2: fix boundary check in ocfs2_check_dir_entry() to use buffer offset Commit 390ac56cf0f6 ("ocfs2: add boundary check to ocfs2_check_dir_entry()") added an out-of-bounds guard using the caller-supplied 'offset' argument: if (offset > size - OCFS2_DIR_REC_LEN(1)) return 0; However, 'offset' and 'size' are not measured against the same base for all callers. In the block-based lookup path, ocfs2_find_entry_el() passes 'offset' as an absolute offset into the whole directory: i = ocfs2_search_dirblock(bh, dir, name, namelen, block << sb->s_blocksize_bits, bh->b_data, sb->s_blocksize, res_dir); while 'size' is a single block size (sb->s_blocksize). For any directory entry located in the second or later block, 'offset' is >= sb->s_blocksize, so the guard rejects every such entry even though it is perfectly valid and lies entirely within its block buffer. This makes mounting fail for filesystems whose system directory spans more than one block, e.g. a volume formatted with a small block size: mkfs.ocfs2 -b 512 -C 4096 -N 2 -T datafiles --fs-features=usrquota,grpquota ocfs2_check_dir_entry:314 ERROR: directory entry (#18: offset=512) too close to end or out-of-bounds ocfs2_init_local_system_inodes:496 ERROR: status=-22, sysfile=12, slot=0 ocfs2_mount_volume:1757 ERROR: status = -22 The dirent's position within the buffer being validated is ((char *)de - buf), which is what the rest of the function already uses (via next_offset) and what must be bounds-checked against 'size'. Compute that buffer-relative offset and use it for the guard. The subtraction is reordered to size - buf_offset < OCFS2_DIR_REC_LEN(1) to avoid an unsigned underflow when size is smaller than the minimal record length. Link: https://lore.kernel.org/20260710040512.3310736-1-joseph.qi@linux.alibaba.com Fixes: 390ac56cf0f6 ("ocfs2: add boundary check to ocfs2_check_dir_entry()") Signed-off-by: Joseph Qi Reviewed-by: Dmitry Antipov Tested-by: Dmitry Antipov Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/dir.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 8e6b03238327..d7fc3cccf2f4 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -302,10 +302,11 @@ static int ocfs2_check_dir_entry(struct inode *dir, unsigned long offset) { const char *error_msg = NULL; + unsigned long buf_offset = (char *)de - buf; unsigned long next_offset; int rlen; - if (offset > size - OCFS2_DIR_REC_LEN(1)) { + if (buf_offset > size || size - buf_offset < OCFS2_DIR_REC_LEN(1)) { /* Dirent is (maybe partially) beyond the buffer * boundaries so touching 'de' members is unsafe. */ @@ -316,7 +317,7 @@ static int ocfs2_check_dir_entry(struct inode *dir, } rlen = le16_to_cpu(de->rec_len); - next_offset = ((char *) de - buf) + rlen; + next_offset = buf_offset + rlen; if (unlikely(rlen < OCFS2_DIR_REC_LEN(1))) error_msg = "rec_len is smaller than minimal"; -- cgit v1.2.3 From e3a0127eee04db8769e53c8102c4e76aa49be8c3 Mon Sep 17 00:00:00 2001 From: Jori Koolstra Date: Fri, 10 Jul 2026 19:17:35 +0200 Subject: selftest: fix headers in fclog.c fclog.c does not compile because it is missing fcntl.h, needed for O_RDONLY etc. There are also some redundant includes that are also in kselftest_harness.h. Link: https://lore.kernel.org/20260710171741.837308-1-jkoolstra@xs4all.nl Signed-off-by: Jori Koolstra Cc: Aleksa Sarai Cc: Shuah Khan Cc: Wei Yang Cc: Christian Brauner Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/filesystems/fclog.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/testing/selftests/filesystems/fclog.c b/tools/testing/selftests/filesystems/fclog.c index 551c4a0f395a..593a5136e991 100644 --- a/tools/testing/selftests/filesystems/fclog.c +++ b/tools/testing/selftests/filesystems/fclog.c @@ -6,10 +6,8 @@ #include #include +#include #include -#include -#include -#include #include #include -- cgit v1.2.3 From de4660898b7aa7e03d3b120a6bfa6b26211e4e77 Mon Sep 17 00:00:00 2001 From: Qi Zheng Date: Fri, 10 Jul 2026 23:43:18 +0800 Subject: mm: mglru: fix stale batch updates after memcg reparenting The mglru page table walker batches per-generation size deltas in walk->nr_pages while walking page tables without holding the lruvec lock. The reset_batch_size() later folds those deltas into walk->lruvec under the lruvec lock. The page table walker can run concurrently with the memcg reparenting path as follows: CPU0 CPU1 ==== ==== walk_mm --> walk_page_range --> update_batch_size --> walk->nr_pages += delta mem_cgroup_css_offline --> memcg_reparent_objcgs --> lock lruvec lru_gen_reparent_memcg --> reparent child folios to parent unlock lruvec lock lruvec reset_batch_size --> child lrugen->nr_pages += delta This will trigger the following warning in lru_gen_exit_memcg(): VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0, sizeof(lruvec->lrugen.nr_pages))); And the user-visible impact of underestimated nr_pages in MGLRU was premature OOMs because MGLRU does not try to reclaim memory when nr_pages reaches zero, but there are still more pages. To fix it, make reset_batch_size() check CSS_DYING under RCU before flushing the pending batch. A non-dying memcg keeps the original lruvec stable against RCU-delayed offlining; a dying memcg redirects the deltas to the first non-dying ancestor. Link: https://lore.kernel.org/20260710154318.75388-1-qi.zheng@linux.dev Fixes: f304652609ea ("mm: vmscan: prepare for reparenting MGLRU folios") Signed-off-by: Qi Zheng Reported-by: Peiyang He Closes: https://lore.kernel.org/all/5A9E929D82717101+12fcf643-efb8-4b9a-a53a-1e28cc894f0b@smail.nju.edu.cn Reviewed-by: Harry Yoo (Oracle) Acked-by: Johannes Weiner Acked-by: Shakeel Butt Cc: Axel Rasmussen Cc: Barry Song Cc: David Hildenbrand Cc: Kairui Song Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Cc: Wei Xu Cc: Yuanchu Xie Cc: Signed-off-by: Andrew Morton --- include/linux/memcontrol.h | 25 +++++++++++++++++++++++++ mm/vmscan.c | 11 ++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index e1f46a0016fc..957260677678 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -1472,6 +1472,31 @@ static inline void lruvec_lock_irq(struct lruvec *lruvec) spin_lock_irq(&lruvec->lru_lock); } +static inline struct lruvec *lruvec_live_lock_irq(struct lruvec *lruvec) +{ +#ifdef CONFIG_MEMCG + struct pglist_data *pgdat = lruvec_pgdat(lruvec); + struct mem_cgroup *memcg = lruvec_memcg(lruvec); + + rcu_read_lock(); + + /* + * The memcg can be NULL when the memory controller is disabled. + * Otherwise, the caller keeps the memcg owning @lruvec alive. + */ + while (unlikely(memcg && css_is_dying(&memcg->css))) { + memcg = parent_mem_cgroup(memcg); + lruvec = mem_cgroup_lruvec(memcg, pgdat); + } + + spin_lock_irq(&lruvec->lru_lock); +#else + lruvec_lock_irq(lruvec); +#endif + + return lruvec; +} + static inline void lruvec_unlock(struct lruvec *lruvec) { spin_unlock(&lruvec->lru_lock); diff --git a/mm/vmscan.c b/mm/vmscan.c index 35c3bb15ae96..1a142c58700d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -3265,7 +3265,7 @@ static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio, static void reset_batch_size(struct lru_gen_mm_walk *walk) { int gen, type, zone; - struct lruvec *lruvec = walk->lruvec; + struct lruvec *lruvec = lruvec_live_lock_irq(walk->lruvec); struct lru_gen_folio *lrugen = &lruvec->lrugen; walk->batched = 0; @@ -3285,6 +3285,8 @@ static void reset_batch_size(struct lru_gen_mm_walk *walk) lru += LRU_ACTIVE; __update_lru_size(lruvec, lru, zone, delta); } + + lruvec_unlock_irq(lruvec); } static int should_skip_vma(unsigned long start, unsigned long end, struct mm_walk *args) @@ -3779,11 +3781,8 @@ static void walk_mm(struct mm_struct *mm, struct lru_gen_mm_walk *walk) mmap_read_unlock(mm); } - if (walk->batched) { - lruvec_lock_irq(lruvec); + if (walk->batched) reset_batch_size(walk); - lruvec_unlock_irq(lruvec); - } cond_resched(); } while (err == -EAGAIN); @@ -4867,9 +4866,7 @@ retry: walk = current->reclaim_state->mm_walk; if (walk && walk->batched) { walk->lruvec = lruvec; - lruvec_lock_irq(lruvec); reset_batch_size(walk); - lruvec_unlock_irq(lruvec); } mod_lruvec_state(lruvec, PGDEMOTE_KSWAPD + reclaimer_offset(sc), -- cgit v1.2.3 From dd9623f58ec702a07b2d67179d6fcea79c52231a Mon Sep 17 00:00:00 2001 From: Xiangfeng Cai Date: Tue, 14 Jul 2026 01:14:55 +0800 Subject: mm/hugetlb: fix list corruption in allocate_file_region_entries() allocate_file_region_entries() tops up resv->region_cache with freshly allocated file_region descriptors. The allocation uses GFP_KERNEL, so resv->lock is dropped around it: the new entries are gathered on a stack-local list head, allocated_regions, and spliced into resv->region_cache once the lock is re-acquired. The splice used list_splice(), which moves the entries but does not re-initialize the source head, so allocated_regions is left pointing at an entry that now lives on resv->region_cache. The top-up runs in a while loop that re-checks the cache deficit after re-acquiring the lock. For a shared mapping the resv_map is shared by every mapper of the hugetlbfs inode, so a concurrent region_chg()/region_add()/region_del() on the same resv_map can consume cache entries during the unlocked window and force a second iteration. That iteration calls list_add() on the stale head and corrupts the list; with CONFIG_DEBUG_LIST the __list_add_valid() check trips: list_add corruption. next->prev should be prev (ffffc900011ff7f8), but was ffff88814c281460. (next=ffff88814c545640). kernel BUG at lib/list_debug.c:31! allocate_file_region_entries+0x191/0x420 region_chg+0x267/0x300 hugetlb_reserve_pages+0x387/0xc80 hugetlbfs_file_mmap+0x2ce/0x3f0 mmap_region+0x1348/0x1a80 do_mmap+0x85e/0xb90 vm_mmap_pgoff+0x18c/0x330 ksys_mmap_pgoff+0x2a1/0x3e0 do_syscall_64+0xd7/0x420 Without CONFIG_DEBUG_LIST the bad list_add() silently links a kernel-stack address into resv->region_cache, leading to later use-after-free. This was observed as a real host panic on a dense KVM host where a QEMU guest-RAM hugetlbfs file was mapped MAP_SHARED by both QEMU and a separate SPDK/DPDK vhost-user target, generating concurrent region_* traffic on one shared resv_map. Use list_splice_init() so the source head is re-initialized empty after each splice, making the retry loop safe. Link: https://lore.kernel.org/20260713171456.300518-2-caixiangfeng@bytedance.com Fixes: d3ec7b6e09e5 ("mm/hugetlb: use list_splice to merge two list at once") Signed-off-by: Xiangfeng Cai Reviewed-by: Muchun Song Cc: Baoquan He Cc: David Hildenbrand Cc: Oscar Salvador Cc: Shuah Khan Cc: Wei Yang Cc: Signed-off-by: Andrew Morton --- mm/hugetlb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index bca2707d02e3..e93c4d2456aa 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -693,7 +693,7 @@ static int allocate_file_region_entries(struct resv_map *resv, spin_lock(&resv->lock); - list_splice(&allocated_regions, &resv->region_cache); + list_splice_init(&allocated_regions, &resv->region_cache); resv->region_cache_count += to_allocate; } -- cgit v1.2.3 From 40de8160ca7f67d14619ee0351ce5d68fc4a237a Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Wed, 15 Jul 2026 15:42:33 +0100 Subject: fs/proc/task_mmu: fix PAGEMAP_SCAN written state for PMD holes PAGEMAP_SCAN reports an unpopulated PTE in a uffd-wp VMA as written, but a range with no page table at all -- a PMD hole -- is skipped: pagemap_scan_pte_hole() tests p->cur_vma_category, which never carries PAGE_IS_WRITTEN, so the hole is neither reported nor (under PM_SCAN_WP_MATCHING) armed. In a uffd-wp VMA, WP_UNPOPULATED installs uffd-wp markers when protecting a range, allocating page tables as needed, so an unpopulated slot is treated as written -- see the pte_none() handling in pagemap_page_category(). A missing marker therefore means the range was zapped, e.g. via MADV_DONTNEED. This applies to anon and shmem VMAs. An anonymous THP is write-protected in place as a huge PMD, so a full-PMD MADV_DONTNEED clears it to pmd_none -- a hole with no page table -- and pagemap_scan_pte_hole() misses it. For a MAP_PRIVATE|MAP_ANON mapping MADV_DONTNEED has fill-with-zeros semantics, so a write-tracking checkpoint/migration tool (e.g. CRIU) treats the range as unchanged and keeps its previous contents; after restore or live migration the process reads stale data instead of zeroes -- data corruption. Report a hole in a non-hugetlb uffd-wp VMA as written, matching the pte_none handling in pagemap_page_category(); the existing PM_SCAN_WP_MATCHING path then arms it via uffd_wp_range(). hugetlb is excluded: pagemap_hugetlb_category() reports an empty hugetlb entry (huge_pte_none) as not-written, unlike pagemap_page_category(), which reports pte_none as written. pagemap_scan_pte_hole() fires for a hugetlb slot only when it has no page table; keeping that not-written matches how an allocated-but-empty hugetlb entry reads, so the hole and the empty-entry cases agree within the VMA. Link: https://lore.kernel.org/20260715144234.442721-2-kirill@shutemov.name Fixes: 2bad466cc9d9 ("mm/uffd: UFFD_FEATURE_WP_UNPOPULATED") Signed-off-by: Kiryl Shutsemau Reported-by: Sashiko AI review Closes: https://sashiko.dev/#/patchset/20260707151349.92143-1-kirill@shutemov.name Tested-by: Muhammad Usama Anjum Acked-by: David Hildenbrand (Arm) Cc: Peter Xu Cc: Jann Horn Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zenghui Yu Assisted-by: Claude:claude-fable-5 Cc: Signed-off-by: Andrew Morton --- fs/proc/task_mmu.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index d45c729ab6bb..229d1fc3d7f1 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -3049,12 +3049,28 @@ static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end, { struct pagemap_scan_private *p = walk->private; struct vm_area_struct *vma = walk->vma; + unsigned long categories; int ret, err; - if (!vma || !pagemap_scan_is_interesting_page(p->cur_vma_category, p)) + if (!vma) + return 0; + + /* + * In a uffd-wp VMA an unpopulated range is treated as written: + * uffd-wp registration populates page tables and installs markers + * with WP_UNPOPULATED, so a missing marker means the range was + * zapped. See the pte_none() handling in pagemap_page_category(). + * + * hugetlb differs, see pagemap_hugetlb_category(). + */ + categories = p->cur_vma_category; + if (userfaultfd_wp(vma) && !is_vm_hugetlb_page(vma)) + categories |= PAGE_IS_WRITTEN; + + if (!pagemap_scan_is_interesting_page(categories, p)) return 0; - ret = pagemap_scan_output(p->cur_vma_category, p, addr, &end); + ret = pagemap_scan_output(categories, p, addr, &end); if (addr == end) return ret; -- cgit v1.2.3 From 7d8ca62d6a9ef593780161586b4efc811ac094fe Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 6 Jul 2026 14:06:27 +0200 Subject: net: phy: marvell: fix return code Return the correct error code, not the value written to the register. Fixes: a219912e0fec ("net: phy: marvell: implement config_inband() method") Signed-off-by: Michael Walle Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260706120637.1947685-1-mwalle@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/phy/marvell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 7a578b5aa2ed..f71cffa88406 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -753,7 +753,7 @@ static int m88e1111_config_inband(struct phy_device *phydev, unsigned int modes) err = phy_modify(phydev, MII_M1111_PHY_EXT_SR, MII_M1111_HWCFG_SERIAL_AN_BYPASS, extsr); if (err < 0) - return extsr; + return err; return phy_modify_paged(phydev, MII_MARVELL_FIBER_PAGE, MII_BMCR, BMCR_ANENABLE, bmcr); -- cgit v1.2.3 From ef01724fa235a228e3d3e8b117e89403cd8feb25 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marlière" Date: Mon, 6 Jul 2026 13:24:04 -0300 Subject: selftests/net: Fix tun IPv6 test addresses to avoid 6to4 range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IPv6 addresses used for the tun_vnet_udptnl fixture currently fall in the 2002::/16 prefix, which is reserved for the 6to4 transition mechanism (RFC 3056). On systems where the sit module is loaded, the kernel automatically claims 2002::/16 as a 6to4 tunnel prefix. When the test assigns a 2002:: address to a TUN interface, sit registers a competing local route for the same address. This ambiguity breaks the GENEVE decapsulation path: packets injected via the TUN fd are not delivered to the test socket, causing the IPv6-outer gtgso send_gso_packet variants to fail. Replace all four IPv6 test addresses with addresses from the fd00:db8::/32 range, which is part of the ULA space (fc00::/7, RFC 4193) and carries no special kernel semantics. Fixes: 24e59f26eef2 ("selftest: tun: Add helpers for GSO over UDP tunnel") Signed-off-by: Ricardo B. Marlière Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260706-b4-net_tun_addr-v1-1-3d3cb2473560@suse.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/tun.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/net/tun.c index cf106a49b55e..abe488bac50b 100644 --- a/tools/testing/selftests/net/tun.c +++ b/tools/testing/selftests/net/tun.c @@ -42,19 +42,19 @@ static struct in_addr param_ipaddr4_inner_src = { }; static struct in6_addr param_ipaddr6_outer_dst = { - { { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, + { { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, }; static struct in6_addr param_ipaddr6_outer_src = { - { { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } }, + { { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } }, }; static struct in6_addr param_ipaddr6_inner_dst = { - { { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, + { { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, }; static struct in6_addr param_ipaddr6_inner_src = { - { { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } }, + { { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } }, }; #ifndef BIT -- cgit v1.2.3 From f6e3b21608e974c4aaa4cfd73a239dacf1d8a9a3 Mon Sep 17 00:00:00 2001 From: Danielle Ratson Date: Tue, 7 Jul 2026 11:03:04 +0300 Subject: netlink: specs: rt-link: convert bridge port flag attributes to u8 A number of IFLA_BRPORT_* attributes are documented in the rt-link spec as having the "flag" type, i.e. a payload-less NLA_FLAG attribute whose meaning is presence-only. This does not match the kernel, which emits these attributes with nla_put_u8() and validates them as NLA_U8 in br_port_policy[]. The values are not mere presence flags but carry a u8 payload (0/1). Convert these bridge port attributes from "flag" to "u8" so the spec reflects the actual wire format. Fixes: 077b6022d24b ("doc/netlink/specs: Add sub-message type to rt_link family") Reviewed-by: Petr Machata Acked-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: Danielle Ratson Link: https://patch.msgid.link/a57cdfcfc4a6dcb92106c25b4dde5059fde2bd44.1783236731.git.danieller@nvidia.com Signed-off-by: Jakub Kicinski --- Documentation/netlink/specs/rt-link.yaml | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index 892979da098e..68c26a70bb64 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -1585,31 +1585,31 @@ attribute-sets: type: u32 - name: mode - type: flag + type: u8 - name: guard - type: flag + type: u8 - name: protect - type: flag + type: u8 - name: fast-leave - type: flag + type: u8 - name: learning - type: flag + type: u8 - name: unicast-flood - type: flag + type: u8 - name: proxyarp - type: flag + type: u8 - name: learning-sync - type: flag + type: u8 - name: proxyarp-wifi - type: flag + type: u8 - name: root-id type: binary @@ -1656,34 +1656,34 @@ attribute-sets: type: pad - name: mcast-flood - type: flag + type: u8 - name: mcast-to-ucast - type: flag + type: u8 - name: vlan-tunnel - type: flag + type: u8 - name: bcast-flood - type: flag + type: u8 - name: group-fwd-mask type: u16 - name: neigh-suppress - type: flag + type: u8 - name: isolated - type: flag + type: u8 - name: backup-port type: u32 - name: mrp-ring-open - type: flag + type: u8 - name: mrp-in-open - type: flag + type: u8 - name: mcast-eht-hosts-limit type: u32 @@ -1692,10 +1692,10 @@ attribute-sets: type: u32 - name: locked - type: flag + type: u8 - name: mab - type: flag + type: u8 - name: mcast-n-groups type: u32 @@ -1704,7 +1704,7 @@ attribute-sets: type: u32 - name: neigh-vlan-suppress - type: flag + type: u8 - name: backup-nhid type: u32 -- cgit v1.2.3 From 50aff80475abd3533eef4320477037e6fcc6b56e Mon Sep 17 00:00:00 2001 From: David Lee Date: Tue, 7 Jul 2026 10:44:37 +0000 Subject: net/packet: avoid fanout hook re-registration after unregister packet_set_ring() temporarily detaches a socket from packet delivery while reconfiguring its ring. It records the previous running state, clears po->num, unregisters the protocol hook when needed, drops po->bind_lock, and later restores po->num and re-registers the hook from the saved was_running value. That unlocked window can race with NETDEV_UNREGISTER. The notifier can observe the socket as not running, skip __unregister_prot_hook(), and invalidate the per-socket binding by setting po->ifindex to -1 and clearing po->prot_hook.dev. A one-member fanout group can still retain its shared fanout hook device pointer. When packet_set_ring() resumes, re-registering solely from the stale was_running state can re-add the fanout hook after the device has been unregistered. Treat po->ifindex == -1 as an invalidated binding after reacquiring po->bind_lock. This is distinct from ifindex 0, the normal unbound/wildcard state: ifindex -1 marks an existing device binding that was invalidated when the device was unregistered. Restore po->num as before, but do not re-register the hook if device unregister already detached the socket. Fixes: dc99f600698d ("packet: Add fanout support.") Link: https://lore.kernel.org/netdev/20260701113947.23180-1-david.lee@trailofbits.com/ Signed-off-by: David Lee Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260707104440.833129-1-david.lee@trailofbits.com Signed-off-by: Jakub Kicinski --- net/packet/af_packet.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8e6f3a734ba0..e75d2932475a 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -4561,7 +4561,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, spin_lock(&po->bind_lock); WRITE_ONCE(po->num, num); - if (was_running) + /* + * NETDEV_UNREGISTER may have invalidated the binding while bind_lock + * was dropped above. Do not re-add a fanout hook to a dead device. + */ + if (was_running && READ_ONCE(po->ifindex) != -1) register_prot_hook(sk); spin_unlock(&po->bind_lock); -- cgit v1.2.3 From 3f4920d165b29052255527d8ae7619e7ec132ece Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 6 Jul 2026 20:56:07 +0200 Subject: bpf: Reject redirect helpers without a bpf_net_context The bpf_redirect*() helpers and skb_do_redirect() obtain the per-task bpf_redirect_info via bpf_net_ctx_get_ri(), which dereferences the current->bpf_net_context unconditionally. That context is established on the paths that run tc BPF such as sch_handle_{ingress,egress}(), *except* for the case where {cls,act}_bpf was attached to a proper qdisc. A program running from there reaches the NULL deref in two ways: * It calls bpf_redirect() directly, which dereferences the context at the top of the helper: tc qdisc add dev eth0 root handle 1: red limit 1MB min 10KB max 20KB \ avpkt 1000 burst 100 qevent early_drop block 10 tc filter add block 10 pref 1 bpf obj redirect.o * It simply returns TC_ACT_REDIRECT without helper call: tcf_qevent_handle() then dispatches to skb_do_redirect(), which dereferences the context Rather than extending bpf_net_context management into the qdisc path, make the redirect helpers refuse to operate when no context exists, and have tcf_qevent_handle() drop a TC_ACT_REDIRECT verdict instead of calling skb_do_redirect(). Previous behaviour was a crash, so nothing regresses by not supporting it. Fixes: 401cb7dae813 ("net: Reference bpf_redirect_info via task_struct on PREEMPT_RT.") Fixes: 3625750f05ec ("net: sched: Introduce helpers for qevent blocks") Signed-off-by: Daniel Borkmann Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260706185609.330006-2-daniel@iogearbox.net Signed-off-by: Jakub Kicinski --- net/core/filter.c | 17 +++++++++++------ net/sched/cls_api.c | 6 ++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index b446aa8be5c3..11bb0d236822 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2552,11 +2552,13 @@ out_drop: BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags) { - struct bpf_redirect_info *ri = bpf_net_ctx_get_ri(); + struct bpf_redirect_info *ri; - if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL))) + if (unlikely(!bpf_net_ctx_get() || + (flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL)))) return TC_ACT_SHOT; + ri = bpf_net_ctx_get_ri(); ri->flags = flags; ri->tgt_index = ifindex; @@ -2573,11 +2575,12 @@ static const struct bpf_func_proto bpf_redirect_proto = { BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags) { - struct bpf_redirect_info *ri = bpf_net_ctx_get_ri(); + struct bpf_redirect_info *ri; - if (unlikely(flags)) + if (unlikely(!bpf_net_ctx_get() || flags)) return TC_ACT_SHOT; + ri = bpf_net_ctx_get_ri(); ri->flags = BPF_F_PEER; ri->tgt_index = ifindex; @@ -2595,11 +2598,13 @@ static const struct bpf_func_proto bpf_redirect_peer_proto = { BPF_CALL_4(bpf_redirect_neigh, u32, ifindex, struct bpf_redir_neigh *, params, int, plen, u64, flags) { - struct bpf_redirect_info *ri = bpf_net_ctx_get_ri(); + struct bpf_redirect_info *ri; - if (unlikely((plen && plen < sizeof(*params)) || flags)) + if (unlikely((plen && plen < sizeof(*params)) || + !bpf_net_ctx_get() || flags)) return TC_ACT_SHOT; + ri = bpf_net_ctx_get_ri(); ri->flags = BPF_F_NEIGH | (plen ? BPF_F_NEXTHOP : 0); ri->tgt_index = ifindex; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index ffeea6db8337..523cf2a8bd1d 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -4046,6 +4046,8 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru fl = rcu_dereference_bh(qe->filter_chain); switch (tcf_classify(skb, NULL, fl, &cl_res, false)) { + case TC_ACT_REDIRECT: + fallthrough; case TC_ACT_SHOT: qdisc_qstats_drop(sch); __qdisc_drop(skb, to_free); @@ -4057,10 +4059,6 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru __qdisc_drop(skb, to_free); *ret = __NET_XMIT_STOLEN; return NULL; - case TC_ACT_REDIRECT: - skb_do_redirect(skb); - *ret = __NET_XMIT_STOLEN; - return NULL; case TC_ACT_CONSUMED: *ret = __NET_XMIT_STOLEN; return NULL; -- cgit v1.2.3 From ec48b3be2c8595dd290be883dbd4fb8b2f9f5d5e Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Mon, 6 Jul 2026 20:56:08 +0200 Subject: net/sched: Handle TC_ACT_REDIRECT from qdisc filter chains When a TC filter attached to a qdisc filter chain returns TC_ACT_REDIRECT (ex: via an eBPF program calling bpf_redirect() or an act_bpf action), the redirect was silently lost i.e no qdisc classify function handled TC_ACT_REDIRECT, so the packet fell through the switch and was enqueued normally instead of being redirected. This has been broken since bpf_redirect() was introduced for TC in commit 27b29f63058d ("bpf: add bpf_redirect() helper"). We got lucky for a long time because bpf_net_context was a per-CPU variable that was always available. commit 401cb7dae813 ("net: Reference bpf_redirect_info via task_struct on PREEMPT_RT.") turned bpf_net_context into a task_struct member that is only set up by explicit callers. Without a caller setting it up, bpf_redirect() itself crashes with a NULL pointer dereference in bpf_net_ctx_get_ri(). However, even with bpf_net_context available, TC_ACT_REDIRECT from qdisc filter chains cannot be honored without adding skb_do_redirect() calls to every qdisc classify function, which would require changes across net/sched/. Isolate it to ebpf core where it belongs. Instead, add a tcf_classify_qdisc() inline helper in pkt_cls.h, as a wrapper around tcf_classify() for use by qdisc classify functions and tcf_qevent_handle(). When the classify verdict is TC_ACT_REDIRECT, the wrapper converts it to TC_ACT_SHOT, dropping the packet rather than letting it continue silently. Dropping is preferred over letting the packet through because the user immediately sees packet loss. Silently passing the packet through would hide the problem and leave the user wondering why their redirect is not working. The clsact fast path, tc_run() continues to call tcf_classify() directly and is unaffected: TC_ACT_REDIRECT is returned as-is and handled by sch_handle_egress/ingress() calling skb_do_redirect() as before. Fixes: 27b29f63058d ("bpf: add bpf_redirect() helper") Fixes: 401cb7dae813 ("net: Reference bpf_redirect_info via task_struct on PREEMPT_RT.") Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Signed-off-by: Daniel Borkmann Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260706185609.330006-3-daniel@iogearbox.net Signed-off-by: Jakub Kicinski --- include/net/pkt_cls.h | 14 +++++++++++++- net/sched/cls_api.c | 4 +--- net/sched/sch_cake.c | 2 +- net/sched/sch_drr.c | 2 +- net/sched/sch_dualpi2.c | 2 +- net/sched/sch_ets.c | 2 +- net/sched/sch_fq_codel.c | 2 +- net/sched/sch_fq_pie.c | 2 +- net/sched/sch_hfsc.c | 2 +- net/sched/sch_htb.c | 2 +- net/sched/sch_multiq.c | 2 +- net/sched/sch_prio.c | 2 +- net/sched/sch_qfq.c | 2 +- net/sched/sch_sfb.c | 2 +- net/sched/sch_sfq.c | 2 +- 15 files changed, 27 insertions(+), 17 deletions(-) diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 3bd08d7f39c1..5f5cb36439fe 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -156,8 +156,20 @@ static inline int tcf_classify(struct sk_buff *skb, { return TC_ACT_UNSPEC; } - #endif +static inline int tcf_classify_qdisc(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res, bool compat_mode) +{ + int ret = tcf_classify(skb, NULL, tp, res, compat_mode); + + /* TC_ACT_REDIRECT from qdisc filter chains is not supported. + * Use BPF via tcx or mirred redirect instead. + */ + if (unlikely(ret == TC_ACT_REDIRECT)) + ret = TC_ACT_SHOT; + return ret; +} static inline unsigned long __cls_set_class(unsigned long *clp, unsigned long cl) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 523cf2a8bd1d..fee4524adc98 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -4045,9 +4045,7 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru fl = rcu_dereference_bh(qe->filter_chain); - switch (tcf_classify(skb, NULL, fl, &cl_res, false)) { - case TC_ACT_REDIRECT: - fallthrough; + switch (tcf_classify_qdisc(skb, fl, &cl_res, false)) { case TC_ACT_SHOT: qdisc_qstats_drop(sch); __qdisc_drop(skb, to_free); diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index f78f8e950776..505f63fecf64 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1727,7 +1727,7 @@ static u32 cake_classify(struct Qdisc *sch, struct cake_tin_data **t, goto hash; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - result = tcf_classify(skb, NULL, filter, &res, false); + result = tcf_classify_qdisc(skb, filter, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 020657f959b5..91b1ef824afa 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -312,7 +312,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch, *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; fl = rcu_dereference_bh(q->filter_list); - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 27088760eff4..4f678d4ff10e 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -364,7 +364,7 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, if (!fl) return NET_XMIT_SUCCESS; - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index cb8cf437ce87..25fcf4079fec 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -391,7 +391,7 @@ static struct ets_class *ets_classify(struct sk_buff *skb, struct Qdisc *sch, *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; if (TC_H_MAJ(skb->priority) != sch->handle) { fl = rcu_dereference_bh(q->filter_list); - err = tcf_classify(skb, NULL, fl, &res, false); + err = tcf_classify_qdisc(skb, fl, &res, false); #ifdef CONFIG_NET_CLS_ACT switch (err) { case TC_ACT_STOLEN: diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index cafd1f943d99..6cce86ba383c 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -91,7 +91,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch, return fq_codel_hash(q, skb) + 1; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - result = tcf_classify(skb, NULL, filter, &res, false); + result = tcf_classify_qdisc(skb, filter, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index 72f48fa4010b..069e1facd413 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -96,7 +96,7 @@ static unsigned int fq_pie_classify(struct sk_buff *skb, struct Qdisc *sch, return fq_pie_hash(q, skb) + 1; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - result = tcf_classify(skb, NULL, filter, &res, false); + result = tcf_classify_qdisc(skb, filter, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 7e537295b8b6..e87f5021a199 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1143,7 +1143,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; head = &q->root; tcf = rcu_dereference_bh(q->root.filter_list); - while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) { + while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { case TC_ACT_QUEUED: diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 908b9ba9ba2e..fdac0dc8f35a 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -243,7 +243,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch, } *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) { + while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { case TC_ACT_QUEUED: diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index a467dd122369..66df30939aa5 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -36,7 +36,7 @@ multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) int err; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - err = tcf_classify(skb, NULL, fl, &res, false); + err = tcf_classify_qdisc(skb, fl, &res, false); #ifdef CONFIG_NET_CLS_ACT switch (err) { case TC_ACT_STOLEN: diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index e4dd56a89072..79437c587e7e 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -39,7 +39,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; if (TC_H_MAJ(skb->priority) != sch->handle) { fl = rcu_dereference_bh(q->filter_list); - err = tcf_classify(skb, NULL, fl, &res, false); + err = tcf_classify_qdisc(skb, fl, &res, false); #ifdef CONFIG_NET_CLS_ACT switch (err) { case TC_ACT_STOLEN: diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index cb56787e1d25..6f3b7273cb16 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -709,7 +709,7 @@ static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch, *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; fl = rcu_dereference_bh(q->filter_list); - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index b1d465094276..ed39869199c0 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -260,7 +260,7 @@ static bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl, struct tcf_result res; int result; - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 758b88f21865..77675f9a4c46 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -171,7 +171,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch, return sfq_hash(q, skb) + 1; *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; - result = tcf_classify(skb, NULL, fl, &res, false); + result = tcf_classify_qdisc(skb, fl, &res, false); if (result >= 0) { #ifdef CONFIG_NET_CLS_ACT switch (result) { -- cgit v1.2.3 From f789afed9448e17f12e0dffe84c2d57acd206d42 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 6 Jul 2026 20:56:09 +0200 Subject: selftests/bpf: Add test for redirect from qdisc qevent block Add a regression test for the NULL current->bpf_net_context deref hit when a BPF classifier attached to a qdisc qevent block asks for a redirect. The classifier runs from tcf_qevent_handle() on the qdisc enqueue path, outside any bpf_net_context. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t qevent [...] + /etc/rcS.d/S50-startup ./test_progs -t qevent #496/1 tc_qevent/redirect_verdict:OK #496/2 tc_qevent/redirect_helper:OK #496 tc_qevent:OK Summary: 1/2 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Daniel Borkmann Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260706185609.330006-4-daniel@iogearbox.net Signed-off-by: Jakub Kicinski --- tools/testing/selftests/bpf/config | 1 + tools/testing/selftests/bpf/prog_tests/tc_qevent.c | 113 +++++++++++++++++++++ tools/testing/selftests/bpf/progs/test_tc_qevent.c | 23 +++++ 3 files changed, 137 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/tc_qevent.c create mode 100644 tools/testing/selftests/bpf/progs/test_tc_qevent.c diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index adb25146e88c..ea7044f30adc 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -82,6 +82,7 @@ CONFIG_NET_SCH_BPF=y CONFIG_NET_SCH_FQ=y CONFIG_NET_SCH_INGRESS=y CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_RED=y CONFIG_NET_SCHED=y CONFIG_NETDEVSIM=y CONFIG_NETFILTER=y diff --git a/tools/testing/selftests/bpf/prog_tests/tc_qevent.c b/tools/testing/selftests/bpf/prog_tests/tc_qevent.c new file mode 100644 index 000000000000..67e1d17567ab --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/tc_qevent.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +#include "test_tc_qevent.skel.h" + +#define NS_TX "tc_qevent_tx" +#define NS_RX "tc_qevent_rx" +#define IP_TX "10.255.0.1" +#define IP_RX "10.255.0.2" +#define PIN_PATH "/sys/fs/bpf/tc_qevent_redirect" + +static void blast_udp(void) +{ + struct sockaddr_in dst = {}; + char buf[1400] = {}; + int fd, i; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (!ASSERT_GE(fd, 0, "udp socket")) + return; + + dst.sin_family = AF_INET; + dst.sin_port = htons(12345); + inet_pton(AF_INET, IP_RX, &dst.sin_addr); + + /* + * Push far more than the RED queue can hold. Once qavg crosses qth_min + * every further packet hits the congestion_drop / early_drop qevent. + */ + for (i = 0; i < 50000; i++) + sendto(fd, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *)&dst, sizeof(dst)); + + close(fd); +} + +static void run_qevent_redirect(struct bpf_program *prog, __u64 *counter) +{ + struct nstoken *tok = NULL; + int err; + + SYS_NOFAIL("ip netns del %s", NS_TX); + SYS_NOFAIL("ip netns del %s", NS_RX); + unlink(PIN_PATH); + + err = bpf_program__pin(prog, PIN_PATH); + if (!ASSERT_OK(err, "pin prog")) + return; + + SYS(unpin, "ip netns add %s", NS_TX); + SYS(del_tx, "ip netns add %s", NS_RX); + SYS(del_rx, "ip -n %s link add veth0 type veth peer name veth1 netns %s", NS_TX, NS_RX); + SYS(del_rx, "ip -n %s addr add %s/24 dev veth0", NS_TX, IP_TX); + SYS(del_rx, "ip -n %s link set veth0 up", NS_TX); + SYS(del_rx, "ip -n %s addr add %s/24 dev veth1", NS_RX, IP_RX); + SYS(del_rx, "ip -n %s link set veth1 up", NS_RX); + + tok = open_netns(NS_TX); + if (!ASSERT_OK_PTR(tok, "open_netns")) + goto del_rx; + + SYS(close_ns, "tc qdisc add dev veth0 root handle 1: htb default 1"); + SYS(close_ns, "tc class add dev veth0 parent 1: classid 1:1 htb rate 1mbit ceil 1mbit"); + + if (system("tc qdisc add dev veth0 parent 1:1 handle 11: red " + "limit 500000 avpkt 1000 probability 1 min 5000 max 6000 " + "burst 6 qevent early_drop block 10 2>/dev/null")) { + test__skip(); + goto close_ns; + } + + if (system("tc filter add block 10 bpf da object-pinned " + PIN_PATH " 2>/dev/null")) { + test__skip(); + goto close_ns; + } + + blast_udp(); + ASSERT_GT(*counter, 0, "qevent classifier ran"); +close_ns: + close_netns(tok); +del_rx: + SYS_NOFAIL("ip netns del %s", NS_RX); +del_tx: + SYS_NOFAIL("ip netns del %s", NS_TX); +unpin: + bpf_program__unpin(prog, PIN_PATH); +} + +void test_tc_qevent(void) +{ + struct test_tc_qevent *skel; + + skel = test_tc_qevent__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + if (test__start_subtest("redirect_verdict")) + run_qevent_redirect(skel->progs.qevent_redirect_verdict, + &skel->bss->verdict_calls); + if (test__start_subtest("redirect_helper")) + run_qevent_redirect(skel->progs.qevent_redirect_helper, + &skel->bss->helper_calls); + + test_tc_qevent__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/test_tc_qevent.c b/tools/testing/selftests/bpf/progs/test_tc_qevent.c new file mode 100644 index 000000000000..1529c111f4aa --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_tc_qevent.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "vmlinux.h" +#include + +int redirect_ifindex = 1; +__u64 verdict_calls = 0; +__u64 helper_calls = 0; + +SEC("tc") +int qevent_redirect_verdict(struct __sk_buff *skb) +{ + __sync_fetch_and_add(&verdict_calls, 1); + return TCX_REDIRECT; +} + +SEC("tc") +int qevent_redirect_helper(struct __sk_buff *skb) +{ + __sync_fetch_and_add(&helper_calls, 1); + return bpf_redirect(redirect_ifindex, 0); +} + +char _license[] SEC("license") = "GPL"; -- cgit v1.2.3 From 1c975de3343cdef506f2eecc833cc1f14b0401c4 Mon Sep 17 00:00:00 2001 From: Zhaolong Zhang Date: Tue, 7 Jul 2026 09:06:22 +0800 Subject: bonding: fix devconf_all NULL dereference when IPv6 is disabled When booting with the 'ipv6.disable=1' parameter, the devconf_all is never initialized because inet6_init() exits before addrconf_init() is called which initializes it. bond_send_validate(), however, will still call bond_ns_send_all() even ipv6 is indeed disabled. It will lead to NULL derefence of net->ipv6.devconf_all in ip6_pol_route(). BUG: kernel NULL pointer dereference, address: 000000000000000c [...] Workqueue: bond0 bond_arp_monitor [bonding] RIP: 0010:ip6_pol_route+0x69/0x480 [...] Call Trace: ? srso_return_thunk+0x5/0x5f ? __pfx_ip6_pol_route_output+0x10/0x10 fib6_rule_lookup+0xfe/0x260 ? wakeup_preempt+0x8a/0x90 ? srso_return_thunk+0x5/0x5f ? srso_return_thunk+0x5/0x5f ? sched_balance_rq+0x369/0x810 ip6_route_output_flags+0xd7/0x170 bond_ns_send_all+0xde/0x280 [bonding] bond_ab_arp_probe+0x296/0x320 [bonding] ? srso_return_thunk+0x5/0x5f bond_activebackup_arp_mon+0xb4/0x2c0 [bonding] process_one_work+0x196/0x370 worker_thread+0x1af/0x320 ? srso_return_thunk+0x5/0x5f ? __pfx_worker_thread+0x10/0x10 kthread+0xe3/0x120 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x199/0x260 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Fix this by adding ipv6_mod_enabled() condition check in the caller. Fixes: 4e24be018eb9 ("bonding: add new parameter ns_targets") Signed-off-by: Qianheng Peng Signed-off-by: Zhaolong Zhang Reviewed-by: Hangbin Liu Link: https://patch.msgid.link/20260707010622.487333-1-zhangzl2013@126.com Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e044fc733b8c..522eab060f9e 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3455,7 +3455,8 @@ static void bond_send_validate(struct bonding *bond, struct slave *slave) { bond_arp_send_all(bond, slave); #if IS_ENABLED(CONFIG_IPV6) - bond_ns_send_all(bond, slave); + if (likely(ipv6_mod_enabled())) + bond_ns_send_all(bond, slave); #endif } -- cgit v1.2.3 From 15f197856d68882af9416fc97516bb55079b7677 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 20 Jul 2026 19:12:10 +0900 Subject: tracing/probes: Avoid temporary buffer truncation in trace_probe_match_command_args() In trace_probe_match_command_args(), a stack buffer buf[MAX_ARGSTR_LEN + 1] (256 bytes) is used to format "=". However, since name can be up to 32 bytes (MAX_ARG_NAME_LEN) and comm up to 255 bytes (MAX_ARGSTR_LEN), the formatted string can exceed 256 bytes and get truncated by snprintf(), causing spurious argument matching failures. Instead of formatting into a temporary buffer on stack, compare the argument name, the '=' delimiter, and the comm expression directly. Link: https://lore.kernel.org/all/178454233010.290363.10428767141343428804.stgit@devnote2/ Fixes: eb5bf81330a7 ("tracing/kprobe: Add per-probe delete from event") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_probe.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index d17cfee77d9c..95e3d072321f 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -2338,16 +2338,17 @@ int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b) bool trace_probe_match_command_args(struct trace_probe *tp, int argc, const char **argv) { - char buf[MAX_ARGSTR_LEN + 1]; int i; if (tp->nr_args < argc) return false; for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "%s=%s", - tp->args[i].name, tp->args[i].comm); - if (strcmp(buf, argv[i])) + int len = strlen(tp->args[i].name); + + if (strncmp(argv[i], tp->args[i].name, len) || + argv[i][len] != '=' || + strcmp(argv[i] + len + 1, tp->args[i].comm)) return false; } return true; -- cgit v1.2.3 From a9d6fb284039a5d3858a1d9f9a0d7e46cfb7c2d4 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 20 Jul 2026 19:12:20 +0900 Subject: tracing/probes: Prevent out-of-bounds write in __trace_probe_log_err() If trace_probe_log.argc is 0 in __trace_probe_log_err(), the loop constructing the command string will not execute and p will remain equal to command. Writing to *(p - 1) will cause an out-of-bounds access before command. This should not happen, but better to be treated. Reject if trace_probe_log.argc is 0. Link: https://lore.kernel.org/all/178454233992.290363.18323091580600697731.stgit@devnote2/ Fixes: ab105a4fb894 ("tracing: Use tracing error_log with probe events") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 95e3d072321f..49daa3cc2a45 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -188,7 +188,7 @@ void __trace_probe_log_err(int offset, int err_type) lockdep_assert_held(&dyn_event_ops_mutex); - if (!trace_probe_log.argv) + if (!trace_probe_log.argv || !trace_probe_log.argc) return; /* Recalculate the length and allocate buffer */ -- cgit v1.2.3 From 8ce20bfba48902e1382187cd1a852f7cf3a1e739 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 20 Jul 2026 19:12:29 +0900 Subject: tracing/probes: Fix potential underflow in LEN_OR_ZERO macro In __set_print_fmt(), LEN_OR_ZERO is defined as (len ? len - pos : 0). If len is non-zero but smaller than pos, len - pos evaluates to a negative integer. When passed as a size argument to snprintf(), this negative value is cast to a large unsigned size_t, bypassing buffer size limits. Ensure len > pos before subtracting to avoid integer underflow. Link: https://lore.kernel.org/all/178454234934.290363.15247317871499514139.stgit@devnote2/ Fixes: 5bf652aaf46c ("tracing/probes: Integrate duplicate set_print_fmt()") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 49daa3cc2a45..506e6037e163 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -2013,7 +2013,7 @@ int traceprobe_update_arg(struct probe_arg *arg) } /* When len=0, we just calculate the needed length */ -#define LEN_OR_ZERO (len ? len - pos : 0) +#define LEN_OR_ZERO (len > pos ? len - pos : 0) static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, enum probe_print_type ptype) { -- cgit v1.2.3 From f418d68d71fd4a0a9cef92377bc8c4c3334b5b53 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Mon, 20 Jul 2026 19:12:38 +0900 Subject: tracing/eprobe: Fix exact system name matching in eprobe_dyn_event_match() eprobe_dyn_event_match() checks if the target event system in argv[0] matches ep->event_system using strncmp(ep->event_system, argv[0], len). However, if ep->event_system is longer than len (e.g. "eprobes" vs "ep/event"), strncmp() still returns 0 because the first len characters match. Check that ep->event_system[len] is '\0' to ensure exact system name matching. Link: https://lore.kernel.org/all/178454235856.290363.14872590900774231133.stgit@devnote2/ Fixes: 7d5fda1c841f ("tracing: Fix event probe removal from dynamic events") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_eprobe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index 50518b071414..bcd97cb24ac9 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -172,7 +172,8 @@ static bool eprobe_dyn_event_match(const char *system, const char *event, if (!slash) return false; - if (strncmp(ep->event_system, argv[0], slash - argv[0])) + if (strncmp(ep->event_system, argv[0], slash - argv[0]) || + ep->event_system[slash - argv[0]] != '\0') return false; if (strcmp(ep->event_name, slash + 1)) return false; -- cgit v1.2.3 From b27e195d4db8dea263050bdbeb11881b2999c9c6 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 20 Jul 2026 20:44:21 +0100 Subject: cdrom: fix stack out-of-bounds read in CDROMVOLCTRL mmc_ioctl_cdrom_volume() first reads the audio control mode page into a 32-byte stack buffer with cgc->buflen set to 24. If the device reports a block descriptor, the function increases cgc->buflen to include that descriptor and reads the page again. For CDROMVOLCTRL, the function then builds a MODE SELECT parameter list by moving cgc->buffer forward by offset - 8 bytes. This drops the block descriptor from the outgoing payload and leaves a new 8-byte mode parameter header in front of the audio control page. However, cgc->buflen is left unchanged. With a standard 8-byte block descriptor, cgc->buffer points at buffer + 8 but cgc->buflen remains 32. cdrom_mode_select() therefore asks the low level packet path to write 32 bytes from that adjusted pointer, reading 8 bytes past the end of the 32-byte stack buffer. This is not hit by CDROMVOLREAD, and CDROMVOLCTRL only triggers it on drives that return a non-zero block descriptor length, which helps explain why it has gone unnoticed. The overread is also sent to the device as extra MODE SELECT payload, so it may not produce an obvious local failure. Reduce cgc->buflen by the same amount as the buffer pointer adjustment so the MODE SELECT transfer covers only the intended parameter list. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Phillip Potter Link: https://patch.msgid.link/20260720194421.1497-2-phil@philpotter.co.uk Signed-off-by: Jens Axboe --- drivers/cdrom/cdrom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 62934cf4b10d..4f1fd389260f 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -3187,6 +3187,7 @@ static noinline int mmc_ioctl_cdrom_volume(struct cdrom_device_info *cdi, /* set volume */ cgc->buffer = buffer + offset - 8; + cgc->buflen -= offset - 8; memset(cgc->buffer, 0, 8); return cdrom_mode_select(cdi, cgc); } -- cgit v1.2.3 From 6881f45d0eb541f2cee8c37c84b3860a23823bb3 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Sat, 4 Jul 2026 17:58:56 +0930 Subject: btrfs: fix leaking BTRFS_FS_STATE_REMOUNTING flag [BUG] The following script can lead to unexpected qgroup rescan failure: # mkfs.btrfs -f -O quota $dev # mount $dev $mnt # mount -o remount,rescue=ibadroots $mnt ^^^^^ This above command is expected to fail # btrfs quota rescan -w $mnt ^^^^^ The above qgroup rescan is not expected to fail # btrfs qgroup show $mnt WARNING: qgroup data inconsistent, rescan recommended Qgroupid Referenced Exclusive Path -------- ---------- --------- ---- 0/5 16.00KiB 16.00KiB The above short script will be converted to a proper fstests case. [CAUSE] Inside btrfs_reconfigure(), if either btrfs_check_options() or btrfs_check_features() failed, we will always have BTRFS_FS_STATE_REMOUNTING set for the fs until the next successful remount. That BTRFS_FS_STATE_REMOUNTING flag will interrupt several operations, including: - Qgroup rescan - Auto defrag - Space reclaim [FIX] Change the error handling of btrfs_check_options() and btrfs_check_features() to goto restore label. Fixes: eddb1a433f26 ("btrfs: add reconfigure callback for fs_context") Reviewed-by: Johannes Thumshirn Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/super.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index c946bccf0748..63bf1f1e16d4 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1519,12 +1519,14 @@ static int btrfs_reconfigure(struct fs_context *fc) sync_filesystem(sb); set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state); - if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) - return -EINVAL; + if (!btrfs_check_options(fs_info, &ctx->mount_opt, fc->sb_flags)) { + ret = -EINVAL; + goto restore; + } ret = btrfs_check_features(fs_info, !(fc->sb_flags & SB_RDONLY)); if (ret < 0) - return ret; + goto restore; btrfs_ctx_to_info(fs_info, ctx); btrfs_remount_begin(fs_info, old_ctx.mount_opt, fc->sb_flags); -- cgit v1.2.3 From 1ebe51c29fa9755d5b2fea28727c051117907cf8 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 3 Jul 2026 07:54:40 +0200 Subject: btrfs: zoned: fix deadlock between metadata writeback and transaction commit When writing out metadata extent buffers in a zoned filesystem, btree_writepages() holds fs_info->zoned_meta_io_lock across the whole writeback loop, including the call to btrfs_check_meta_write_pointer() -> check_bg_is_active(). For the tree-log block group, check_bg_is_active() may fail to activate the zone and fall back to btrfs_zone_finish_one_bg() to free an active zone. That path waits for the running transaction to commit while still holding zoned_meta_io_lock, but the committer needs that same lock to write out the tree extents, so the two tasks deadlock: Task A (kworker, metadata writeback) Task B (fsstress, transaction commit) ------------------------------------ ------------------------------------- wb_workfn() btrfs_commit_transaction(T) btree_writepages() btrfs_write_and_wait_transaction() btrfs_zoned_meta_io_lock() btrfs_write_marked_extents() btrfs_check_meta_write_pointer() btree_writepages() check_bg_is_active() [treelog_bg] btrfs_zoned_meta_io_lock() btrfs_zone_finish_one_bg() do_zone_finish() btrfs_inc_block_group_ro() btrfs_wait_for_commit() The sibling branch in check_bg_is_active() already drops zoned_meta_io_lock around do_zone_finish() for this exact reason. Do the same in the tree-log branch: release the lock around btrfs_zone_finish_one_bg() and re-acquire it afterwards. The lock only protects fs_info->active_{meta,system}_bg, which this branch does not touch, and ctx->zoned_bg keeps a reference to the block group across the unlock, so nothing is lost while the lock is dropped. This hang occasionally reproduces with fstests generic/475 on a zoned btrfs filesystem. Fixes: 13bb483d32ab ("btrfs: zoned: activate metadata block group on write time") Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba --- fs/btrfs/zoned.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 97f06dd01693..44a13ed6b8b2 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -2190,7 +2190,11 @@ static bool check_bg_is_active(struct btrfs_eb_write_context *ctx, if (fs_info->treelog_bg == block_group->start) { if (!btrfs_zone_activate(block_group)) { - int ret_fin = btrfs_zone_finish_one_bg(fs_info); + int ret_fin; + + btrfs_zoned_meta_io_unlock(fs_info); + ret_fin = btrfs_zone_finish_one_bg(fs_info); + btrfs_zoned_meta_io_lock(fs_info); if (ret_fin != 1 || !btrfs_zone_activate(block_group)) return false; -- cgit v1.2.3 From 5fabb1cf25d723274009d7b759545fd59f230c9d Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 3 Jul 2026 07:54:45 +0200 Subject: btrfs: zoned: reset meta_write_pointer on zone reset btrfs_reset_unused_block_groups() resets a block group's zone and sets alloc_offset back to 0 so the space can be reused, but it leaves meta_write_pointer pointing at the previous end of the zone. Once the block group is reactivated and reused for metadata, newly allocated tree blocks live before that stale write pointer. btrfs_check_meta_write_pointer() then sees them behind the write pointer, so they can never be written out in sequential order: the dirty extent buffers are stranded and pin their btree_inode folios until unmount. Reset meta_write_pointer back to the start of the block group for metadata and system block groups. Fixes: 453a73c3069a ("btrfs: zoned: reclaim unused zone by zone resetting") Reviewed-by: Naohiro Aota Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba --- fs/btrfs/zoned.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 44a13ed6b8b2..0706c0788cb2 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -3189,6 +3189,17 @@ int btrfs_reset_unused_block_groups(struct btrfs_space_info *space_info, u64 num reclaimed = bg->alloc_offset; bg->zone_unusable = bg->length - bg->zone_capacity; bg->alloc_offset = 0; + /* + * The zone was just reset to empty, so alloc_offset went back to + * the start of the zone. For metadata/system block groups the + * write pointer must follow it back to the start of the zone; + * otherwise it stays stale at the previous (finished) zone end, + * and metadata written into the reused zone would sit behind the + * write pointer, could never be written out in sequential order, + * and would be stranded (pinning its folio) until unmount. + */ + if (bg->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM)) + bg->meta_write_pointer = bg->start; /* * This holds because we currently reset fully used then freed * block group. -- cgit v1.2.3 From 51a0e8399858621442807a26057bcd1cd3ced046 Mon Sep 17 00:00:00 2001 From: Dongjiang Zhu Date: Mon, 13 Jul 2026 16:50:08 +0800 Subject: btrfs: skip global block reserve accounting for rescue mounts [BUG] Mounting with rescue=ibadroots after corrupting the block group tree root triggers a NULL pointer dereference: BUG: kernel NULL pointer dereference, address: 0000000000000100 RIP: 0010:btrfs_update_global_block_rsv+0x9d/0x1c0 [btrfs] Call Trace: fill_dummy_bgs+0xd4/0x120 [btrfs] open_ctree+0xc6e/0x1ca0 [btrfs] btrfs_get_tree+0x50d/0xa40 [btrfs] The same crash occurs with a corrupted raid stripe tree root, via btrfs_read_block_groups() instead of fill_dummy_bgs(). [CAUSE] With rescue=ibadroots, btrfs_read_roots() allows the mount to continue when either root cannot be read, leaving the corresponding root pointer NULL while its on-disk feature bit remains set. btrfs_update_global_block_rsv() then dereferences the missing root based on the feature bit alone. [FIX] Rescue mounts are fully read-only and cannot start transactions, so the global reserve is never consumed. Under btrfs_is_full_ro(), mark the reserve as full and return before performing the accounting. And since we need to check if the fs is mount fully RO, export fs_is_full_ro() as btrfs_is_full_ro(), and move it to fs.h. Fixes: 8dbfc14fc736 ("btrfs: account block group tree when calculating global reserve size") Fixes: 515020900d44 ("btrfs: read raid stripe tree from disk") Suggested-by: Qu Wenruo Signed-off-by: Dongjiang Zhu [ Squash the fs_is_full_ro() export commit into this one. ] Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/block-rsv.c | 19 +++++++++++++++++-- fs/btrfs/disk-io.c | 11 +---------- fs/btrfs/fs.h | 9 +++++++++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/block-rsv.c b/fs/btrfs/block-rsv.c index 9efb3016ef11..c68a8f4b7d19 100644 --- a/fs/btrfs/block-rsv.c +++ b/fs/btrfs/block-rsv.c @@ -322,10 +322,25 @@ void btrfs_block_rsv_add_bytes(struct btrfs_block_rsv *block_rsv, void btrfs_update_global_block_rsv(struct btrfs_fs_info *fs_info) { struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv; - struct btrfs_space_info *sinfo = block_rsv->space_info; + struct btrfs_space_info *sinfo; struct btrfs_root *root, *tmp; - u64 num_bytes = btrfs_root_used(&fs_info->tree_root->root_item); unsigned int min_items = 1; + u64 num_bytes; + + /* + * A full read-only mount (rescue options) cannot start transactions, + * so the global reserve is never consumed. Mark it as full and skip + * the accounting. + */ + if (btrfs_is_full_ro(fs_info)) { + spin_lock(&block_rsv->lock); + block_rsv->full = true; + spin_unlock(&block_rsv->lock); + return; + } + + sinfo = block_rsv->space_info; + num_bytes = btrfs_root_used(&fs_info->tree_root->root_item); /* * The global block rsv is based on the size of the extent tree, the diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 0a7d80da9c94..36332df9a0f1 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3288,15 +3288,6 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount) return 0; } -static bool fs_is_full_ro(const struct btrfs_fs_info *fs_info) -{ - if (!sb_rdonly(fs_info->sb)) - return false; - if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK)) - return true; - return false; -} - /* * Try to wait for any metadata readahead, and invalidate all btree folios. * @@ -3462,7 +3453,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device WRITE_ONCE(fs_info->fs_error, -EUCLEAN); /* If the fs has any rescue options, no transaction is allowed. */ - if (fs_is_full_ro(fs_info)) + if (btrfs_is_full_ro(fs_info)) WRITE_ONCE(fs_info->fs_error, -EROFS); /* Set up fs_info before parsing mount options */ diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 5f0cfb0b5466..7ee9ec2b0efb 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -1159,6 +1159,15 @@ void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag, #define btrfs_test_opt(fs_info, opt) ((fs_info)->mount_opt & \ BTRFS_MOUNT_##opt) +static inline bool btrfs_is_full_ro(const struct btrfs_fs_info *fs_info) +{ + if (!sb_rdonly(fs_info->sb)) + return false; + if (unlikely(fs_info->mount_opt & BTRFS_MOUNT_FULL_RO_MASK)) + return true; + return false; +} + static inline bool btrfs_fs_closing(const struct btrfs_fs_info *fs_info) { return unlikely(test_bit(BTRFS_FS_CLOSING_START, &fs_info->flags)); -- cgit v1.2.3 From c438d34ec1eed4d23e2081d61c5e96f5176898a9 Mon Sep 17 00:00:00 2001 From: Dongjiang Zhu Date: Mon, 13 Jul 2026 16:50:10 +0800 Subject: btrfs: report missing raid stripe tree root during lookup When rescue=ibadroots ignores a failure to load the raid stripe tree root, fs_info->stripe_root remains NULL. After the rescue mount proceeds, reading file data that requires the raid stripe tree reaches btrfs_get_raid_extent_offset(). Currently btrfs_search_slot() handles the NULL root and returns -EINVAL. This avoids a NULL pointer dereference, but provides no diagnostic and incorrectly describes missing filesystem metadata as an invalid argument. Check stripe_root before allocating a path, emit a rate-limited error with the logical address, and return -EUCLEAN. Lookups with a valid stripe root are unchanged. Reviewed-by: Qu Wenruo Signed-off-by: Dongjiang Zhu Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/raid-stripe-tree.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c index 454a95bf542a..b210371ce91e 100644 --- a/fs/btrfs/raid-stripe-tree.c +++ b/fs/btrfs/raid-stripe-tree.c @@ -414,6 +414,12 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, int slot; int ret; + if (unlikely(!stripe_root)) { + btrfs_err_rl(fs_info, "missing raid stripe tree root for logical %llu", + logical); + return -EUCLEAN; + } + stripe_key.objectid = logical; stripe_key.type = BTRFS_RAID_STRIPE_KEY; stripe_key.offset = 0; -- cgit v1.2.3 From 330dcc553f282e8dc0b88c9495b4c296465364e1 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Sun, 12 Jul 2026 13:12:51 +0930 Subject: btrfs: raid56: fix an incorrect csum skip during scrub Commit 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() helper") uses the new helper to replace the nested loop inside verify_bio_data_sectors(), which simplifies the code. However that also changed the behavior of "continue" when a block has no data checksum. Previously the "continue" would skip the old for() loop, which would also increase @total_sector_nr. Now the "continue" will skip the new btrfs_bio_for_each_block_all() loop, which doesn't update @total_sector_nr. This means if we hit a block that has no data checksum, we will skip all the remaining blocks no matter if they have data checksum. As @total_sector_nr will never be updated, and that test_bit() will always return false. Fix it by increasing @total_sector_nr before calling "continue". Fixes: 7425a2894019 ("btrfs: introduce btrfs_bio_for_each_block_all() helper") Reviewed-by: Daniel Vacek Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/raid56.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index f7f7db40994c..3f2896e793e3 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -1679,8 +1679,10 @@ static void verify_bio_data_sectors(struct btrfs_raid_bio *rbio, continue; /* No csum for this sector, skip to the next sector. */ - if (!test_bit(total_sector_nr, rbio->csum_bitmap)) + if (!test_bit(total_sector_nr, rbio->csum_bitmap)) { + total_sector_nr++; continue; + } expected_csum = rbio->csum_buf + total_sector_nr * fs_info->csum_size; btrfs_calculate_block_csum_pages(fs_info, paddrs, csum_buf); -- cgit v1.2.3 From 8bc4d7209611e8aa9d5409b6a4a86a9eb91b69a3 Mon Sep 17 00:00:00 2001 From: Guanghui Yang <3497809730@qq.com> Date: Tue, 14 Jul 2026 17:55:43 +0800 Subject: btrfs: zoned: fix missing chunk metadata reservation reserve_chunk_space() stores the return value of btrfs_zoned_activate_one_bg() in ret. The helper can return 1 after successfully activating a block group, but ret is later used to decide whether to reserve metadata for chunk tree updates. As a result, successful activation skips btrfs_block_rsv_add() and leaves trans->chunk_bytes_reserved unchanged. Use a separate variable for the activation result so positive success does not affect the later reservation. Keep activation failures in ret instead of returning early so the function uses the common tail path. Fixes: b6a98021e401 ("btrfs: zoned: activate necessary block group") CC: stable@vger.kernel.org Reviewed-by: Johannes Thumshirn Signed-off-by: Guanghui Yang <3497809730@qq.com> Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index ab76a5173272..8def7abb728f 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -4532,25 +4532,29 @@ static void reserve_chunk_space(struct btrfs_trans_handle *trans, if (IS_ERR(bg)) { ret = PTR_ERR(bg); } else { + int activate_ret; + /* * We have a new chunk. We also need to activate it for * zoned filesystem. */ - ret = btrfs_zoned_activate_one_bg(info, true); - if (ret < 0) - return; - - /* - * If we fail to add the chunk item here, we end up - * trying again at phase 2 of chunk allocation, at - * btrfs_create_pending_block_groups(). So ignore - * any error here. An ENOSPC here could happen, due to - * the cases described at do_chunk_alloc() - the system - * block group we just created was just turned into RO - * mode by a scrub for example, or a running discard - * temporarily removed its free space entries, etc. - */ - btrfs_chunk_alloc_add_chunk_item(trans, bg); + activate_ret = btrfs_zoned_activate_one_bg(info, true); + if (activate_ret < 0) { + ret = activate_ret; + } else { + /* + * If we fail to add the chunk item here, we end + * up trying again at phase 2 of chunk allocation, + * at btrfs_create_pending_block_groups(). So + * ignore any error here. An ENOSPC here could + * happen, due to the cases described at + * do_chunk_alloc() - the system block group we + * just created was just turned into RO mode by a + * scrub for example, or a running discard + * temporarily removed its free space entries, etc. + */ + btrfs_chunk_alloc_add_chunk_item(trans, bg); + } } } -- cgit v1.2.3 From 0d214d14be503f16238999723acfcbf63f04cc8b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 14 Jul 2026 13:32:24 -0700 Subject: btrfs: initialize 'args' to avoid compiler warning in btrfs_ioctl_get_csums() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [COMPILER WARNING] With GCC 11.5.0 and KASAN enabled on ARM, the following warning is triggered during compiling: In file included from ./include/asm-generic/rwonce.h:26, from ./arch/arm64/include/asm/rwonce.h:81, from ./include/linux/compiler.h:369, from ./include/linux/array_size.h:5, from ./include/linux/kernel.h:16, from fs/btrfs/ioctl.c:6: In function ‘instrument_copy_from_user_before’, inlined from ‘_inline_copy_from_user’ at ./include/linux/uaccess.h:184:2, inlined from ‘copy_from_user’ at ./include/linux/uaccess.h:222:9, inlined from ‘btrfs_ioctl_get_csums.isra’ at fs/btrfs/ioctl.c:5220:6: ./include/linux/kasan-checks.h:38:27: warning: ‘args’ may be used uninitialized [-Wmaybe-uninitialized] 38 | #define kasan_check_write __kasan_check_write ./include/linux/instrumented.h:146:9: note: in expansion of macro ‘kasan_check_write’ 146 | kasan_check_write(to, n); | ^~~~~~~~~~~~~~~~~ fs/btrfs/ioctl.c: In function ‘btrfs_ioctl_get_csums.isra’: ./include/linux/kasan-checks.h:20:6: note: by argument 1 of type ‘const volatile void *’ to ‘__kasan_check_write’ declared here 20 | bool __kasan_check_write(const volatile void *p, unsigned int size); | ^~~~~~~~~~~~~~~~~~~ fs/btrfs/ioctl.c:5201:43: note: ‘args’ declared here 5201 | struct btrfs_ioctl_get_csums_args args; | ^~~~ [POSSIBLE FALSE ALERTS] This seems to be a false alert from certain GCC versions. The @args is immediately over-written by copy_from_user(), and there is no code touching that @args until copy_from_user() finished correctly. [WORKAROUND] Initialize 'args' to zero, which suppresses the warning. Reviewed-by: Qu Wenruo Signed-off-by: Paul E. McKenney Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 68b33f365fda..baa645e98812 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -5205,7 +5205,7 @@ static int btrfs_ioctl_get_csums(struct file *file, void __user *argp) struct btrfs_inode *inode = BTRFS_I(vfs_inode); struct btrfs_fs_info *fs_info = inode->root->fs_info; struct btrfs_root *root = inode->root; - struct btrfs_ioctl_get_csums_args args; + struct btrfs_ioctl_get_csums_args args = { 0 }; BTRFS_PATH_AUTO_FREE(path); const u64 ino = btrfs_ino(inode); const u32 csum_size = fs_info->csum_size; -- cgit v1.2.3 From ab602da96a915d42dcb1b0b322e8daea0f71b51f Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Thu, 16 Jul 2026 10:23:12 +0200 Subject: btrfs: zoned: skip fully truncated ordered extents at zone finish A fully truncated ordered extent (truncated_len == 0) wrote no data, so its ->csum_list is empty and btrfs_finish_ordered_zoned() trips: assertion failed: !list_empty(&ordered->csum_list), in fs/btrfs/zoned.c:2141 Since commit 66ff4d366e7e a short or cancelled direct IO write finishes the unsubmitted ordered extent as truncated with uptodate = true instead of setting BTRFS_ORDERED_IOERR, so it now reaches btrfs_finish_ordered_zoned() rather than being skipped by the IOERR check in btrfs_finish_ordered_io(). generic/208 hits this on a zoned filesystem. Return early for these, like the BTRFS_ORDERED_PREALLOC case; there is no zone append result to record and btrfs_finish_one_ordered() skips them too. Fixes: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write") Reviewed-by: Qu Wenruo Signed-off-by: Johannes Thumshirn Signed-off-by: David Sterba --- fs/btrfs/zoned.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 0706c0788cb2..a016cb471beb 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -2138,6 +2138,16 @@ void btrfs_finish_ordered_zoned(struct btrfs_ordered_extent *ordered) if (test_bit(BTRFS_ORDERED_PREALLOC, &ordered->flags)) return; + /* + * A fully truncated ordered extent wrote no data and so has + * no zone append result to record. + */ + if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags) && + ordered->truncated_len == 0) { + ASSERT(list_empty(&ordered->csum_list)); + return; + } + ASSERT(!list_empty(&ordered->csum_list)); sum = list_first_entry(&ordered->csum_list, struct btrfs_ordered_sum, list); logical = sum->logical; -- cgit v1.2.3 From c4c0673e4cb15b0c127e6d00732a2427bdd12c11 Mon Sep 17 00:00:00 2001 From: Mykola Lysenko Date: Sat, 18 Jul 2026 16:37:10 -0700 Subject: btrfs: raid56: fix scrub read assembly submitting no reads Commit 5387bd958180 ("btrfs: raid56: remove sector_ptr structure") converted the bio-list membership checks from sector pointers to physical addresses. The two conversions in rmw_assemble_write_bios() kept their polarity (skip the sector when it is NOT in the bio list, i.e. when there is nothing to write), but scrub_assemble_read_bios() has the opposite polarity -- skip the sector when it IS in the bio list, because then there is nothing to read -- and the conversion flipped it: - sector = sector_in_rbio(rbio, stripe, sectornr, 1); - if (sector) + paddr = sector_paddr_in_rbio(rbio, stripe, sectornr, 1); + if (paddr == INVALID_PADDR) continue; Since a parity-scrub rbio's bio list only holds the empty completion bio, the result is that scrub_assemble_read_bios() submits no reads at all. finish_parity_scrub() then compares the parity it computes from the (cached, correct) data stripes against whatever happens to be in the freshly allocated, uninitialized stripe pages: - if the garbage differs from the computed parity, the sector is "repaired" and written back -- accidentally producing the correct on-disk result; - if a recycled page happens to still hold the old (correct) parity content, the sector is deemed clean, dropped from dbitmap, and the actually-corrupt on-disk parity is left in place. (Scrub reports no errors either way: there is no counter for P/Q corruption by design, so the bug here is purely the failure to read and repair.) The second case is intermittent because it depends on page-allocator recycling. Observed with fstests btrfs/297 (raid5, 2 devices): the corrupted P stripe intermittently stays corrupt after a scrub -- roughly 1/10 runs on x86-64 KVM and up to 7/8 on a UML build whose timing favors page reuse. Since the bio-list check can never be true for a parity-scrub rbio -- raid56_parity_alloc_scrub_rbio() adds a single empty completion bio (asserting bi_size == 0), bio_paddrs[] is only populated by index_rbio_pages() which is never called for BTRFS_RBIO_PARITY_SCRUB, and rbio_can_merge() refuses to merge rbios of different operations -- remove the dead check entirely and assert the invariant instead, as suggested by Qu Wenruo. After this fix the injected corruption is read, detected and repaired in every run (8/8 UML, 10/10 KVM), and the new assertion never fires across the full fstests raid group. Fixes: 5387bd958180 ("btrfs: raid56: remove sector_ptr structure") CC: stable@vger.kernel.org # 7.1+ Suggested-by: Qu Wenruo Assisted-by: Claude:claude-fable-5 Reviewed-by: Qu Wenruo Signed-off-by: Mykola Lysenko Signed-off-by: David Sterba --- fs/btrfs/raid56.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 3f2896e793e3..ca94c9e3d563 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -2911,13 +2911,12 @@ static int scrub_assemble_read_bios(struct btrfs_raid_bio *rbio) continue; /* - * We want to find all the sectors missing from the rbio and - * read them from the disk. If sector_paddr_in_rbio() finds a sector - * in the bio list we don't need to read it off the stripe. + * A parity-scrub rbio carries no data in its bio list: the + * only bio there is the empty completion bio added by + * raid56_parity_alloc_scrub_rbio(). Every sector is read + * from the stripe, so only assert that invariant here. */ - paddrs = sector_paddrs_in_rbio(rbio, stripe, sectornr, 1); - if (paddrs == NULL) - continue; + ASSERT(!sector_paddrs_in_rbio(rbio, stripe, sectornr, 1)); paddrs = rbio_stripe_paddrs(rbio, stripe, sectornr); /* -- cgit v1.2.3 From be7cc4656eb1f54029610e82d1f0fdd3f9b5ec0a Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Tue, 7 Jul 2026 02:00:54 -0500 Subject: net/iucv: fix use-after-free of a severed iucv_path af_iucv queues not-yet-received message notifications on iucv->message_q, each holding a raw pointer to the connection's iucv_path. When the peer severs the connection, iucv_sever_path() frees that path with iucv_path_free() but leaves the notifications queued. A later recvmsg() drains message_q via iucv_process_message_q() and hands the stale path to message_receive() -- a use-after-free of the freed iucv_path. Drop the queued notifications when the path is severed; once the path is gone they can no longer be received. This also frees the notifications leaked when a socket is closed with messages still queued. Fixes: f0703c80e515 ("[AF_IUCV]: postpone receival of iucv-packets") Closes: https://sashiko.dev/#/patchset/20260705-b4-disp-fc79c0dc-v1-1-d2cdcb57afa9@proton.me?part=1 Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260707-b4-disp-783fedbb-v1-1-463b9dbda2ea@proton.me Signed-off-by: Paolo Abeni --- net/iucv/af_iucv.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index b85fb9767dec..e3e71d168c47 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -337,6 +337,7 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) unsigned char user_data[16]; struct iucv_sock *iucv = iucv_sk(sk); struct iucv_path *path = iucv->path; + struct sock_msg_q *p, *n; /* Whoever resets the path pointer, must sever and free it. */ if (xchg(&iucv->path, NULL)) { @@ -348,6 +349,19 @@ static void iucv_sever_path(struct sock *sk, int with_user_data) } else pr_iucv->path_sever(path, NULL); iucv_path_free(path); + + /* + * Message notifications queued on message_q still reference + * the now freed path; drop them, otherwise a later recvmsg() + * would pass the freed iucv_path to message_receive() via + * iucv_process_message_q(). + */ + spin_lock_bh(&iucv->message_q.lock); + list_for_each_entry_safe(p, n, &iucv->message_q.list, list) { + list_del(&p->list); + kfree(p); + } + spin_unlock_bh(&iucv->message_q.lock); } } -- cgit v1.2.3 From 847b371debf3c8c72384ab7b9a0c4123a74cc925 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 14 Jul 2026 10:00:25 -0700 Subject: drm/gpusvm: Fix MM reference leak in drm_gpusvm_range_evict If kvmalloc_array() fails in drm_gpusvm_range_evict(), the MM reference acquired earlier is not released, resulting in a reference leak. Fix this by dropping the MM reference on the kvmalloc_array() failure path. Fixes: 99624bdff867 ("drm/gpusvm: Add support for GPU Shared Virtual Memory") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260714170025.3487974-1-matthew.brost@intel.com --- drivers/gpu/drm/drm_gpusvm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 9a06ff7d2608..9aeaca02e928 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1753,8 +1753,10 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm, return -EFAULT; pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); - if (!pfns) + if (!pfns) { + mmput(mm); return -ENOMEM; + } hmm_range.hmm_pfns = pfns; while (!time_after(jiffies, timeout)) { -- cgit v1.2.3 From 67b8bfd4ec7dac6e79a7ad9ad19a7a9d6fc35a26 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Tue, 14 Jul 2026 15:54:32 -0700 Subject: drm/gpusvm: Zero HMM PFNs before scanning ranges drm_gpusvm_scan_mm() asks HMM to report the current CPU page-table state without faulting missing entries by leaving default_flags set to zero. The HMM PFN array is still caller-owned input/output state, and the framework may preserve input bits while filling entries. It is not safe for the caller to hand HMM an uninitialized array and then treat entries without HMM_PFN_VALID as an authoritative unpopulated result. Use kvcalloc() for the temporary PFN array so entries that are not reported as valid start from the documented zero state. This prevents random stack or heap contents from being interpreted as HMM PFN flags or PFN values during the scan. Fixes: f1d08a586482 ("drm/gpusvm: Introduce a function to scan the current migration state") Cc: stable@vger.kernel.org Signed-off-by: Stanislav Kinsburskii Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/178406967042.1113483.2116704310277917086.stgit@skinsburskii --- drivers/gpu/drm/drm_gpusvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 9aeaca02e928..18ddef94da47 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -781,7 +781,7 @@ enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range, const struct dev_pagemap *other = NULL; int err, i; - pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); + pfns = kvcalloc(npages, sizeof(*pfns), GFP_KERNEL); if (!pfns) return DRM_GPUSVM_SCAN_UNPOPULATED; -- cgit v1.2.3 From d163725af84ad958bb74ef4f6eb906a04daa8902 Mon Sep 17 00:00:00 2001 From: Aniket Negi Date: Tue, 7 Jul 2026 20:56:39 +0530 Subject: net: airoha: fix MIB stats collection to be lossless REG_FE_GDM_MIB_CLEAR after every read creates a race window where packets arriving between read and clear are lost from statistics. Switch to a delta-based approach instead: - 64-bit H+L registers (ok pkts/bytes, E64..L1023): read absolute hardware total directly into a local variable; clamp with max(new, old) to prevent torn-read regression when the counter carries between the two reads. - 32-bit registers (drops, bc, mc, errors, runt, long): accumulate (u32)(curr - prev) into a 64-bit software counter; unsigned subtraction handles wrap-around transparently. - tx/rx_len[0] ([0,64] bucket): combines RUNT_CNT (32-bit, delta via tx_runt/rx_runt) and E64_CNT (64-bit, absolute) into a single local accumulator; max(new, old) applied here too to guard against a torn read of E64 when the RUNT accumulator is unchanged between polls. MIB counters are zeroed by the SCU FE reset (EN7581_FE_RST) asserted in airoha_hw_init() at module load, so no explicit MIB clear is needed in airoha_fe_init(). Merge airoha_dev_get_hw_stats() into airoha_update_hw_stats() and move stats_lock inside. Plain spin_lock() is correct: the function is only called from ndo_get_stats64() in process context. Each dev refreshes only its own MIB counters; sibling devs on a shared GDM3/4 port are polled when their own netdev is queried. Fixes: 8f4695fb67b2 ("net: airoha: better handle MIBs for GDM ports with multiple devs attached") Signed-off-by: Aniket Negi Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260707152639.105628-1-aniket.negi03@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/airoha/airoha_eth.c | 171 +++++++++++++++++++------------ drivers/net/ethernet/airoha/airoha_eth.h | 27 +++++ 2 files changed, 132 insertions(+), 66 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 59001fd4b6f7..90aa8b0210bd 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1686,11 +1686,14 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma) } } -static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev) +static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) { struct airoha_gdm_port *port = dev->port; struct airoha_eth *eth = dev->eth; u32 val, i = 0; + u64 data; + + spin_lock(&port->stats_lock); /* Read relevant MIB for GDM with multiple port attached */ if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX) @@ -1701,152 +1704,188 @@ static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev) u64_stats_update_begin(&dev->stats.syncp); - /* TX */ + /* TX - 64-bit H+L registers: hw accumulates the total, read directly. + * Use local variable to prevent readers from seeing intermediate values. + * Clamp to prevent regression from torn reads between H and L. + */ val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id)); - dev->stats.tx_ok_pkts += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id)); - dev->stats.tx_ok_pkts += val; + data += val; + dev->stats.tx_ok_pkts = max(data, dev->stats.tx_ok_pkts); val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id)); - dev->stats.tx_ok_bytes += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id)); - dev->stats.tx_ok_bytes += val; + data += val; + dev->stats.tx_ok_bytes = max(data, dev->stats.tx_ok_bytes); + /* TX - 32-bit registers: accumulate delta to handle wrap-around. */ val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id)); - dev->stats.tx_drops += val; + dev->stats.tx_drops += (u32)(val - dev->stats.mib_prev.tx_drops); + dev->stats.mib_prev.tx_drops = val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id)); - dev->stats.tx_broadcast += val; + dev->stats.tx_broadcast += (u32)(val - dev->stats.mib_prev.tx_broadcast); + dev->stats.mib_prev.tx_broadcast = val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id)); - dev->stats.tx_multicast += val; + dev->stats.tx_multicast += (u32)(val - dev->stats.mib_prev.tx_multicast); + dev->stats.mib_prev.tx_multicast = val; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id)); - dev->stats.tx_len[i] += val; + dev->stats.mib_prev.tx_runt64 += + (u32)(val - dev->stats.mib_prev.tx_runt); + dev->stats.mib_prev.tx_runt = val; + /* tx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */ + data = dev->stats.mib_prev.tx_runt64; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data += (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id)); - dev->stats.tx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id)); - dev->stats.tx_len[i++] += val; + data += val; + dev->stats.tx_len[i] = max(data, dev->stats.tx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id)); - dev->stats.tx_len[i++] += val; + dev->stats.tx_len[i++] += (u32)(val - dev->stats.mib_prev.tx_long); + dev->stats.mib_prev.tx_long = val; /* RX */ val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id)); - dev->stats.rx_ok_pkts += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id)); - dev->stats.rx_ok_pkts += val; + data += val; + dev->stats.rx_ok_pkts = max(data, dev->stats.rx_ok_pkts); val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id)); - dev->stats.rx_ok_bytes += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id)); - dev->stats.rx_ok_bytes += val; + data += val; + dev->stats.rx_ok_bytes = max(data, dev->stats.rx_ok_bytes); val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id)); - dev->stats.rx_drops += val; + dev->stats.rx_drops += (u32)(val - dev->stats.mib_prev.rx_drops); + dev->stats.mib_prev.rx_drops = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id)); - dev->stats.rx_broadcast += val; + dev->stats.rx_broadcast += (u32)(val - dev->stats.mib_prev.rx_broadcast); + dev->stats.mib_prev.rx_broadcast = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id)); - dev->stats.rx_multicast += val; + dev->stats.rx_multicast += (u32)(val - dev->stats.mib_prev.rx_multicast); + dev->stats.mib_prev.rx_multicast = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id)); - dev->stats.rx_errors += val; + dev->stats.rx_errors += (u32)(val - dev->stats.mib_prev.rx_errors); + dev->stats.mib_prev.rx_errors = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id)); - dev->stats.rx_crc_error += val; + dev->stats.rx_crc_error += (u32)(val - dev->stats.mib_prev.rx_crc_error); + dev->stats.mib_prev.rx_crc_error = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id)); - dev->stats.rx_over_errors += val; + dev->stats.rx_over_errors += (u32)(val - dev->stats.mib_prev.rx_over_errors); + dev->stats.mib_prev.rx_over_errors = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id)); - dev->stats.rx_fragment += val; + dev->stats.rx_fragment += (u32)(val - dev->stats.mib_prev.rx_fragment); + dev->stats.mib_prev.rx_fragment = val; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id)); - dev->stats.rx_jabber += val; + dev->stats.rx_jabber += (u32)(val - dev->stats.mib_prev.rx_jabber); + dev->stats.mib_prev.rx_jabber = val; i = 0; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id)); - dev->stats.rx_len[i] += val; + dev->stats.mib_prev.rx_runt64 += + (u32)(val - dev->stats.mib_prev.rx_runt); + dev->stats.mib_prev.rx_runt = val; + /* rx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */ + data = dev->stats.mib_prev.rx_runt64; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data += (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id)); - dev->stats.rx_len[i] += ((u64)val << 32); + data = (u64)val << 32; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id)); - dev->stats.rx_len[i++] += val; + data += val; + dev->stats.rx_len[i] = max(data, dev->stats.rx_len[i]); + i++; val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id)); - dev->stats.rx_len[i++] += val; + dev->stats.rx_len[i] += (u32)(val - dev->stats.mib_prev.rx_long); + dev->stats.mib_prev.rx_long = val; u64_stats_update_end(&dev->stats.syncp); -} - -static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) -{ - struct airoha_gdm_port *port = dev->port; - int i; - - spin_lock(&port->stats_lock); - - for (i = 0; i < ARRAY_SIZE(port->devs); i++) { - if (port->devs[i]) - airoha_dev_get_hw_stats(port->devs[i]); - } - - /* Reset MIB counters */ - airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id), - FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK); spin_unlock(&port->stats_lock); } diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index f6d01a8e8da1..fe934f9ffe8a 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -245,6 +245,33 @@ struct airoha_hw_stats { u64 rx_fragment; u64 rx_jabber; u64 rx_len[7]; + + struct { + /* Previous HW register values for 32-bit counter delta + * tracking. Storing the last seen value and accumulating + * (u32)(curr - prev) into the 64-bit software counter + * handles wrap-around transparently via unsigned arithmetic. + * tx_runt64/rx_runt64 hold the running sum of runt deltas. + * These fields are never reported to userspace. + */ + u32 tx_drops; + u32 tx_broadcast; + u32 tx_multicast; + u32 tx_runt; + u32 tx_long; + u64 tx_runt64; + u32 rx_drops; + u32 rx_broadcast; + u32 rx_multicast; + u32 rx_errors; + u32 rx_crc_error; + u32 rx_over_errors; + u32 rx_fragment; + u32 rx_jabber; + u32 rx_runt; + u32 rx_long; + u64 rx_runt64; + } mib_prev; }; enum { -- cgit v1.2.3 From 9573818cc1b7ac22176d0a2b60bfbc440e94f7d5 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Tue, 21 Jul 2026 15:14:22 +0800 Subject: ALSA: hda/realtek - Add quirk for Dell Pro QC1255 Vendor want to add more machine on this workaround. Fixes: 97272a5704bf ("ALSA: hda/realtek - Fixed Headphone noise issue for Dell QCM1255") Signed-off-by: Kailang Yang Link: https://lore.kernel.org/e13d08e96ac449b6994d56dfe6ce3f5c@realtek.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 5840afbcf949..379e1458f4ac 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7007,7 +7007,11 @@ 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, 0x0d56, "Dell Pro Tower QCT1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0d58, "Dell Pro Micro QCM1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0d5a, "Dell Pro Slim QCS1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0e6b, "Dell Pro Tower QCT1255", ALC236_FIXUP_DELL_HP_POP_NOISE), + SND_PCI_QUIRK(0x1028, 0x0e6c, "Dell Pro Slim QCS1255", 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), -- cgit v1.2.3 From 4032f8ed10fcb84d41c508dfb04be96589f78dfe Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Tue, 7 Jul 2026 15:16:35 -0700 Subject: openvswitch: fix GSO userspace truncation underflow OVS_ACTION_ATTR_TRUNC currently stores a delta from the original skb length in OVS_CB(skb)->cutlen. When a later userspace action segments a GSO skb, queue_gso_packets() reuses that delta for each smaller segment. A segment can then reach queue_userspace_packet() with cutlen greater than skb->len, underflowing the length passed to skb_zerocopy(). Store the maximum preserved length instead and bound each consumer against the current skb length. Use U32_MAX as the no-truncation sentinel so the value remains valid if skb geometry changes before a consumer handles it. Fixes: f2a4d086ed4c ("openvswitch: Add packet truncation support.") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.5 Signed-off-by: Kyle Zeng Reviewed-by: Ilya Maximets Reviewed-by: Aaron Conole Link: https://patch.msgid.link/20260707221635.27489-1-kylebot@openai.com Signed-off-by: Paolo Abeni --- net/openvswitch/actions.c | 19 +++++++------------ net/openvswitch/datapath.c | 25 ++++++++++++++----------- net/openvswitch/datapath.h | 2 +- net/openvswitch/vport.c | 2 +- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 140388a18ae0..513fca6a8e8a 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -837,12 +837,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, u16 mru = OVS_CB(skb)->mru; u32 cutlen = OVS_CB(skb)->cutlen; - if (unlikely(cutlen > 0)) { - if (skb->len - cutlen > ovs_mac_header_len(key)) - pskb_trim(skb, skb->len - cutlen); - else - pskb_trim(skb, ovs_mac_header_len(key)); - } + if (unlikely(cutlen < skb->len)) + pskb_trim(skb, max(cutlen, ovs_mac_header_len(key))); if (likely(!mru || (skb->len <= mru + vport->dev->hard_header_len))) { @@ -1234,7 +1230,7 @@ static void execute_psample(struct datapath *dp, struct sk_buff *skb, psample_group.net = ovs_dp_get_net(dp); md.in_ifindex = OVS_CB(skb)->input_vport->dev->ifindex; - md.trunc_size = skb->len - OVS_CB(skb)->cutlen; + md.trunc_size = min(skb->len, OVS_CB(skb)->cutlen); md.rate_as_probability = 1; rate = OVS_CB(skb)->probability ? OVS_CB(skb)->probability : U32_MAX; @@ -1284,22 +1280,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, clone = skb_clone(skb, GFP_ATOMIC); if (clone) do_output(dp, clone, port, key); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; break; } case OVS_ACTION_ATTR_TRUNC: { struct ovs_action_trunc *trunc = nla_data(a); - if (skb->len > trunc->max_len) - OVS_CB(skb)->cutlen = skb->len - trunc->max_len; + OVS_CB(skb)->cutlen = trunc->max_len; break; } case OVS_ACTION_ATTR_USERSPACE: output_userspace(dp, skb, key, a, attr, len, OVS_CB(skb)->cutlen); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; if (nla_is_last(a, rem)) { consume_skb(skb); return 0; @@ -1453,7 +1448,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, case OVS_ACTION_ATTR_PSAMPLE: execute_psample(dp, skb, a); - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; if (nla_is_last(a, rem)) { consume_skb(skb); return 0; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index f0164817d9b7..eaf332b156d7 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -276,7 +276,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) upcall.portid = ovs_vport_find_upcall_portid(p, skb); upcall.mru = OVS_CB(skb)->mru; - error = ovs_dp_upcall(dp, skb, key, &upcall, 0); + error = ovs_dp_upcall(dp, skb, key, &upcall, U32_MAX); switch (error) { case 0: case -EAGAIN: @@ -457,7 +457,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, struct sk_buff *nskb = NULL; struct sk_buff *user_skb = NULL; /* to be queued to userspace */ struct nlattr *nla; - size_t len; + size_t msg_size; + size_t skb_len; unsigned int hlen; int err, dp_ifindex; u64 hash; @@ -478,7 +479,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, skb = nskb; } - if (nla_attr_size(skb->len) > USHRT_MAX) { + skb_len = min(skb->len, cutlen); + if (nla_attr_size(skb_len) > USHRT_MAX) { err = -EFBIG; goto out; } @@ -493,13 +495,13 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, * padding logic. Only perform zerocopy if padding is not required. */ if (dp->user_features & OVS_DP_F_UNALIGNED) - hlen = skb_zerocopy_headlen(skb); + hlen = min(skb_zerocopy_headlen(skb), cutlen); else - hlen = skb->len; + hlen = skb_len; - len = upcall_msg_size(upcall_info, hlen - cutlen, - OVS_CB(skb)->acts_origlen); - user_skb = genlmsg_new(len, GFP_ATOMIC); + msg_size = upcall_msg_size(upcall_info, hlen, + OVS_CB(skb)->acts_origlen); + user_skb = genlmsg_new(msg_size, GFP_ATOMIC); if (!user_skb) { err = -ENOMEM; goto out; @@ -560,7 +562,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, } /* Add OVS_PACKET_ATTR_LEN when packet is truncated */ - if (cutlen > 0 && + if (skb_len < skb->len && nla_put_u32(user_skb, OVS_PACKET_ATTR_LEN, skb->len)) { err = -ENOBUFS; goto out; @@ -585,9 +587,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, err = -ENOBUFS; goto out; } - nla->nla_len = nla_attr_size(skb->len - cutlen); + nla->nla_len = nla_attr_size(skb_len); - err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen); + err = skb_zerocopy(user_skb, skb, skb_len, hlen); if (err) goto out; @@ -644,6 +646,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) packet->ignore_df = 1; } OVS_CB(packet)->mru = mru; + OVS_CB(packet)->cutlen = U32_MAX; if (a[OVS_PACKET_ATTR_HASH]) { hash = nla_get_u64(a[OVS_PACKET_ATTR_HASH]); diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index db0c3e69d66c..696640e88fa7 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -118,7 +118,7 @@ struct datapath { * @mru: The maximum received fragement size; 0 if the packet is not * fragmented. * @acts_origlen: The netlink size of the flow actions applied to this skb. - * @cutlen: The number of bytes from the packet end to be removed. + * @cutlen: The number of bytes in the packet to preserve on output. * @probability: The sampling probability that was applied to this skb; 0 means * no sampling has occurred; U32_MAX means 100% probability. * @upcall_pid: Netlink socket PID to use for sending this packet to userspace; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 56b2e2d1a749..12741485c939 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -502,7 +502,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, OVS_CB(skb)->input_vport = vport; OVS_CB(skb)->mru = 0; - OVS_CB(skb)->cutlen = 0; + OVS_CB(skb)->cutlen = U32_MAX; OVS_CB(skb)->probability = 0; OVS_CB(skb)->upcall_pid = 0; if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { -- cgit v1.2.3 From e32649b4bad90a6216d8e93cd7dd050af8ac9740 Mon Sep 17 00:00:00 2001 From: Manjunath Patil Date: Tue, 7 Jul 2026 16:39:11 -0700 Subject: net/mlx5e: Use sender devcom for MPV master-up After PCIe DPC recovery, mlx5 reloads the affected functions and replays multiport affiliation events. In the reported failure, the first relevant device error was: pcieport 0000:10:01.1: DPC: containment event pcieport 0000:10:01.1: PCIe Bus Error: severity=Uncorrected (Fatal) pcieport 0000:10:01.1: [ 5] SDES (First) mlx5 recovered the PCI functions and resumed 0000:11:00.1. During that resume, RDMA multiport binding replayed MLX5_DRIVER_EVENT_AFFILIATION_DONE and mlx5e sent MPV_DEVCOM_MASTER_UP. The host then panicked with: BUG: kernel NULL pointer dereference, address: 0000000000000010 RIP: mlx5_devcom_comp_set_ready+0x5/0x40 [mlx5_core] RDI: 0000000000000000 Call trace included: mlx5_devcom_comp_set_ready mlx5e_devcom_event_mpv mlx5_devcom_send_event mlx5_ib_bind_slave_port mlx5r_mp_probe mlx5_pci_resume MPV devcom registration publishes mlx5e private data to the component peer list before mlx5e_devcom_init_mpv() stores the returned component device in priv->devcom. A concurrent master-up event can therefore reach a peer whose private data is visible but whose priv->devcom backpointer is still NULL. MPV_DEVCOM_MASTER_UP already carries the sender/master mlx5e private data as event_data. The ready bit is stored on the shared devcom component, not on an individual peer. Use the sender devcom when marking the MPV component ready. This preserves the readiness transition while avoiding a NULL dereference of the peer devcom pointer during affiliation replay after PCI error recovery. Fixes: bf11485f8419 ("net/mlx5: Register mlx5e priv to devcom in MPV mode") Assisted-by: Codex:gpt-5 Signed-off-by: Manjunath Patil Cc: stable@vger.kernel.org # 6.7+ Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260707233911.3651139-1-manjunath.b.patil@oracle.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index aa8610cedaa8..c1acb9012d3f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -211,11 +211,11 @@ static void mlx5e_disable_async_events(struct mlx5e_priv *priv) static int mlx5e_devcom_event_mpv(int event, void *my_data, void *event_data) { - struct mlx5e_priv *slave_priv = my_data; + struct mlx5e_priv *master_priv = event_data; switch (event) { case MPV_DEVCOM_MASTER_UP: - mlx5_devcom_comp_set_ready(slave_priv->devcom, true); + mlx5_devcom_comp_set_ready(master_priv->devcom, true); break; case MPV_DEVCOM_MASTER_DOWN: /* no need for comp set ready false since we unregister after -- cgit v1.2.3 From 5521ae71e32a8069ed4ca6e792179dc57bc43ab2 Mon Sep 17 00:00:00 2001 From: Aldo Ariel Panzardo Date: Tue, 7 Jul 2026 19:43:14 -0700 Subject: rds: drop incoming messages that cross network namespace boundaries rds_find_bound() looks up the destination socket using a global rhashtable keyed solely on (addr, port, scope_id). Network namespaces are not part of the key, so a sender in netns A can deliver an incoming message (inc) to a socket that lives in a different netns B. When this happens, inc->i_conn points to an rds_connection whose c_net is netns A, but the receiving rs lives in netns B. Once the child process that created netns A exits, cleanup_net() calls rds_loop_exit_net() -> rds_loop_kill_conns() -> rds_conn_destroy(), freeing that connection. If the survivor socket in netns B still holds the inc, any subsequent dereference of inc->i_conn is a use-after-free. There are two dangerous sites in rds_clear_recv_queue(): 1. inc->i_conn->c_lcong (offset 88 of freed rds_connection, size 200) read via rds_recv_rcvbuf_delta() -- confirmed by KASAN. 2. inc->i_conn->c_trans->inc_free(inc) (function pointer at offset 80) called via rds_inc_put() when the inc refcount reaches zero -- same race window, potential call-through-freed-object primitive. The bug is reachable from unprivileged user namespaces (CLONE_NEWUSER + CLONE_NEWNET), available since Linux 3.8. Fix this by rejecting the delivery in rds_recv_incoming() when the socket returned by rds_find_bound() belongs to a different network namespace than the connection that carried the message. Use the existing rds_conn_net() / sock_net() helpers and net_eq() for the comparison. Fixes: c809195f5523 ("rds: clean up loopback rds_connections on netns deletion") Signed-off-by: Aldo Ariel Panzardo Reviewed-by: Allison Henderson Tested-by: Allison Henderson Signed-off-by: Allison Henderson Link: https://patch.msgid.link/20260708024314.601139-1-achender@kernel.org Signed-off-by: Paolo Abeni --- net/rds/recv.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/rds/recv.c b/net/rds/recv.c index 4b3f9e4a8bfd..cf3884d87931 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -399,6 +399,21 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr, goto out; } + /* + * rds_find_bound() uses a global (netns-agnostic) hash table. + * An RDS connection created in netns A can match a socket bound + * in the init netns, delivering inc cross-netns with inc->i_conn + * pointing into netns A. When cleanup_net() then frees that conn, + * any subsequent dereference of inc->i_conn is a use-after-free. + * Drop the inc if the receiving socket lives in a different netns. + */ + if (!net_eq(sock_net(rds_rs_to_sk(rs)), rds_conn_net(conn))) { + rds_stats_inc(s_recv_drop_no_sock); + rds_sock_put(rs); + rs = NULL; + goto out; + } + /* Process extension headers */ rds_recv_incoming_exthdrs(inc, rs); -- cgit v1.2.3 From 96e37e2f618e931aa97af95e707dcdfb1ec41264 Mon Sep 17 00:00:00 2001 From: Zhixing Chen Date: Wed, 8 Jul 2026 12:22:44 +0800 Subject: gtp: parse extension headers before reading inner protocol GTPv1-U packets may carry a chain of extension headers before the inner IP packet. The receive path already parses and skips these extension headers, but it currently reads the inner protocol before doing so. As a result, the first extension header byte is interpreted as the inner IP version. Packets with extension headers are then dropped before PDP lookup. Parse the extension header chain before calling gtp_inner_proto(), so the inner protocol is read from the actual inner IP header. Fixes: c75fc0b9e5be ("gtp: identify tunnel via GTP device + GTP version + TEID + family") Signed-off-by: Zhixing Chen Link: https://patch.msgid.link/20260708042244.120898-1-running910@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/gtp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index a60ef32b35b8..c0e38878af51 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -826,13 +826,17 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) if (!pskb_may_pull(skb, hdrlen)) return -1; + gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); + + if (gtp1->flags & GTP1_F_EXTHDR && + gtp_parse_exthdrs(skb, &hdrlen) < 0) + return -1; + if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) { netdev_dbg(gtp->dev, "GTP packet does not encapsulate an IP packet\n"); return -1; } - gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); - pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid), gtp_proto_to_family(inner_proto)); if (!pctx) { @@ -840,10 +844,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) return 1; } - if (gtp1->flags & GTP1_F_EXTHDR && - gtp_parse_exthdrs(skb, &hdrlen) < 0) - return -1; - return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto); } -- cgit v1.2.3 From 5ff172f6c94d282d83cb88bdfec5f647ad9c6105 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Tue, 21 Jul 2026 11:19:21 +0200 Subject: slab: silence sparse warning with type-based partitioning Sparse does not know __builtin_infer_alloc_token() and complains: sparse: sparse: undefined identifier '__builtin_infer_alloc_token' Fix it by using a dummy variant of __kmalloc_token() if __CHECKER__ is defined. Fixes: feb662d9168b ("slab: support for compiler-assisted type-based slab cache partitioning") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607110912.nZTqfCrH-lkp@intel.com/ Signed-off-by: Marco Elver Link: https://patch.msgid.link/20260721092005.1986693-1-elver@google.com Acked-by: Harry Yoo (Oracle) Signed-off-by: Vlastimil Babka (SUSE) --- include/linux/slab.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 3a14df50766a..32c9f8ed7ae2 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -514,8 +514,12 @@ typedef struct { unsigned long v; } kmalloc_token_t; extern unsigned long random_kmalloc_seed; #define __kmalloc_token(...) ((kmalloc_token_t){ .v = _CODE_LOCATION_ }) #elif defined(CONFIG_KMALLOC_PARTITION_TYPED) +#ifdef __CHECKER__ +#define __kmalloc_token(...) ((kmalloc_token_t){ .v = 0 }) +#else /* !__CHECKER__ */ #define __kmalloc_token(...) ((kmalloc_token_t){ .v = __builtin_infer_alloc_token(__VA_ARGS__) }) -#endif +#endif /* __CHECKER__ */ +#endif /* CONFIG_KMALLOC_PARTITION_TYPED */ #define DECL_TOKEN_PARAM(_token) , kmalloc_token_t (_token) #define _PASS_TOKEN_PARAM(_token) , (_token) #define PASS_TOKEN_PARAM(_token) (_token) -- cgit v1.2.3 From 8bed376124ab4505b70083a2b91f2c7ef6d51e24 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 10 Jul 2026 14:22:57 +0900 Subject: ntfs: harden runlist realloc size calculations Add a shared helper to safely convert runlist element counts to byte sizes using overflow checks, and use it in both ntfs_rl_realloc() and ntfs_rl_realloc_nofail(). Fixes: 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator") Co-developed-by: Alper Mudar Signed-off-by: Alper Mudar Tested-by: Alper Mudar Signed-off-by: Namjae Jeon --- fs/ntfs/runlist.c | 50 +++++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index cbb6576cf725..8e0fd400e7f7 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -71,29 +71,46 @@ static inline void ntfs_rl_mc(struct runlist_element *dstbase, int dst, * On success, return a pointer to the newly allocated, or recycled, memory. * On error, return -errno. */ -struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, - int old_size, int new_size) +static inline struct runlist_element *ntfs_rl_realloc_gfp(struct runlist_element *rl, + int old_size, int new_size, gfp_t gfp) { struct runlist_element *new_rl; + size_t new_bytes; + + if (old_size < 0 || new_size < 0) + return ERR_PTR(-EINVAL); - old_size = old_size * sizeof(*rl); - new_size = new_size * sizeof(*rl); if (old_size == new_size) return rl; - new_rl = kvzalloc(new_size, GFP_NOFS); + if (check_mul_overflow(new_size, sizeof(*rl), &new_bytes)) + return ERR_PTR(-EINVAL); + + new_rl = kvzalloc(new_bytes, gfp); if (unlikely(!new_rl)) return ERR_PTR(-ENOMEM); if (likely(rl != NULL)) { - if (unlikely(old_size > new_size)) - old_size = new_size; - memcpy(new_rl, rl, old_size); + size_t old_bytes; + + if (check_mul_overflow(old_size, sizeof(*rl), &old_bytes)) { + kvfree(new_rl); + return ERR_PTR(-EINVAL); + } + if (unlikely(old_bytes > new_bytes)) + old_bytes = new_bytes; + memcpy(new_rl, rl, old_bytes); kvfree(rl); } return new_rl; } +struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, + int old_size, int new_size) +{ + return ntfs_rl_realloc_gfp(rl, old_size, new_size, GFP_NOFS); +} + /* * ntfs_rl_realloc_nofail - Reallocate memory for runlists * @rl: original runlist @@ -118,21 +135,8 @@ struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, static inline struct runlist_element *ntfs_rl_realloc_nofail(struct runlist_element *rl, int old_size, int new_size) { - struct runlist_element *new_rl; - - old_size = old_size * sizeof(*rl); - new_size = new_size * sizeof(*rl); - if (old_size == new_size) - return rl; - - new_rl = kvmalloc(new_size, GFP_NOFS | __GFP_NOFAIL); - if (likely(rl != NULL)) { - if (unlikely(old_size > new_size)) - old_size = new_size; - memcpy(new_rl, rl, old_size); - kvfree(rl); - } - return new_rl; + return ntfs_rl_realloc_gfp(rl, old_size, new_size, + GFP_NOFS | __GFP_NOFAIL); } /* -- cgit v1.2.3 From 4e646ecd44759e552b0b9ccd995f3f608daab414 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 13 Jul 2026 16:49:57 +0900 Subject: ntfs: drop stale page-cache when shrinking a non-resident attr ntfs_non_resident_attr_shrink() shrinks attribute sizes but fails to trim the page cache. This leaves orphaned dirty folios beyond the new end of the attribute, leading to writeback failures (-ENOENT), data loss, and $EA chain corruption. Fix this by truncating the page cache to the new size immediately after updating the sizes, preventing writeback from flushing out-of-range folios. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 239b7bcbaedf..58f32aac5f61 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -4293,6 +4293,16 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz ni->initialized_size = newsize; ctx->attr->data.non_resident.initialized_size = cpu_to_le64(newsize); } + + /* + * Drop any page-cache folios that now lie beyond the shrunk + * attribute. The clusters backing them have just been freed and the + * runlist truncated, so leaving stale dirty folios around makes a + * later writeback map a vcn past the new allocation, which fails with + * -ENOENT and loses the write. + */ + truncate_inode_pages(VFS_I(ni)->i_mapping, newsize); + /* Update data size in the index. */ if (ni->type == AT_DATA && ni->name == AT_UNNAMED) NInoSetFileNameDirty(ni); -- cgit v1.2.3 From 745fb794c3e933c023af9dbb5876a5e16ad2dc71 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Wed, 8 Jul 2026 17:35:34 +0800 Subject: rxrpc: fix io_thread race in rxrpc_wake_up_io_thread() rxrpc_wake_up_io_thread() checks local->io_thread before waking it, but then reloads the pointer for wake_up_process(). local->io_thread is cleared with WRITE_ONCE() when the I/O thread exits, so the second load can see NULL even if the first load did not. Take a READ_ONCE() snapshot and use it for both the NULL check and the wake_up_process() call, as rxrpc_encap_rcv() already does. Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE") Signed-off-by: Xuanqiang Luo Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260708093534.53486-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni --- net/rxrpc/ar-internal.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ce946b0a03e2..865f05fe37ab 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1285,9 +1285,11 @@ int rxrpc_io_thread(void *data); void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb); static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local) { - if (!local->io_thread) + struct task_struct *io_thread = READ_ONCE(local->io_thread); + + if (!io_thread) return; - wake_up_process(READ_ONCE(local->io_thread)); + wake_up_process(io_thread); } static inline bool rxrpc_protocol_error(struct sk_buff *skb, enum rxrpc_abort_reason why) -- cgit v1.2.3 From 2a12c05aef213ff304ecc9e2f351de20731946b8 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 8 Jul 2026 12:29:03 +0200 Subject: vsock/virtio: collapse receive queue under memory pressure When many small packets accumulate in the receive queue, the skb overhead can exceed buf_alloc even while the payload is within bounds. This causes virtio_transport_inc_rx_pkt() to reject packets, leading to connection resets during large transfers under backpressure. The issue was reported by Brien, who has a reproducer, but it is also easily reproducible with iperf-vsock [1] using a small packet size: iperf3 --vsock -c $CID -l 129 which fails immediately without this patch but with commit 059b7dbd20a6 ("vsock/virtio: fix potential unbounded skb queue"). Inspired by TCP's tcp_collapse() which solves a similar problem, add virtio_transport_collapse_rx_queue() that walks the receive queue and re-copies data into compact linear skbs to reduce the overhead. The collapse is triggered proactively from when the number of skb queued is close to exceeding the overhead budget. A pre-scan counts the eligible bytes to size each allocation precisely, avoiding waste for isolated small packets. Partially consumed skbs are kept as-is to preserve buf_used/fwd_cnt accounting, EOM-marked skbs to maintain SEQPACKET message boundaries, and skbs already larger than the collapse target because they already have a good data-to-overhead ratio. Walking a large queue may take a significant amount of time and cache misses, causing traffic burstiness. To limit this, the collapse stops once enough room is freed for this packet and the next one, but may opportunistically free more to fill each collapsed skb to capacity. [1] https://github.com/stefano-garzarella/iperf-vsock Fixes: 059b7dbd20a6 ("vsock/virtio: fix potential unbounded skb queue") Cc: stable@vger.kernel.org Reported-by: Brien Oberstein Closes: https://lore.kernel.org/netdev/618701dd023e$063de350$12b9a9f0$@gmail.com/ Tested-by: Brien Oberstein Signed-off-by: Stefano Garzarella Acked-by: Michael S. Tsirkin Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/20260708102904.50732-2-sgarzare@redhat.com Signed-off-by: Paolo Abeni --- net/vmw_vsock/virtio_transport_common.c | 165 +++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 09475007165b..8becad81279c 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -26,6 +26,13 @@ /* Threshold for detecting small packets to copy */ #define GOOD_COPY_LEN 128 +/* Max payload that can be collapsed into a single linear skb, using the same + * allocation threshold as virtio_vsock_alloc_skb() to avoid adding pressure + * on the page allocator. + */ +#define MAX_COLLAPSE_LEN \ + SKB_MAX_ORDER(VIRTIO_VSOCK_SKB_HEADROOM, PAGE_ALLOC_COSTLY_ORDER) + static void virtio_transport_cancel_close_work(struct vsock_sock *vsk, bool cancel_timeout); static s64 virtio_transport_has_space(struct virtio_vsock_sock *vvs); @@ -420,6 +427,145 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, return ret; } +static bool virtio_transport_can_collapse(struct sk_buff *skb) +{ + /* skbs that are partially consumed, mark a SEQPACKET message boundary, + * or are already large enough should not be collapsed: they either + * need special accounting, carry protocol state, or already have a + * good data-to-overhead ratio. + */ + if (VIRTIO_VSOCK_SKB_CB(skb)->offset) + return false; + if (le32_to_cpu(virtio_vsock_hdr(skb)->flags) & VIRTIO_VSOCK_SEQ_EOM) + return false; + if (skb->len >= MAX_COLLAPSE_LEN) + return false; + return true; +} + +/* Iterate through the packets in the queue starting from the current skb to + * count the number of bytes we can collapse. + */ +static unsigned int +virtio_transport_collapse_size(struct sk_buff *skb, struct sk_buff_head *queue) +{ + unsigned int target = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset; + + while ((skb = skb_peek_next(skb, queue)) && + virtio_transport_can_collapse(skb)) { + unsigned int len = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset; + + if (len > MAX_COLLAPSE_LEN - target) + return target; + + target += len; + } + + return target; +} + +/* Called under lock_sock to compact the receive queue by merging small skbs. + * @min_to_free: minimum number of skbs to eliminate from the queue. May free + * more to fill each collapsed skb to capacity. + */ +static void +virtio_transport_collapse_rx_queue(struct virtio_vsock_sock *vvs, + u32 min_to_free) +{ + struct sk_buff *skb, *next_skb, *new_skb = NULL; + struct sk_buff_head new_queue; + u32 saved = 0; + + __skb_queue_head_init(&new_queue); + + skb_queue_walk_safe(&vvs->rx_queue, skb, next_skb) { + struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb); + u32 src_off = VIRTIO_VSOCK_SKB_CB(skb)->offset; + u32 src_len = skb->len - src_off; + bool keep; + + keep = !virtio_transport_can_collapse(skb); + if (keep) { + /* Finalize pending collapsed skb to preserve packet + * ordering. + */ + if (new_skb) { + __skb_queue_tail(&new_queue, new_skb); + new_skb = NULL; + saved--; + } + goto next; + } + + /* Finalize if this packet won't fit in the remaining tailroom, + * so we can allocate a right-sized new_skb. + */ + if (new_skb && src_len > skb_tailroom(new_skb)) { + __skb_queue_tail(&new_queue, new_skb); + new_skb = NULL; + saved--; + } + + if (!new_skb) { + unsigned int alloc_size; + + /* Check after finalizing to opportunistically fill + * each collapsed skb to capacity, merging more skbs + * than strictly required. + */ + if (saved >= min_to_free) + break; + + alloc_size = virtio_transport_collapse_size(skb, &vvs->rx_queue); + + /* Only this skb's data is eligible, nothing to merge + * with. Keep as-is. + */ + if (alloc_size <= src_len) { + keep = true; + goto next; + } + + new_skb = virtio_vsock_alloc_linear_skb(alloc_size + + VIRTIO_VSOCK_SKB_HEADROOM, GFP_KERNEL); + if (!new_skb) + break; + + memcpy(virtio_vsock_hdr(new_skb), hdr, + sizeof(struct virtio_vsock_hdr)); + virtio_vsock_hdr(new_skb)->len = 0; + } + + /* Cannot fail since src_off/src_len are within bounds, but if + * it does, discard new_skb to avoid queuing corrupted data. + */ + if (WARN_ON_ONCE(skb_copy_bits(skb, src_off, + skb_put(new_skb, src_len), + src_len))) { + kfree_skb(new_skb); + new_skb = NULL; + break; + } + + le32_add_cpu(&virtio_vsock_hdr(new_skb)->len, src_len); + virtio_vsock_hdr(new_skb)->flags |= hdr->flags; + +next: + __skb_unlink(skb, &vvs->rx_queue); + if (keep) { + __skb_queue_tail(&new_queue, skb); + } else { + consume_skb(skb); + saved++; + } + } + + if (new_skb) + __skb_queue_tail(&new_queue, new_skb); + + skb_queue_splice(&new_queue, &vvs->rx_queue); +} + static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs, u32 len) { @@ -1354,12 +1500,29 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk, { struct virtio_vsock_sock *vvs = vsk->trans; bool can_enqueue, free_pkt = false; + u32 len, queue_max, queue_len; struct virtio_vsock_hdr *hdr; - u32 len; hdr = virtio_vsock_hdr(skb); len = le32_to_cpu(hdr->len); + /* virtio_transport_inc_rx_pkt() rejects packets when the per-skb + * overhead (skb_queue_len * SKB_TRUESIZE(0)) exceeds buf_alloc. + * Proactively collapse the queue before that happens. + * No rx_lock needed: lock_sock is held by caller, preventing + * concurrent enqueue or dequeue. + */ + queue_max = vvs->buf_alloc / SKB_TRUESIZE(0); + queue_len = skb_queue_len(&vvs->rx_queue); + if (queue_len >= queue_max) { + /* Walking a large queue may take a significant amount of time + * and cache misses, causing traffic burstiness. Limit the + * collapse to freeing room for this packet and the next one. + * It may free more to fill each collapsed skb to capacity. + */ + virtio_transport_collapse_rx_queue(vvs, queue_len + 2 - queue_max); + } + spin_lock_bh(&vvs->rx_lock); can_enqueue = virtio_transport_inc_rx_pkt(vvs, len); -- cgit v1.2.3 From 30c82aa0a8b116989bc4d8f75e1936bd83c0134e Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 8 Jul 2026 12:29:04 +0200 Subject: vsock/test: add test for small packets under pressure Add a test that sends 2 MB of data using randomly sized small packets (129-512 bytes) over a SOCK_STREAM connection. Packets above GOOD_COPY_LEN (128) bypass the in-place coalescing in recv_enqueue(), forcing each one into its own skb. Without receive queue collapsing, the per-skb overhead eventually exceeds buf_alloc and the connection is reset. The test verifies that all data arrives and that content integrity is preserved. Signed-off-by: Stefano Garzarella Acked-by: Michael S. Tsirkin Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/20260708102904.50732-3-sgarzare@redhat.com Signed-off-by: Paolo Abeni --- tools/testing/vsock/vsock_test.c | 87 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 76be0e4a7f0e..b4ff9f946565 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -2347,6 +2347,88 @@ static void test_stream_tx_credit_bounds_server(const struct test_opts *opts) close(fd); } +/* Test that many small packets don't cause a connection reset under pressure + * and that data integrity is preserved. Packet sizes vary randomly between + * 129 and 512 bytes, above GOOD_COPY_LEN (128) to bypass in-place coalescing + * in recv_enqueue, forcing each one into its own skb. Without receive queue + * collapsing, the per-skb overhead eventually exceeds buf_alloc and the + * connection is reset. + */ +#define COLLAPSE_PKT_MIN 129 +#define COLLAPSE_PKT_MAX 512 +#define COLLAPSE_TOTAL (2 * 1024 * 1024) + +static void test_stream_collapse_client(const struct test_opts *opts) +{ + unsigned char *data; + unsigned long hash; + size_t offset = 0; + int i, fd; + + data = malloc(COLLAPSE_TOTAL); + if (!data) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < COLLAPSE_TOTAL; i++) + data[i] = rand() & 0xff; + + fd = vsock_stream_connect(opts->peer_cid, opts->peer_port); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + while (offset < COLLAPSE_TOTAL) { + size_t pkt_size = COLLAPSE_PKT_MIN + + rand() % (COLLAPSE_PKT_MAX - COLLAPSE_PKT_MIN + 1); + + pkt_size = min(pkt_size, COLLAPSE_TOTAL - offset); + + send_buf(fd, data + offset, pkt_size, 0, pkt_size); + offset += pkt_size; + } + + hash = hash_djb2(data, COLLAPSE_TOTAL); + control_writeulong(hash); + + free(data); + close(fd); +} + +static void test_stream_collapse_server(const struct test_opts *opts) +{ + unsigned long hash, remote_hash; + unsigned char *data; + int fd; + + data = malloc(COLLAPSE_TOTAL); + if (!data) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + recv_buf(fd, data, COLLAPSE_TOTAL, 0, COLLAPSE_TOTAL); + + hash = hash_djb2(data, COLLAPSE_TOTAL); + remote_hash = control_readulong(); + if (hash != remote_hash) { + fprintf(stderr, "hash mismatch: local %lu remote %lu\n", + hash, remote_hash); + exit(EXIT_FAILURE); + } + + free(data); + close(fd); +} + static struct test_case test_cases[] = { { .name = "SOCK_STREAM connection reset", @@ -2546,6 +2628,11 @@ static struct test_case test_cases[] = { .run_client = test_stream_msg_peek_client, .run_server = test_stream_peek_after_recv_server, }, + { + .name = "SOCK_STREAM small packets backpressure", + .run_client = test_stream_collapse_client, + .run_server = test_stream_collapse_server, + }, {}, }; -- cgit v1.2.3 From 4c1eabbef7a1707635652e956e39db1269c3af2b Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:10:25 +0800 Subject: dpaa2-switch: put MAC endpoint device on disconnect fsl_mc_get_endpoint() returns the MAC endpoint device with a reference taken through device_find_child(). The switch port connect path stores that device in mac->mc_dev and keeps it for the lifetime of the connected MAC object. However, the disconnect path only closes the MAC and frees the dpaa2_mac object. It does not drop the endpoint device reference stored in mac->mc_dev, so every successful connect leaks that device reference when the MAC is later disconnected. Drop the endpoint device reference before freeing the dpaa2_mac object. Fixes: 84cba72956fd ("dpaa2-switch: integrate the MAC endpoint support") Signed-off-by: Guangshuo Li Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260708111025.749311-1-lgs201920130244@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 858ba844ac51..dd4f60031d0c 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1560,6 +1560,7 @@ static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv) dpaa2_mac_disconnect(mac); dpaa2_mac_close(mac); + put_device(&mac->mc_dev->dev); kfree(mac); } -- cgit v1.2.3 From 2484568a335cd7bda951c75b3a7d95ea36161ae7 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 8 Jul 2026 19:16:16 +0800 Subject: net: airoha: Fix potential use-after-free in airoha_ppe_deinit() airoha_ppe_deinit() replaces the NPU pointer with NULL via rcu_replace_pointer() but does not wait for existing RCU readers to exit before calling ppe_deinit() and airoha_npu_put(). This can cause a use-after-free if a reader in an RCU read-side critical section still holds a reference to the NPU when it is freed. The init path (airoha_ppe_init) already calls synchronize_rcu() after rcu_assign_pointer(), but the deinit path introduced in commit 6abcf751bc08 ("net: airoha: Fix schedule while atomic in airoha_ppe_deinit()") omitted the matching barrier when switching from rcu_read_lock()/rcu_dereference() to rcu_replace_pointer(). Add synchronize_rcu() before ppe_deinit() to ensure all existing RCU readers have completed before the NPU resources are released. Fixes: 6abcf751bc084804a9e5b3051442e8a2ce67f48a ("net: airoha: Fix schedule while atomic in airoha_ppe_deinit()") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178351022574.97989.6880403520276841703@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/airoha/airoha_ppe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index e7c78293002a..f6396925722d 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -1659,6 +1659,7 @@ void airoha_ppe_deinit(struct airoha_eth *eth) npu = rcu_replace_pointer(eth->npu, NULL, lockdep_is_held(&flow_offload_mutex)); if (npu) { + synchronize_rcu(); npu->ops.ppe_deinit(npu); airoha_npu_put(npu); } -- cgit v1.2.3 From 8a570b19b4b16a8a3b5ffa2b332bd5613110b2d8 Mon Sep 17 00:00:00 2001 From: D Scott Phillips Date: Tue, 14 Jul 2026 16:11:58 -0700 Subject: KVM: arm64: vgic: Avoid double-deactivate of IRQs in the nested context In the nested state, the physical interrupt has already been deactivated through the HW bit in the LR. The extra deactivation would be harmless but can hit an errata case on AmpereOne, so avoid it here. On AmpereOne, deactivating a physical interrupt through ICC_DIR_EL1 or ICC_EOIR1_EL1 (depending on EOImode) which is not active, but is the highest priority pending interrupt causes the cpu to lose the interrupt pending state and also prevents the delivery of future interrupts. Fixes: 6dd333c8942b2 ("KVM: arm64: GICv3: nv: Plug L1 LR sync into deactivation primitive") Signed-off-by: D Scott Phillips Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-arm-kernel/20260710222128.416581-1-scott@os.amperecomputing.com/ Link: https://patch.msgid.link/20260714231158.496808-1-scott@os.amperecomputing.com Signed-off-by: Marc Zyngier --- Documentation/arch/arm64/silicon-errata.rst | 4 ++++ arch/arm64/kvm/vgic/vgic-v3.c | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index 014aa1c215a1..88b4aa45a206 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -55,10 +55,14 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | Ampere | AmpereOne | AC03_CPU_38 | AMPERE_ERRATUM_AC03_CPU_38 | +----------------+-----------------+-----------------+-----------------------------+ +| Ampere | AmpereOne | AC03_CPU_57 | N/A | ++----------------+-----------------+-----------------+-----------------------------+ | Ampere | AmpereOne AC04 | AC04_CPU_10 | AMPERE_ERRATUM_AC03_CPU_38 | +----------------+-----------------+-----------------+-----------------------------+ | Ampere | AmpereOne AC04 | AC04_CPU_23 | AMPERE_ERRATUM_AC04_CPU_23 | +----------------+-----------------+-----------------+-----------------------------+ +| Ampere | AmpereOne AC04 | AC04_CPU_29 | N/A | ++----------------+-----------------+-----------------+-----------------------------+ +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A510 | #2457168 | ARM64_ERRATUM_2457168 | +----------------+-----------------+-----------------+-----------------------------+ diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index 9e841e7afd4a..7aa417440f6a 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -275,7 +275,13 @@ void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val) lr = vgic_v3_compute_lr(vcpu, irq) & ~ICH_LR_ACTIVE_BIT; } - if (lr & ICH_LR_HW) + /* + * In the nested state, the irq has already been deactivated via the HW + * bit in the LR. Deactivating again would be harmless except AmpereOne + * errata AC03_CPU_57, AC04_CPU_29 could cause irq delivery to break if + * the deactivation hits the highest priority pending irq. + */ + if ((lr & ICH_LR_HW) && !vgic_state_is_nested(vcpu)) vgic_v3_deactivate_phys(FIELD_GET(ICH_LR_PHYS_ID_MASK, lr)); vgic_v3_fold_lr(vcpu, lr); -- cgit v1.2.3 From 5aab4d46278df6e9e94bd0a92df9a135a5fe16ad Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Sun, 19 Jul 2026 17:32:57 +0100 Subject: KVM: arm64: Update Fuad Tabba's email address Switch my KVM/arm64 reviewer entry to fuad.tabba@linux.dev, my canonical address for kernel work, and add a .mailmap entry so the existing tabba@google.com commits map onto it. Signed-off-by: Fuad Tabba Link: https://patch.msgid.link/20260719163257.3156529-1-fuad.tabba@linux.dev Signed-off-by: Marc Zyngier --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index 735470e21075..91003fb86136 100644 --- a/.mailmap +++ b/.mailmap @@ -291,6 +291,7 @@ Frank Rowand Frank Rowand Frank Rowand Frank Zago +Fuad Tabba Gao Xiang Gao Xiang Gao Xiang diff --git a/MAINTAINERS b/MAINTAINERS index a674e36529f7..5ab6fc150426 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14192,7 +14192,7 @@ F: virt/kvm/* KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64) M: Marc Zyngier M: Oliver Upton -R: Fuad Tabba +R: Fuad Tabba R: Joey Gouly R: Steffen Eiden R: Suzuki K Poulose -- cgit v1.2.3 From b4b201cc93ff70150853aba03e14d314d1980ca0 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 19:17:37 +0800 Subject: dpaa2-eth: put MAC endpoint device on disconnect fsl_mc_get_endpoint() returns the MAC endpoint device with a reference taken through device_find_child(). The Ethernet connect path stores that device in mac->mc_dev and keeps it for the lifetime of the connected MAC object. However, the disconnect path only disconnects and closes the MAC before freeing the dpaa2_mac object. It does not drop the endpoint device reference stored in mac->mc_dev, so every successful connect leaks that device reference when the MAC is later disconnected. Drop the endpoint device reference after closing the MAC and before freeing the dpaa2_mac object. Fixes: 719479230893 ("dpaa2-eth: add MAC/PHY support through phylink") Signed-off-by: Guangshuo Li Reviewed-by: Ioana Ciornei Reviewed-by: Ioana Ciornei Link: https://patch.msgid.link/20260708111738.750391-1-lgs201920130244@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 9335703768a9..764d2a09668f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -4732,6 +4732,7 @@ static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) dpaa2_mac_disconnect(mac); dpaa2_mac_close(mac); + put_device(&mac->mc_dev->dev); kfree(mac); } -- cgit v1.2.3 From 6f884eb87a79e0c482baef2ad96c96b81d024235 Mon Sep 17 00:00:00 2001 From: Wayen Yan Date: Wed, 8 Jul 2026 19:35:29 +0800 Subject: net: airoha: Fix DMA direction for NPU mailbox buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit airoha_npu_send_msg() always maps the mailbox buffer with DMA_TO_DEVICE, but some callers expect the NPU to write response data back into the same buffer: - airoha_npu_wlan_msg_get() (NPU_OP_GET): NPU writes response into the buffer, then the caller reads it via memcpy() - airoha_npu_ppe_stats_setup() (NPU_OP_SET): NPU writes back npu_stats_addr field in the response On non-cache-coherent architectures like EN7581 (Cortex-A53 without hardware cache coherency for NPU DMA), DMA_TO_DEVICE unmap is a no-op — it does not invalidate the CPU cache. If the NPU-written cache line is still present in the CPU cache when the caller reads the buffer, the CPU observes stale data instead of the NPU response. This is a timing-sensitive bug: small mailbox buffers (~24 bytes) typically fit in a single cache line and may survive in the cache until the caller reads them, producing silent data corruption rather than a crash. The bug is more likely to trigger when the caller reads the response immediately after dma_unmap_single() without intervening cache-evicting operations. Fix by using DMA_BIDIRECTIONAL for both map and unmap, which ensures dma_unmap_single() invalidates the CPU cache on non-coherent systems. The mailbox buffers are small so there is no performance concern. Fixes: c52918744ee1e49cea86622a2633b9782446428f ("net: airoha: npu: Move memory allocation in airoha_npu_send_msg() caller") Signed-off-by: Wayen Yan Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/178351055214.98729.11403147818632027428@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/airoha/airoha_npu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c index 870d61fdd9c6..b679bed952de 100644 --- a/drivers/net/ethernet/airoha/airoha_npu.c +++ b/drivers/net/ethernet/airoha/airoha_npu.c @@ -168,7 +168,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, dma_addr_t dma_addr; int ret; - dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE); + dma_addr = dma_map_single(npu->dev, p, size, DMA_BIDIRECTIONAL); ret = dma_mapping_error(npu->dev, dma_addr); if (ret) return ret; @@ -191,7 +191,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, spin_unlock_bh(&npu->cores[core].lock); - dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE); + dma_unmap_single(npu->dev, dma_addr, size, DMA_BIDIRECTIONAL); return ret; } -- cgit v1.2.3 From 622ebfac01ba4f9c0060cebd41257fe46fc4a0b3 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 17 Jul 2026 12:30:11 +0200 Subject: KVM: nVMX: Hide shadow VMCS right after VMCLEAR free_nested() frees the shadow VMCS while vmcs01 still points to it. But because it is asynchronous with respect to loaded_vmcs_clear(), the vCPU might migrate before the pointer is cleared and __loaded_vmcs_clear() may then execute VMCLEAR. The VMCS needs to stay attached until its explicit VMCLEAR completes, but then it can be hidden and the page safely freed. Fixes: 355f4fb1405e ("kvm: nVMX: VMCLEAR an active shadow VMCS after last use") Cc: stable@vger.kernel.org Signed-off-by: Hyunwoo Kim Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/nested.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 220d42ebc82e..ddf6df7bee93 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -336,6 +336,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu) static void free_nested(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); + struct vmcs *shadow_vmcs; if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01)) vmx_switch_vmcs(vcpu, &vmx->vmcs01); @@ -353,9 +354,15 @@ static void free_nested(struct kvm_vcpu *vcpu) vmx->nested.current_vmptr = INVALID_GPA; if (enable_shadow_vmcs) { vmx_disable_shadow_vmcs(vmx); - vmcs_clear(vmx->vmcs01.shadow_vmcs); - free_vmcs(vmx->vmcs01.shadow_vmcs); + + /* + * Keep the pointer visible until after VMCLEAR, so migration + * can clear an active shadow VMCS on the old CPU. + */ + shadow_vmcs = vmx->vmcs01.shadow_vmcs; + vmcs_clear(shadow_vmcs); vmx->vmcs01.shadow_vmcs = NULL; + free_vmcs(shadow_vmcs); } kfree(vmx->nested.cached_vmcs12); vmx->nested.cached_vmcs12 = NULL; -- cgit v1.2.3 From 2abd5287f08319fa35764566b15c6e22cb1068db Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 13 Jul 2026 08:15:33 -0700 Subject: KVM: x86: Check for invalid/obsolete root *after* making MMU pages available Check for a "stale" page fault, i.e. for an invalid and/or obsolete root, after making MMU pages available for the shadow MMU. If reclaiming shadow pages zaps an in-use root, i.e. marks it invalid, then KVM will attempt to map memory into an invalid root. On its own, populating an invalid root is "fine", but because child shadow pages inherit their parent's role, any children created during the map/fetch will be created as invalid pages, thus violating KVM's invariant that invalid pages are never on the list of active MMU pages. Note, the underlying flaw has existed since KVM first started tracking invalid roots in 2008 (commit 2e53d63acba7, "KVM: MMU: ignore zapped root pagetables"), but the true badness only came along in 2020 (Linux 5.9) with the invariant that invalid shadow pages can't be on the list of active pages. Note #2, inheriting role.invalid when creating child shadow pages is also far from ideal; that flaw will be addressed separately. Reported-by: Hyunwoo Kim Fixes: f95eec9bed76 ("KVM: x86/mmu: Don't put invalid SPs back on the list of active pages") Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 9 +++++---- arch/x86/kvm/mmu/paging_tmpl.h | 10 ++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 234d0a95abf5..41f92ed1ca37 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4852,16 +4852,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (r != RET_PF_CONTINUE) return r; - r = RET_PF_RETRY; write_lock(&vcpu->kvm->mmu_lock); - if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; - r = make_mmu_pages_available(vcpu); if (r) goto out_unlock; + if (is_page_fault_stale(vcpu, fault)) { + r = RET_PF_RETRY; + goto out_unlock; + } + r = direct_map(vcpu, fault); out_unlock: diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index df3ae0c7ec2c..1ba840a73b7a 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -864,15 +864,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault } #endif - r = RET_PF_RETRY; write_lock(&vcpu->kvm->mmu_lock); - if (is_page_fault_stale(vcpu, fault)) - goto out_unlock; - r = make_mmu_pages_available(vcpu); if (r) goto out_unlock; + + if (is_page_fault_stale(vcpu, fault)) { + r = RET_PF_RETRY; + goto out_unlock; + } + r = FNAME(fetch)(vcpu, fault, &walker); out_unlock: -- cgit v1.2.3 From 7a2c70e777a00c32ebafd376a6fe31ebb91c5b20 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Sun, 12 Jul 2026 10:14:50 +0900 Subject: KVM: x86/mmu: Preserve nested TDP shadow page tables if they are used as roots kvm_mmu_zap_oldest_mmu_pages() excludes a shadow page whose root_count is non-zero from top-level reclaim, because such a page cannot be freed. The path in mmu_page_zap_pte() that recursively zaps a parentless nested TDP child has no such check. As a result, a shadow page can be zapped even if the page itself can't be freed; as the comment in kvm_mmu_zap_oldest_mmu_pages() notes, zapping it will just force vCPUs to rebuild the page. As in top-level reclaim, do not recursively prepare zapping of a nested TDP child whose root_count is non-zero. Fixes: 2de4085cccea ("KVM: x86/MMU: Recursively zap nested TDP SPs when zapping last/only parent") Signed-off-by: Hyunwoo Kim Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 41f92ed1ca37..7e80abba7313 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2642,6 +2642,7 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp, */ if (tdp_enabled && invalid_list && child->role.guest_mode && + !child->root_count && !atomic_long_read(&child->parent_ptes.val)) return kvm_mmu_prepare_zap_page(kvm, child, invalid_list); -- cgit v1.2.3 From 52f2f7c30126037975389aa04d24c506a5177c35 Mon Sep 17 00:00:00 2001 From: Phil Rosenthal Date: Sat, 18 Jul 2026 12:50:23 -0400 Subject: KVM: x86/mmu: Fix use-after-free on vendor module reload mmu_destroy_caches() destroys pte_list_desc_cache and mmu_page_header_cache, but leaves both pointers unchanged. The pointers live in kvm.ko, and therefore survive when a vendor module is unloaded while kvm.ko remains loaded. If creation of pte_list_desc_cache fails during a subsequent vendor module load, its assignment sets pte_list_desc_cache to NULL and the error path calls mmu_destroy_caches(). mmu_page_header_cache still points to the cache destroyed during the preceding vendor module unload. Passing that stale pointer to kmem_cache_destroy() causes a slab use-after-free. Reproduce the issue on a v7.1.3 kernel with CONFIG_KASAN=y, CONFIG_KASAN_GENERIC=y, CONFIG_KVM=m, and CONFIG_KVM_INTEL=m. A one-shot test hook forces pte_list_desc_cache to NULL on the second invocation of kvm_mmu_vendor_module_init(): 1. Load kvm.ko and kvm-intel.ko, creating both caches. 2. Unload only kvm_intel, leaving kvm.ko loaded. 3. Reload kvm_intel and force initialization through the -ENOMEM path. KASAN reports: BUG: KASAN: slab-use-after-free in kvm_mmu_vendor_module_init+0x5b/0x170 [kvm] ... kmem_cache_destroy+0x21/0x1d0 kvm_mmu_vendor_module_init+0x5b/0x170 [kvm] ... Allocated by task 16817: __kmem_cache_create_args+0x12c/0x3b0 __kmem_cache_create.constprop.0+0xb6/0xf0 [kvm] kvm_mmu_vendor_module_init+0x13b/0x170 [kvm] ... Freed by task 16820: kmem_cache_destroy+0x117/0x1d0 kvm_mmu_vendor_module_exit+0x21/0x30 [kvm] Clear both pointers immediately after destroying their caches so that the stored state reflects the caches' lifetime and repeated cleanup is safe. With the fix applied, the same injected vendor module reload fails with -ENOMEM as expected and produces no KASAN report. Fixes: cb498ea2ce1d ("KVM: Portability: Combine kvm_init and kvm_init_x86") Cc: stable@vger.kernel.org Signed-off-by: Phil Rosenthal Message-ID: <20260718-kvm-mmu-cache-uaf-v3-1-e103b93c74e1@phil.gs> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 7e80abba7313..22cf222d3033 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -7576,7 +7576,9 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) static void mmu_destroy_caches(void) { kmem_cache_destroy(pte_list_desc_cache); + pte_list_desc_cache = NULL; kmem_cache_destroy(mmu_page_header_cache); + mmu_page_header_cache = NULL; } static void kvm_wake_nx_recovery_thread(struct kvm *kvm) -- cgit v1.2.3 From cd76ec58be59b061cc6a835226abff50fa35e3e6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 20 Jul 2026 12:03:02 +0100 Subject: KVM: selftests: sev_smoke_test: Only run VM types the host offers sev_smoke_test ran the plain SEV subtest unconditionally, gated only on the X86_FEATURE_SEV CPUID bit, while gating SEV-ES and SNP on the KVM_CAP_VM_TYPES bits. CPUID reporting SEV does not mean KVM offers the SEV VM type: when all SEV ASIDs are assigned to SEV-SNP, KVM_X86_SEV_VM is unavailable even though X86_FEATURE_SEV is set. On such a host the test aborts in KVM_CREATE_VM instead of exercising the available modes. Gate the SEV subtest on KVM_CAP_VM_TYPES like the others, so the test runs the VM types the host actually offers. Reviewed-by: Tycho Andersen (AMD) Signed-off-by: David Woodhouse Message-ID: <2b5e7a83d277134294199a455469bb436196b902.1784545391.git.dwmw@amazon.co.uk> Signed-off-by: Paolo Bonzini --- tools/testing/selftests/kvm/x86/sev_smoke_test.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c index 6b2cbe2a90b7..bf27b6187afa 100644 --- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c +++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c @@ -247,7 +247,14 @@ int main(int argc, char *argv[]) { TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); - test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0); + /* + * Only exercise VM types the host actually offers. CPUID reporting + * SEV does not guarantee KVM offers the SEV VM type: when all SEV + * ASIDs are assigned to SEV-SNP, KVM_X86_SEV_VM is unavailable even + * though X86_FEATURE_SEV is set. Gate every type on KVM_CAP_VM_TYPES. + */ + if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)) + test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0); if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM)) test_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES); -- cgit v1.2.3 From f148dd411d451e3e4bf79240faec736d101832d4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 20 Jul 2026 12:03:03 +0100 Subject: KVM: selftests: sev_init2_tests: Derive SEV availability from KVM The test asserted that the X86_FEATURE_SEV CPUID bit exactly matches whether KVM offers KVM_X86_SEV_VM. That is not an invariant: when all SEV ASIDs are assigned to SEV-SNP, KVM does not offer the SEV VM type even though CPUID reports SEV, so the test aborts on an SNP-only host. Derive SEV availability from KVM_CAP_VM_TYPES (as already done for SEV-ES and SNP), assert only the one-way implication that a type offered by KVM is also reported in CPUID, and TEST_REQUIRE() the SEV VM type so the test skips cleanly when it is unavailable. Reviewed-by: Tycho Andersen (AMD) Signed-off-by: David Woodhouse Message-ID: <5d3c345113748f39b7982e365d241abaf3e11086.1784545391.git.dwmw@amazon.co.uk> Signed-off-by: Paolo Bonzini --- tools/testing/selftests/kvm/x86/sev_init2_tests.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/kvm/x86/sev_init2_tests.c b/tools/testing/selftests/kvm/x86/sev_init2_tests.c index 8db88c355f16..689390c10f7c 100644 --- a/tools/testing/selftests/kvm/x86/sev_init2_tests.c +++ b/tools/testing/selftests/kvm/x86/sev_init2_tests.c @@ -130,12 +130,18 @@ int main(int argc, char *argv[]) KVM_X86_SEV_VMSA_FEATURES, &supported_vmsa_features); - have_sev = kvm_cpu_has(X86_FEATURE_SEV); - TEST_ASSERT(have_sev == !!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)), - "sev: KVM_CAP_VM_TYPES (%x) does not match cpuid (checking %x)", - kvm_check_cap(KVM_CAP_VM_TYPES), 1 << KVM_X86_SEV_VM); + /* + * Whether a VM type is available depends on KVM, not just CPUID: e.g. + * when all SEV ASIDs are assigned to SEV-SNP, KVM does not offer the + * SEV VM type even though X86_FEATURE_SEV is set. Derive availability + * from KVM_CAP_VM_TYPES and only assert the one-way implication that a + * type offered by KVM must also be reported in CPUID. + */ + have_sev = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM); + TEST_ASSERT(!have_sev || kvm_cpu_has(X86_FEATURE_SEV), + "sev: SEV_VM supported without SEV in CPUID"); - TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)); + TEST_REQUIRE(have_sev); have_sev_es = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM); TEST_ASSERT(!have_sev_es || kvm_cpu_has(X86_FEATURE_SEV_ES), -- cgit v1.2.3 From e800decd9c0ac4349bcd8f8f9b29fd21fe93165e Mon Sep 17 00:00:00 2001 From: Venkatesh Srinivas Date: Wed, 15 Jul 2026 23:42:35 +0000 Subject: KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN On Intel platforms with a VMX preemption timer and APICv, if a VMM calls KVM_GET_LAPIC before KVM_GET_MSRS to save the vCPU state, it is possible to lose a pending timer interrupt. If the thread running these ioctls is migrated to another core after calling KVM_GET_LAPIC but before KVM_GET_MSRS and the guest is using their LAPIC timer in TSC-deadline mode, not only does the save LAPIC state not carry the pending interrupt, the TSCDEADLINE MSR will be zeroed. After migration across CPUs, KVM_GET_MSRS calls vcpu_load, posting the interrupt and clearing the MSR: vcpu_load() -> kvm_arch_vcpu_load() -> kvm_lapic_restart_hv_timer() -> start_hv_timer() -> apic_timer_expired() -> kvm_apic_inject_pending_timer_irqs() . post interrupt into the LAPIC state . clear IA32_TSCDEADLINE The saved LAPIC state will be missing the pending interrupt and the saved MSR will be zero. Oops. Fix by only posting an interrupt when we're attempting to enter the guest (vcpu->wants_to_run == true), not for vcpu_load from other paths. Assisted-by: gemini:gemini-3.1-pro-preview Debugged-by: David Matlack Debugged-by: Sean Christopherson Debugged-by: Jim Mattson Debugged-by: James Houghton Signed-off-by: Venkatesh Srinivas Message-ID: <20260715234234.15382-2-venkateshs@chromium.org> Reviewed-by: James Houghton Reviewed-by: Chao Gao Cc: stable@vger.kernel.org Fixes: ae95f566b3d2 ("KVM: X86: TSCDEADLINE MSR emulation fastpath", 2020-05-15) Signed-off-by: Paolo Bonzini --- arch/x86/kvm/lapic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 38bba9a1114c..48b019114c19 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2052,7 +2052,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn) if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; - if (!from_timer_fn && apic->apicv_active) { + if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) { WARN_ON(kvm_get_running_vcpu() != vcpu); kvm_apic_inject_pending_timer_irqs(apic); return; -- cgit v1.2.3 From 1e75a8255f11c81fb07e81e5029cfd75804350a0 Mon Sep 17 00:00:00 2001 From: Guanghui Feng Date: Thu, 16 Jul 2026 22:16:22 +0800 Subject: iommu/amd: Wait for completion instead of returning early in iommu_completion_wait() need_sync is a per-IOMMU flag shared by all domains and devices behind that IOMMU. It is set whenever a command is queued with sync == true and cleared when a completion-wait (CWAIT) command is queued. However, a cleared need_sync only means that a covering CWAIT has been queued, not that all previously queued commands have actually completed in hardware. iommu_completion_wait() read need_sync locklessly and returned early when it was false. This breaks the "block until all previously queued commands have completed" contract in a multi-CPU scenario: CPU2: queue inv-B => need_sync = true CPU1: queue CWAIT(N); need_sync = false; then wait_on_sem(N) CPU2: read need_sync == false => return 0 (no wait!) CPU2 returns without waiting for any sequence number even though its inv-B may not have completed yet (CWAIT(N), queued after inv-B, has not been signaled). CPU2 then proceeds to, for example, free page-table pages while the IOMMU can still walk stale translations, opening a use-after-free window. This is a logical race in the meaning of the flag, not a memory-visibility issue, so barriers alone do not help. Fix it without losing the optimization of avoiding redundant CWAIT commands: take iommu->lock before testing need_sync, and when it is false do not return early but wait for the last allocated sequence number (cmd_sem_val). Since need_sync == false implies no sync command was queued after the last CWAIT, that CWAIT is FIFO-ordered after every not-yet-completed command, so waiting for its sequence number guarantees all prior commands (possibly queued by another CPU) have completed. The common path with pending work is unchanged and no extra hardware command is issued. Signed-off-by: Guanghui Feng Fixes: 815b33fdc279 ("x86/amd-iommu: Cleanup completion-wait handling") Reviewed-by: Vasant Hegde Signed-off-by: Will Deacon --- drivers/iommu/amd/iommu.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 563f9c2672d5..29dc18d3d22e 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -1450,11 +1450,23 @@ static int iommu_completion_wait(struct amd_iommu *iommu) int ret; u64 data; - if (!iommu->need_sync) - return 0; - raw_spin_lock_irqsave(&iommu->lock, flags); + if (!iommu->need_sync) { + /* + * No command has been queued since the last completion-wait. + * A concurrent CPU may have already queued that CWAIT and + * cleared need_sync; need_sync == false only means a covering + * CWAIT is queued, not that all prior commands have completed. + * Wait for the last allocated sequence number so that any + * command queued before this call (possibly on another CPU) + * is guaranteed to have completed before returning. + */ + data = iommu->cmd_sem_val; + raw_spin_unlock_irqrestore(&iommu->lock, flags); + return wait_on_sem(iommu, data); + } + data = get_cmdsem_val(iommu); build_completion_wait(&cmd, iommu, data); @@ -1464,9 +1476,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu) if (ret) return ret; - ret = wait_on_sem(iommu, data); - - return ret; + return wait_on_sem(iommu, data); } static void domain_flush_complete(struct protection_domain *domain) -- cgit v1.2.3 From 16cc4f5c1c4b9e45eca7f7deefa5410a292db599 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 21 Jul 2026 12:08:49 +0200 Subject: exec: fix unsigned loop counter wrap in transfer_args_to_stack() The stop value is derived from bprm->p >> PAGE_SHIFT. The index variable is an unsigned long. If bprm->p drops below PAGE_SIZE and stop becomes zero the loop condition index >= stop is always true. After the index == 0 iteration the decrement wraps to ULONG_MAX and bprm->page[ULONG_MAX] reads sizeof(void *) bytes in front of the array. The pointer has wrapped to -1. That garbage pointer is then passed to kmap_local_page() and PAGE_SIZE bytes are copied from wherever that lands into the stack of the process being created. And the loop doesn't terminate either... Getting there only requires bprm->p < PAGE_SIZE. On !MMU bprm_set_stack_limit() and bprm_hit_stack_limit() are empty. So the only constraint on how far bprm->p is pushed down is valid_arg_len(), i.e. that each individual string still fits in what is left. bprm->p starts at PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *) so a single argument or environment string of a little over 31 pages leaves it in the first page: Oops - load access fault [#1] CPU: 0 UID: 0 PID: 1 Comm: victim Not tainted 7.2.0-rc4 #1 epc : __memcpy+0xd4/0xf8 ra : transfer_args_to_stack+0xaa/0xae s4 : ffffffffffffffff s2 : 0000000000000000 a1 : ffffffdc98000000 a2 : 0000000000001000 status: 0000000a00001880 badaddr: ffffffdc98000000 cause: 0000000000000005 [<801a5324>] __memcpy+0xd4/0xf8 [<800d5f6a>] load_flat_binary+0x43a/0x65e [<800a2de4>] bprm_execve+0x1d4/0x316 [<800a351a>] do_execveat_common+0x12e/0x138 [<800a3d44>] __riscv_sys_execve+0x38/0x4e Kernel panic - not syncing: Fatal exception in interrupt This is an arcane bug but we should still fix it. Count down from MAX_ARG_PAGES so the loop ends when index reaches stop, stop == 0 included. The iterations performed are unchanged for every other value of stop. Only CONFIG_MMU=n builds are affected, transfer_args_to_stack() is used by binfmt_flat and binfmt_elf_fdpic on nommu only. The loop predates git history. commit 7e7ec6a93434 ("elf_fdpic_transfer_args_to_stack(): make it generic") only moved it from binfmt_elf_fdpic.c into fs/exec.c and narrowed the copy to the used part of the first page. The condition and the decrement are unchanged from 2.6.12-rc2. Link: https://patch.msgid.link/20260721-hochachtung-staumauer-pigmente-15d71f7d7d04@brauner Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: David Hildenbrand (Arm) Signed-off-by: Christian Brauner (Amutable) --- fs/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/exec.c b/fs/exec.c index d5993cedc829..c7b8f2d6366c 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -740,7 +740,7 @@ int transfer_args_to_stack(struct linux_binprm *bprm, stop = bprm->p >> PAGE_SHIFT; sp = *sp_location; - for (index = MAX_ARG_PAGES - 1; index >= stop; index--) { + for (index = MAX_ARG_PAGES; index-- > stop; ) { unsigned int offset = index == stop ? bprm->p & ~PAGE_MASK : 0; char *src = kmap_local_page(bprm->page[index]) + offset; sp -= PAGE_SIZE - offset; -- cgit v1.2.3 From 952c02b33f56207a160421bcd61e7ac53c9c59ae Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sat, 11 Jul 2026 14:03:02 -0700 Subject: wifi: mac80211: tear down new links on vif update error path When ieee80211_vif_update_links() adds new links it allocates a link container for each and calls ieee80211_link_init() (which registers the per-link debugfs files with file->private_data pointing into the container) and ieee80211_link_setup(). If the subsequent drv_change_vif_links() fails, the error path restores the old pointers and jumps to 'free', which frees the new containers but never removes their debugfs entries or stops the links. The debugfs files survive with file->private_data dangling at the freed container, so a later open()+read() (e.g. link-1/txpower) dereferences freed memory in ieee80211_if_read_link(), a use-after-free. The removal path already dismantles links correctly via ieee80211_tear_down_links(), which removes each link's keys and debugfs entries and calls ieee80211_link_stop(); the add path on the error branch does not. Commit be1ba9ed221f ("wifi: mac80211: avoid weird state in error path") hardened this same error path for the link-removal case (new_links == 0) but left the newly-added links' teardown unaddressed. drv_change_vif_links() can fail at runtime on MLO drivers (internal allocation / queue / firmware command failures). Remove the new links' debugfs entries and stop them before freeing. BUG: KASAN: slab-use-after-free in ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127) Read of size 8 at addr ffff888011290000 by task exploit/145 Call Trace: ... ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127) short_proxy_read (fs/debugfs/file.c:373) vfs_read (fs/read_write.c:572) ksys_read (fs/read_write.c:716) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) ... Oops: general protection fault, probably for non-canonical address 0xdffffc000000000a RIP: 0010:ieee80211_if_read_link (net/mac80211/debugfs_netdev.c:127) Kernel panic - not syncing: Fatal exception Fixes: 170cd6a66d9a ("wifi: mac80211: add netdev per-link debugfs data and driver hook") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260711210302.2098404-1-xmei5@asu.edu Signed-off-by: Johannes Berg --- net/mac80211/link.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index d0535268962c..dc68144dc363 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -399,6 +399,10 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, memcpy(sdata->link, old_data, sizeof(old_data)); memcpy(sdata->vif.link_conf, old, sizeof(old)); ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links); + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + ieee80211_link_debugfs_remove(&links[link_id]->data); + ieee80211_link_stop(&links[link_id]->data); + } /* and free (only) the newly allocated links */ memset(to_free, 0, sizeof(links)); goto free; -- cgit v1.2.3 From a61b4db34a753bdf5c9e77a7f3d3dddd41dcfacc Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Wed, 8 Jul 2026 22:34:08 +0800 Subject: nfp: Check resource mutex allocation nfp_cpp_resource_find() allocates a CPP mutex handle for the matching resource-table entry and then reports success. nfp_resource_try_acquire() immediately passes that handle to nfp_cpp_mutex_trylock(). However, nfp_cpp_mutex_alloc() returns NULL on failure. If that happens for a matching table entry, the resource lookup still returns success and the following trylock dereferences a NULL mutex pointer while opening the resource. nfp_resource_acquire() already treats failure to allocate the table mutex as -ENOMEM. Do the same for the resource mutex and fail the lookup before publishing the rest of the resource handle. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: f01a2161577d ("nfp: add support for resources") Signed-off-by: Ruoyu Wang Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260708143408.3168425-1-ruoyuw560@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c index 6d5833479d12..237300b82b91 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c @@ -96,6 +96,9 @@ static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res) res->mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET, addr, key); + if (!res->mutex) + return -ENOMEM; + res->cpp_id = NFP_CPP_ID(entry.region.cpp_target, entry.region.cpp_action, entry.region.cpp_token); -- cgit v1.2.3 From 91957b89da995607cb654b1f9a3c126ddbaee10f Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Wed, 8 Jul 2026 22:34:15 +0800 Subject: wan: wanxl: Only reset hardware after BAR mapping wanxl_pci_init_one() stores the freshly allocated card in driver data before the PLX BAR is mapped. Several early probe failures then unwind through wanxl_pci_remove_one(), including failure to allocate the coherent status area or to restore the DMA mask. wanxl_pci_remove_one() unconditionally calls wanxl_reset(), and wanxl_reset() dereferences card->plx. On those early failures card->plx is still NULL, so the error path can dereference a NULL MMIO pointer. Only issue the hardware reset once the BAR mapping exists. The remaining cleanup in wanxl_pci_remove_one() already checks whether later resources were allocated. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260708143415.3169358-1-ruoyuw560@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/wan/wanxl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index d4da88c77112..065c00c12cc1 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -514,7 +514,8 @@ static void wanxl_pci_remove_one(struct pci_dev *pdev) if (card->irq) free_irq(card->irq, card); - wanxl_reset(card); + if (card->plx) + wanxl_reset(card); for (i = 0; i < RX_QUEUE_LENGTH; i++) if (card->rx_skbs[i]) { -- cgit v1.2.3 From 3349ef6a366a61d631f6a263d12cea240957719d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 21 Jul 2026 13:20:45 +0200 Subject: binfmt_elf_fdpic: only honour the first PT_INTERP The program header scan handles PT_INTERP from a switch nested in the scan loop, so its break leaves the switch and not the loop. A binary carrying more than one PT_INTERP runs the case again and overwrites both interpreter_name and interpreter. The previous name allocation leaks and so does the previous interpreter reference, along with the write denial open_exec() took on it. The denial is never released, so the file stays unwritable for as long as the system runs. An unprivileged caller reaches this with a crafted binary and repeats it at will. binfmt_elf stops at the first PT_INTERP. Do the same here. The flaw dates back to the driver's introduction in the pre-git history tree introduced in v2.6.11 by 91808d6ebe39 ("[PATCH] FRV: Add FDPIC ELF binary format driver"). Link: https://patch.msgid.link/20260721-gezittert-medium-kreide-b41fc1f0277e@brauner Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Jori Koolstra Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_elf_fdpic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 7e3108489c83..fe0b5c5ed2bc 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -231,6 +231,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) { switch (phdr->p_type) { case PT_INTERP: + /* elf ABI allows only one interpreter */ + if (interpreter_name) + continue; + retval = -ENOMEM; if (phdr->p_filesz > PATH_MAX) goto error; -- cgit v1.2.3 From 121a96c5a0db8d18e2ba2cb89660cca8a40508fe Mon Sep 17 00:00:00 2001 From: Georgi Valkov Date: Mon, 13 Jul 2026 01:17:09 +0300 Subject: wifi: mwifiex: fix freeze for 60 seconds caused by request_firmware Fix regression in rgpower table loading, caused by using request_firmware(): when the requested firmware does not exist, e.g. nxp/rgpower_WW.bin does not exist on OpenWRT builds for WRT3200ACM, request_firmware() falls back to firmware_fallback_sysfs(), which expects the firmware to be provided by user space using SYSFS. No such utility is provided in this configuration, so the entire system locks up for 60 seconds, until the request times out. During this time, no other log messages are observed, and the device does not respond to commands over UART. The request_firmware() call is performed in the following context: current->comm kworker/1:2 in_task 1 irqs_disabled 0 in_atomic 0 Fixed by using request_firmware_direct(). This prevents fallback to SYSFS, and avoids delay. The rgpower table is optional. The driver falls back to the device tree power table if the firmware is not present. The error code is printed for debugging and returned to the caller, which only cares for success or failure, so there are no side effects. Fixes: 7b6f16a25806 ("wifi: mwifiex: add rgpower table loading support") Signed-off-by: Georgi Valkov Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260712221709.7099-1-gvalkov@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index a6550548d3b4..9460d5352b23 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -196,6 +196,7 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv) struct mwifiex_adapter *adapter = priv->adapter; char rgpower_table_name[30]; char country_code[3]; + int ret; strscpy(country_code, domain_info->country_code, sizeof(country_code)); @@ -214,16 +215,17 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv) adapter->rgpower_data = NULL; } - if ((request_firmware(&adapter->rgpower_data, rgpower_table_name, - adapter->dev))) { + ret = request_firmware_direct(&adapter->rgpower_data, rgpower_table_name, + adapter->dev); + + if (ret) { mwifiex_dbg( adapter, INFO, - "info: %s: failed to request regulatory power table\n", - __func__); - return -EIO; + "info: %s: failed to request regulatory power table: %d\n", + __func__, ret); } - return 0; + return ret; } static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv) -- cgit v1.2.3 From 4c4c97b60a5e978121d9ee8cb0ab3916e5d6a8de Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Tue, 14 Jul 2026 17:17:58 +0800 Subject: wifi: wilc1000: validate assoc response length before subtracting header wilc_parse_assoc_resp_info() computes the trailing IE length as ies_len = buffer_len - sizeof(*res); without first checking that buffer_len is at least sizeof(struct wilc_assoc_resp) (6 bytes). buffer_len is the length reported for a received association response (host_int_parse_assoc_resp_info() passes hif_drv->assoc_resp / assoc_resp_info_len straight in) and must be validated before the driver accesses the fixed header. For a frame shorter than the 6-byte fixed header, the subtraction wraps. For a four-byte response the result is truncated to a u16 ies_len of 65534, so kmemdup() then attempts to copy 65534 bytes starting at buffer + sizeof(*res), beyond the valid association-response data (CWE-125). A response shorter than four bytes can also cause an out-of-bounds read of res->status_code at offsets 2 and 3. Reject frames too short to hold the fixed header before touching the header or computing ies_len. Also set the connection status to a failure on this path: the caller falls through to a "conn_info->status == WLAN_STATUS_SUCCESS" check after the parser returns, so leaving the status untouched could let a malformed short response be treated as a successful association. Fixes: c5c77ba18ea6 ("staging: wilc1000: Add SDIO/SPI 802.11 driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Huihui Huang Link: https://patch.msgid.link/20260714091811.3596126-1-hhhuang@smu.edu.sg Signed-off-by: Johannes Berg --- drivers/net/wireless/microchip/wilc1000/hif.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index 009c4770a6f9..60fe5f08964f 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -600,6 +600,11 @@ static s32 wilc_parse_assoc_resp_info(u8 *buffer, u32 buffer_len, u16 ies_len; struct wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer; + if (buffer_len < sizeof(*res)) { + ret_conn_info->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + return -EINVAL; + } + ret_conn_info->status = le16_to_cpu(res->status_code); if (ret_conn_info->status == WLAN_STATUS_SUCCESS) { ies = &buffer[sizeof(*res)]; -- cgit v1.2.3 From 3c0d10f233f19153f81fef685b5c6716776a5af3 Mon Sep 17 00:00:00 2001 From: Enrico Zanda Date: Wed, 8 Jul 2026 16:22:42 +0100 Subject: vhost-net: fix TX stall when vhost owns virtio-net header When vhost owns the virtio-net header, i.e. when VHOST_NET_F_VIRTIO_NET_HDR is negotiated, sock_hlen is 0, meaning that no header will be forwarded to the TAP device. In the current vhost_net_build_xdp() implementation, when sock_hlen == 0, the gso pointer can point at the start of the Ethernet frame instead of a virtio-net header. This results in a wrong interpretation of the destination MAC address bytes as struct virtio_net_hdr fields. This can, for some MAC addresses, trigger -EINVAL and return early before the TX descriptor is completed, which can stall vhost-net TX. Before 97b2409f28e0, the gso pointer was set to the zeroed padding area, using it as a synthetic virtio-net header. Restore that behavior. Fixes: 97b2409f28e0 ("vhost-net: reduce one userspace copy when building XDP buff") Signed-off-by: Enrico Zanda Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260708152242.2268848-1-enrico.zanda@arm.com Signed-off-by: Paolo Abeni --- drivers/vhost/net.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 77b59f49bddb..3e72b9c6af0c 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -731,10 +731,12 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq, goto err; } - gso = buf + pad - sock_hlen; - - if (!sock_hlen) + if (!sock_hlen) { memset(buf, 0, pad); + gso = buf; + } else { + gso = buf + pad - sock_hlen; + } if ((gso->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && vhost16_to_cpu(vq, gso->csum_start) + -- cgit v1.2.3 From 0fe2d5be7ab59717adb3f9cfab3832c6c4dd770c Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 14 Jul 2026 14:10:46 +0300 Subject: wifi: mac80211: copy aggregation information This information can be considered part of the capabilities and should also be copied to the NAN data station. Fixes: 27e9b326b674 ("wifi: mac80211: support NAN stations") Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260714141038.15620aa5324b.I049254b854ac91c32e0768eb7c819f32eda34218@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b00191e02a63..43f142624d33 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2611,6 +2611,9 @@ static int sta_apply_parameters(struct ieee80211_local *local, memcpy(&sta->deflink.pub->supp_rates, &nmi_sta->deflink.pub->supp_rates, sizeof(sta->deflink.pub->supp_rates)); + + sta->deflink.pub->agg = nmi_sta->deflink.pub->agg; + __ieee80211_sta_recalc_aggregates(sta, 0); } /* set the STA state after all sta info from usermode has been set */ -- cgit v1.2.3 From f0858bfc7d3cab411a447b88e3ef970e575032c9 Mon Sep 17 00:00:00 2001 From: "HE WEI (ギカク)" Date: Wed, 15 Jul 2026 22:57:11 +0900 Subject: wifi: mwifiex: bound uAP association event IEs to the event buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mwifiex_process_uap_event() handles EVENT_UAP_STA_ASSOC by exposing the (re)association request IEs that the firmware copies into the event: sinfo->assoc_req_ies = &event->data[len]; len = (u8 *)sinfo->assoc_req_ies - (u8 *)&event->frame_control; sinfo->assoc_req_ies_len = le16_to_cpu(event->len) - (u16)len; event->len is supplied by the device firmware and is never validated, and the subtraction is unchecked. assoc_req_ies points into adapter->event_body[MAX_EVENT_SIZE], a fixed-size array embedded in the kmalloc()'d struct mwifiex_adapter. On the ap_11n_enabled path mwifiex_set_sta_ht_cap() walks these IEs with cfg80211_find_ie(), whose for_each_element() loop dereferences each element header. A firmware-reported event->len larger than the bytes actually received makes assoc_req_ies_len describe IEs that extend past event_body, so the walk reads out of the adapter slab object, a slab-out-of-bounds read (KASAN: slab-out-of-bounds in cfg80211_find_ie). An event->len smaller than the header instead makes the int subtraction negative, which wraps to a huge size_t when stored in assoc_req_ies_len. The same length is handed to cfg80211_new_sta(), so a more modest over-claim can also copy stale event_body bytes into the NL80211_CMD_NEW_STATION notification. A malicious or malfunctioning mwifiex device (USB/SDIO/PCIe) can deliver such an event while the interface is in AP/uAP mode. Validate event->len before use: reject a length that underflows the header or that would place the IEs outside the event_body[] buffer the event was copied into. event->len here is struct mwifiex_assoc_event.len, a payload field internal to this event, not the transport frame length, so it is validated in this handler rather than at the generic MWIFIEX_TYPE_EVENT receive path, which only sees the event cause and the transport frame length. The bound is against event_body[MAX_EVENT_SIZE] rather than the actually-received length because the transports store the event differently (USB and SDIO leave the 4-byte event header in event_skb, PCIe strips it via skb_pull), whereas event_body is the single fixed buffer all of them copy the event into. This is the event-path analogue of the receive-path bounds checks added in commit 119585281617 ("wifi: mwifiex: Fix OOB and integer underflow when rx packets"). Fixes: e568634ae7ac ("mwifiex: add AP event handling framework") Signed-off-by: HE WEI (ギカク) Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260715135711.34688-1-skyexpoc@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/uap_event.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c index 679fdae0f001..ba1bdbbff687 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_event.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -123,11 +123,31 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) len = ETH_ALEN; if (len != -1) { + u16 evt_len = le16_to_cpu(event->len); + sinfo->assoc_req_ies = &event->data[len]; len = (u8 *)sinfo->assoc_req_ies - (u8 *)&event->frame_control; - sinfo->assoc_req_ies_len = - le16_to_cpu(event->len) - (u16)len; + + /* + * event->len is reported by the device firmware + * and is not otherwise validated. Reject a + * length that underflows the header, or that + * would place the association request IEs + * outside the fixed-size event_body[] buffer the + * event was copied into; otherwise the IE walk + * in mwifiex_set_sta_ht_cap() reads past + * event_body and out of the adapter slab object. + */ + if (evt_len < len || + (u8 *)&event->frame_control + evt_len > + adapter->event_body + MAX_EVENT_SIZE) { + mwifiex_dbg(adapter, ERROR, + "invalid STA assoc event length\n"); + kfree(sinfo); + return -1; + } + sinfo->assoc_req_ies_len = evt_len - (u16)len; } } cfg80211_new_sta(priv->netdev->ieee80211_ptr, event->sta_addr, -- cgit v1.2.3 From 780dfed688622ea01be3c9c2c55eec2207f05e04 Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Thu, 16 Jul 2026 13:35:53 +0800 Subject: iommu/vt-d: Disallow SVA if page walk is not coherent Hardware implementations report Scalable-Mode Page-walk Coherency Support via the SMPWCS field in the extended capability register. If the hardware does not support page-walk coherency, a clflush is required every time the page table entries (which are walked by the IOMMU hardware) are updated. In the SVA case, page tables are managed by the CPU mm core, not by the IOMMU driver. Because the IOMMU driver has no way of knowing whether the CPU page table management code has ensured coherency via clflush, the driver must deny SVA if the hardware does not support coherent paging. Fixes: ff3dc6521f78 ("iommu/vt-d: Fix CPU and IOMMU SVM feature matching checks") Cc: stable@vger.kernel.org Signed-off-by: Lu Baolu Reviewed-by: Kevin Tian Reviewed-by: Samiullah Khawaja Reviewed-by: Jason Gunthorpe Signed-off-by: Will Deacon --- drivers/iommu/intel/svm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index fea10acd4f02..726f7b6d0bff 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -27,7 +27,7 @@ void intel_svm_check(struct intel_iommu *iommu) { - if (!pasid_supported(iommu)) + if (!pasid_supported(iommu) || !ecap_smpwc(iommu->ecap)) return; if (cpu_feature_enabled(X86_FEATURE_GBPAGES) && -- cgit v1.2.3 From 61a799ffd1e5a4fd3702d547828b7ff3d161468e Mon Sep 17 00:00:00 2001 From: Huihui Huang Date: Wed, 15 Jul 2026 22:08:10 +0800 Subject: wifi: at76c50x-usb: avoid length underflow in at76_guess_freq() at76_guess_freq() checks only that the received frame is at least a bare 802.11 header (24 bytes) before subtracting the fixed management-body offset: len -= el_off; For both beacon and probe response frames, el_off is 36. If the frame is shorter than el_off, subtracting it causes the calculated IE length to wrap. The length is eventually passed to cfg80211_find_elem_match() as a very large unsigned value, so the element walk runs beyond the RX skb. This path is reached from at76_rx_tasklet() while scanning. If the device delivers a truncated beacon or probe response, the oversized IE length causes an out-of-bounds read during scanning. Skip the IE lookup if the frame does not reach the variable elements, before subtracting el_off. Fixes: 1264b951463a ("at76c50x-usb: add driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Huihui Huang Link: https://patch.msgid.link/20260715140815.1242033-1-hhhuang@smu.edu.sg Signed-off-by: Johannes Berg --- drivers/net/wireless/atmel/at76c50x-usb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 32e3e09e7680..d9c2809be4ba 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -1521,13 +1521,16 @@ static inline int at76_guess_freq(struct at76_priv *priv) if (ieee80211_is_probe_resp(hdr->frame_control)) { el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; } else if (ieee80211_is_beacon(hdr->frame_control)) { el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; } else { goto exit; } + + if (len < el_off) + goto exit; + + el = priv->rx_skb->data + el_off; len -= el_off; el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); -- cgit v1.2.3 From 1cb5845a58d8e1f85d5766c6fbcbfddf96c212a1 Mon Sep 17 00:00:00 2001 From: Georgi Valkov Date: Thu, 16 Jul 2026 03:17:28 +0300 Subject: wifi: mwifiex: replace one-element arrays with flexible array members Replace deprecated one-element arrays with flexible array members. CONFIG_FORTIFY_SOURCE reports the following warning when one-element arrays are used as variable-length buffers: sta_cmd.c:1033 mwifiex_sta_prepare_cmd memcpy: detected field-spanning write (size 84) of single field "domain->triplet" at .../marvell/mwifiex/sta_cmd.c:1033 (size 3) Convert affected structs to use flexible array members. - Preserve existing wire layouts. - Use DECLARE_FLEX_ARRAY() for structs inside affected unions. Tested-on: WRT3200ACM, OpenWrt Signed-off-by: Georgi Valkov Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260716001728.57799-1-gvalkov@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/fw.h | 18 +++++++++--------- drivers/net/wireless/marvell/mwifiex/join.c | 8 ++++---- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index e9e896606912..93561116959a 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -823,7 +823,7 @@ struct chan_band_param_set { struct mwifiex_ie_types_chan_band_list_param_set { struct mwifiex_ie_types_header header; - struct chan_band_param_set chan_band_param[1]; + struct chan_band_param_set chan_band_param[]; } __packed; struct mwifiex_ie_types_rates_param_set { @@ -886,7 +886,7 @@ struct mwifiex_ie_types_wildcard_ssid_params { #define TSF_DATA_SIZE 8 struct mwifiex_ie_types_tsf_timestamp { struct mwifiex_ie_types_header header; - u8 tsf_data[1]; + u8 tsf_data[]; } __packed; struct mwifiex_cf_param_set { @@ -903,8 +903,8 @@ struct mwifiex_ibss_param_set { struct mwifiex_ie_types_ss_param_set { struct mwifiex_ie_types_header header; union { - struct mwifiex_cf_param_set cf_param_set[1]; - struct mwifiex_ibss_param_set ibss_param_set[1]; + DECLARE_FLEX_ARRAY(struct mwifiex_cf_param_set, cf_param_set); + DECLARE_FLEX_ARRAY(struct mwifiex_ibss_param_set, ibss_param_set); } cf_ibss; } __packed; @@ -922,8 +922,8 @@ struct mwifiex_ds_param_set { struct mwifiex_ie_types_phy_param_set { struct mwifiex_ie_types_header header; union { - struct mwifiex_fh_param_set fh_param_set[1]; - struct mwifiex_ds_param_set ds_param_set[1]; + DECLARE_FLEX_ARRAY(struct mwifiex_fh_param_set, fh_param_set); + DECLARE_FLEX_ARRAY(struct mwifiex_ds_param_set, ds_param_set); } fh_ds; } __packed; @@ -1383,7 +1383,7 @@ struct host_cmd_ds_802_11_snmp_mib { __le16 query_type; __le16 oid; __le16 buf_size; - u8 value[1]; + u8 value[]; } __packed; struct mwifiex_rate_scope { @@ -1551,7 +1551,7 @@ struct mwifiex_scan_cmd_config { * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set */ - u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored + u8 tlv_buf[]; /* SSID TLV(s) and ChanList TLVs are stored here */ } __packed; @@ -1683,7 +1683,7 @@ struct host_cmd_ds_802_11_bg_scan_query_rsp { struct mwifiex_ietypes_domain_param_set { struct mwifiex_ie_types_header header; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - struct ieee80211_country_ie_triplet triplet[1]; + struct ieee80211_country_ie_triplet triplet[]; } __packed; struct host_cmd_ds_802_11d_domain_info { diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index b48f7febaf03..259140395d35 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -421,15 +421,15 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); - phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); - memcpy(&phy_tlv->fh_ds.ds_param_set, + phy_tlv->header.len = cpu_to_le16(sizeof(*phy_tlv->fh_ds.ds_param_set)); + memcpy(phy_tlv->fh_ds.ds_param_set, &bss_desc->phy_param_set.ds_param_set.current_chan, - sizeof(phy_tlv->fh_ds.ds_param_set)); + sizeof(*phy_tlv->fh_ds.ds_param_set)); pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); - ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); + ss_tlv->header.len = cpu_to_le16(sizeof(*ss_tlv->cf_ibss.cf_param_set)); pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); /* Get the common rates supported between the driver and the BSS Desc */ diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 623ddde8c8e5..071f7cb305e1 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -108,7 +108,7 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) - - 1 + S_DS_GEN); + + S_DS_GEN); snmp_mib->oid = cpu_to_le16((u16)cmd_oid); if (cmd_action == HostCmd_ACT_GEN_GET) { -- cgit v1.2.3 From c3d68e294cbb6a4090bb219d3dcaca85a011809b Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 16 Jul 2026 12:30:42 +0200 Subject: wifi: mwifiex: fix NULL dereference when the AP has HT-cap but no HT-oper mwifiex_tdls_add_ht_oper() gates its follow-the-AP-bandwidth path on bss_desc->bcn_ht_cap being present, but then dereferences a different pointer, bss_desc->bcn_ht_oper: if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && bss_desc->bcn_ht_cap && ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) bcn_ht_cap and bcn_ht_oper are populated independently while parsing the associated AP's beacon in mwifiex_update_bss_desc_with_ie(): an AP that advertises an HT Capabilities element but no HT Operation element leaves bcn_ht_cap non-NULL and bcn_ht_oper NULL. Setting up a TDLS link to a peer while associated to such an AP then dereferences the NULL bcn_ht_oper and crashes the kernel. Every other bcn_ht_oper user in the driver NULL-checks it first. Guard on the pointer that is actually dereferenced. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: 396939f94084 ("mwifiex: add HT operation IE in TDLS setup confirm") Cc: stable@vger.kernel.org Assisted-by: 0sec:multi-model Signed-off-by: Doruk Tan Ozturk Reviewed-by: Francesco Dolcini Link: https://patch.msgid.link/20260716103042.88469-1-doruk@0sec.ai Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/tdls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 845f2a22e071..c71ffe8399e4 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -215,7 +215,7 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, /* follow AP's channel bandwidth */ if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && - bss_desc->bcn_ht_cap && + bss_desc->bcn_ht_oper && ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; -- cgit v1.2.3 From fb80117fddb5b477218dc99bb53911b72c3847f8 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 20 Jul 2026 19:46:13 +0800 Subject: iommu/amd: Bound the early ACPI HID map The ivrs_acpihid command-line parser appends entries to a fixed four-element early_acpihid_map array. Unlike the sibling IOAPIC and HPET parsers, it does not reject a fifth entry before incrementing the map size. Check the capacity at the common found label before parsing the HID and UID or writing the entry. Fixes: ca3bf5d47cec ("iommu/amd: Introduces ivrs_acpihid kernel parameter") Signed-off-by: Pengpeng Hou Reviewed-by: Ankit Soni Signed-off-by: Will Deacon --- drivers/iommu/amd/init.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index e93bcb5eef70..e7d7b4cb9337 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -3864,6 +3864,12 @@ not_found: return 1; found: + if (early_acpihid_map_size == EARLY_MAP_SIZE) { + pr_err("Early ACPI HID map overflow - ignoring ivrs_acpihid%s\n", + str); + return 1; + } + p = acpiid; hid = strsep(&p, ":"); uid = p; -- cgit v1.2.3 From 754f8efe45f87e3a9c6871b645b2f9d46d1b407b Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Tue, 21 Jul 2026 17:34:10 +0800 Subject: iommu/intel: Fix out-of-bounds memset in dmar_latency_disable() dmar_latency_disable() intends to zero out only the single latency_statistic entry for the given type, but the memset size was computed as sizeof(*lstat) * DMAR_LATENCY_NUM, which clears the entire array starting from &lstat[type]. When type > 0, this writes beyond the end of the allocated array, corrupting adjacent memory. Fix by using sizeof(*lstat) to clear only the target entry. Fixes: 55ee5e67a59a ("iommu/vt-d: Add common code for dmar latency performance monitors") Signed-off-by: Li RongQing Signed-off-by: Will Deacon --- drivers/iommu/intel/perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/intel/perf.c b/drivers/iommu/intel/perf.c index 02168f2f20a4..bec98dcbb9ec 100644 --- a/drivers/iommu/intel/perf.c +++ b/drivers/iommu/intel/perf.c @@ -63,7 +63,7 @@ void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type) return; spin_lock_irqsave(&latency_lock, flags); - memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM); + memset(&lstat[type], 0, sizeof(*lstat)); spin_unlock_irqrestore(&latency_lock, flags); } -- cgit v1.2.3 From a007a384c9eb17610f53a53e2f59944c31f1565a Mon Sep 17 00:00:00 2001 From: Andrew Pope Date: Fri, 17 Jul 2026 11:17:51 +1000 Subject: wifi: mac80211: recalculate TIM when a station enters power save When an AP buffers frames for a station on its per-station TXQs and the station subsequently enters power save, sta_ps_start() records the buffered TIDs in txq_buffered_tids but does not update the TIM. The station's TIM bit is only ever set when a further frame is buffered while the station is already asleep (ieee80211_tx_h_unicast_ps_buf() -> sta_info_recalc_tim()). If no further downlink frame arrives for that station the beacon TIM never advertises the buffered traffic. A station relying on the TIM then remains in doze indefinitely on top of a non-empty queue. Its TXQs were removed from the scheduler's active list at PS entry, nothing pages it, and the flow deadlocks until an unrelated event wakes the station. Recalculate the TIM at the end of sta_ps_start(), so traffic already buffered at PS entry is advertised immediately. sta_info_recalc_tim() already consults txq_buffered_tids, which is updated above, and is safe in this context (it is already called from equivalent paths such as the tx handlers and ieee80211_handle_filtered_frame()). Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation") Signed-off-by: Andrew Pope Link: https://patch.msgid.link/20260717011751.79524-1-andrew.pope@morsemicro.com [add wifi: subject prefix] Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index d9ea19be075d..5e26be8e27d8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1717,6 +1717,8 @@ static void sta_ps_start(struct sta_info *sta) else clear_bit(tid, &sta->txq_buffered_tids); } + + sta_info_recalc_tim(sta); } static void sta_ps_end(struct sta_info *sta) -- cgit v1.2.3 From 538c51e9d124cf656f2dd0c0394a8545efc7102d Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Sat, 18 Jul 2026 02:43:52 +0000 Subject: wifi: brcmfmac: make release_scratchbuffers idempotent brcmf_pcie_release_scratchbuffers() frees the shared.scratch and shared.ringupd DMA buffers with dma_free_coherent() but does not clear the pointers afterwards, unlike the sibling release_ringbuffers() which NULLs commonrings/flowrings/idxbuf on release. Both the bus_reset .reset callback (brcmf_pcie_reset) and brcmf_pcie_remove() call release_scratchbuffers. When reset teardown has run before removal, remove's own teardown would call dma_free_coherent() a second time on the already-freed DMA allocation. NULL the pointers after free, matching release_ringbuffers(), so a later release observes that the allocation has already been released. This patch makes repeated sequential release safe; the reset-work lifetime is handled separately by the following patch. This issue was found by an in-house static analysis tool. Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Assisted-by: Codex:gpt-5.6 Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260718024353.3147201-2-fanwu01@zju.edu.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 13662aa4b4ea..9f10b3fff9ff 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1383,16 +1383,20 @@ fail: static void brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) { - if (devinfo->shared.scratch) + if (devinfo->shared.scratch) { dma_free_coherent(&devinfo->pdev->dev, BRCMF_DMA_D2H_SCRATCH_BUF_LEN, devinfo->shared.scratch, devinfo->shared.scratch_dmahandle); - if (devinfo->shared.ringupd) + devinfo->shared.scratch = NULL; + } + if (devinfo->shared.ringupd) { dma_free_coherent(&devinfo->pdev->dev, BRCMF_DMA_D2H_RINGUPD_BUF_LEN, devinfo->shared.ringupd, devinfo->shared.ringupd_dmahandle); + devinfo->shared.ringupd = NULL; + } } static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) -- cgit v1.2.3 From 43b25879f004c98defa2776bedc6ca4763c51945 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Sat, 18 Jul 2026 02:43:53 +0000 Subject: wifi: brcmfmac: drain bus_reset work on device removal brcmf_fw_crashed() and the debugfs "reset" entry both schedule drvr->bus_reset, whose callback recovers drvr through container_of() and dereferences it. The removal path frees drvr (brcmf_free -> wiphy_free) without draining the work, so a bus_reset callback pending or running during removal can outlive drvr. Cancellation cannot live in brcmf_detach() or brcmf_free(): the work callback reaches teardown through the bus .reset op (PCIe brcmf_pcie_reset -> brcmf_detach; SDIO brcmf_sdio_bus_reset -> brcmf_sdiod_remove -> brcmf_free), so cancelling there would wait for the running work and deadlock. Add a per-bus mutex (bus_reset_lock) and route all arming through brcmf_bus_schedule_reset(), which under the lock skips when the bus is marked removing. Each bus remove entry calls brcmf_bus_cancel_reset_work(), which under the same lock sets removing and cancels the work. Holding the mutex across cancel_work_sync() makes the set-removing + drain step atomic. Every producer reaches the arming path from process context -- the PCIe firmware-halt notification runs in the threaded IRQ handler (brcmf_pcie_isr_thread) and the SDIO hostmail path runs from the data workqueue -- so the mutex is taken only in sleepable contexts. Where applicable the remove entry first stops the firmware-crash producer: on PCIe mask the mailbox and synchronize_irq; on SDIO unregister the bus interrupt and cancel the data worker, which also reports firmware halts through brcmf_fw_crashed(). The mutex is initialized at bus allocation. The SDIO suspend power-off path frees drvr through the same brcmf_sdiod_remove() and takes the same lock; resume re-allows the work only on a successful re-probe. Also guard brcmf_fw_crashed() against a NULL bus_if/drvr: it can fire before brcmf_attach() wires up drvr, and it dereferences drvr (bphy_err/brcmf_dev_coredump) before reaching the arming gate. The bus_reset work is shared across buses, so the drain is applied to every remove path: PCIe (the .reset op introduced by the Fixes commit), SDIO (arms the same work through brcmf_fw_crashed()), and USB (via the debugfs "reset" entry). cancel_work_sync() drains a running or pending bus_reset work item before removal frees drvr, and patch 1/2 makes the scratch-buffer release safe when reset teardown has already released those DMA buffers. This patch fixes the lifetime of the bus_reset work item itself. It does not attempt to address the separate, pre-existing lifetime of the asynchronous firmware completion started by the PCIe reset path. That callback needs its own lifetime/ownership protocol and is being tracked separately. This issue was found by an in-house static analysis tool. Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Assisted-by: Codex:gpt-5.6 Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260718024353.3147201-3-fanwu01@zju.edu.cn Signed-off-by: Johannes Berg --- .../wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 13 ++++++ .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 6 +++ .../wireless/broadcom/brcm80211/brcmfmac/core.c | 46 ++++++++++++++++++++-- .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 6 +++ .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 +++ .../wireless/broadcom/brcm80211/brcmfmac/sdio.h | 1 + .../net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 3 ++ 7 files changed, 77 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index d24b80e492e0..ec487c6f2e38 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -1069,6 +1069,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, bus_if = kzalloc_obj(*bus_if); if (!bus_if) return -ENOMEM; + mutex_init(&bus_if->bus_reset_lock); sdiodev = kzalloc_obj(*sdiodev); if (!sdiodev) { kfree(bus_if); @@ -1130,6 +1131,14 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) if (func->num != 1) return; + /* Drain bus_reset before the shared brcmf_sdiod_remove() + * teardown, which the SDIO reset callback also reaches. The + * data worker can arm bus_reset via brcmf_fw_crashed(); cancel + * it first. + */ + brcmf_sdio_cancel_datawork(sdiodev->bus); + brcmf_bus_cancel_reset_work(bus_if); + /* only proceed with rest of cleanup if func 1 */ brcmf_sdiod_remove(sdiodev); @@ -1204,6 +1213,8 @@ static int brcmf_ops_sdio_suspend(struct device *dev) } else { /* power will be cut so remove device, probe again in resume */ brcmf_sdiod_intr_unregister(sdiodev); + brcmf_sdio_cancel_datawork(sdiodev->bus); + brcmf_bus_cancel_reset_work(bus_if); ret = brcmf_sdiod_remove(sdiodev); if (ret) brcmf_err("Failed to remove device on suspend\n"); @@ -1229,6 +1240,8 @@ static int brcmf_ops_sdio_resume(struct device *dev) ret = brcmf_sdiod_probe(sdiodev); if (ret) brcmf_err("Failed to probe device on resume\n"); + else + brcmf_bus_allow_reset_work(bus_if); } else { if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported) disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index fe31051a9e11..9371c1489948 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "debug.h" /* IDs of the 6 default common rings of msgbuf protocol */ @@ -179,6 +180,8 @@ struct brcmf_bus { enum brcmf_fwvendor fwvid; bool always_use_fws_queue; bool wowl_supported; + bool removing; /* device removal in progress; quiesce async work */ + struct mutex bus_reset_lock; const struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; @@ -186,6 +189,9 @@ struct brcmf_bus { struct list_head list; }; +void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if); +void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if); + /* * callback wrappers */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index ec170647800d..dad6f4563d14 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1167,6 +1167,35 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data) return 0; } +/* + * Serialize arming from debugfs reset and brcmf_fw_crashed() against + * teardown. The remove path sets ->removing and drains the work while + * holding bus_reset_lock, so a racing armer is either drained or skips it. + */ +static void brcmf_bus_schedule_reset(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + if (bus_if->drvr && bus_if->drvr->bus_reset.func && !bus_if->removing) + schedule_work(&bus_if->drvr->bus_reset); + mutex_unlock(&bus_if->bus_reset_lock); +} + +void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + bus_if->removing = true; + if (bus_if->drvr) + cancel_work_sync(&bus_if->drvr->bus_reset); + mutex_unlock(&bus_if->bus_reset_lock); +} + +void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + bus_if->removing = false; + mutex_unlock(&bus_if->bus_reset_lock); +} + static void brcmf_core_bus_reset(struct work_struct *work) { struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, @@ -1187,7 +1216,7 @@ static ssize_t bus_reset_write(struct file *file, const char __user *user_buf, if (value != 1) return -EINVAL; - schedule_work(&drvr->bus_reset); + brcmf_bus_schedule_reset(drvr->bus_if); return count; } @@ -1417,14 +1446,23 @@ void brcmf_dev_coredump(struct device *dev) void brcmf_fw_crashed(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_pub *drvr; + + /* May fire before brcmf_attach() wires up drvr, or after removal + * has cleared it; guard the derefs below (and the arming gate in + * brcmf_bus_schedule_reset() already checks drvr/->removing). + */ + if (!bus_if) + return; + drvr = bus_if->drvr; + if (!drvr) + return; bphy_err(drvr, "Firmware has halted or crashed\n"); brcmf_dev_coredump(dev); - if (drvr->bus_reset.func) - schedule_work(&drvr->bus_reset); + brcmf_bus_schedule_reset(bus_if); } void brcmf_detach(struct device *dev) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 9f10b3fff9ff..55f4d7b970f2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2503,6 +2503,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = -ENOMEM; goto fail; } + mutex_init(&bus->bus_reset_lock); bus->msgbuf = kzalloc_obj(*bus->msgbuf); if (!bus->msgbuf) { ret = -ENOMEM; @@ -2598,6 +2599,11 @@ brcmf_pcie_remove(struct pci_dev *pdev) if (devinfo->ci) brcmf_pcie_intr_disable(devinfo); + if (devinfo->irq_allocated) + synchronize_irq(pdev->irq); + + brcmf_bus_cancel_reset_work(bus); + brcmf_detach(&pdev->dev); brcmf_free(&pdev->dev); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index b725c64e5b5c..9f7ed1d293a0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -4560,6 +4560,12 @@ fail: return ret; } +void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus) +{ + if (bus) + cancel_work_sync(&bus->datawork); +} + /* Detach and free everything */ void brcmf_sdio_remove(struct brcmf_sdio *bus) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 80180d5c6c87..b93d153a8963 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -361,6 +361,7 @@ int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev); int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus, bool in_isr); +void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus); void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active); void brcmf_sdio_wowl_config(struct device *dev, bool enabled); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 0b52f968b907..b41949a9bdc8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1260,6 +1260,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo, ret = -ENOMEM; goto fail; } + mutex_init(&bus->bus_reset_lock); bus->dev = dev; bus_pub->bus = bus; @@ -1329,6 +1330,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) return; brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo); + brcmf_bus_cancel_reset_work(devinfo->bus_pub.bus); + brcmf_detach(devinfo->dev); brcmf_free(devinfo->dev); kfree(devinfo->bus_pub.bus); -- cgit v1.2.3 From 3dc723ac78a6e4fa0fd49e27e487ed319da40a9f Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Tue, 21 Jul 2026 13:53:46 +0200 Subject: wifi: mac80211_hwsim: reject undersized HWSIM_ATTR_TX_INFO hwsim_tx_info_frame_received_nl() casts the HWSIM_ATTR_TX_INFO payload to a struct hwsim_tx_rate * and unconditionally reads IEEE80211_TX_MAX_RATES entries (8 bytes) from it. The policy only bounds the attribute from above (NLA_BINARY .len is a maximum) and the op sets GENL_DONT_VALIDATE_STRICT, so a short or zero-length attribute is accepted and the loop reads past the payload. Require the exact length in the policy, so a malformed attribute is rejected before the handler runs. Signed-off-by: Ibrahim Hashimov Assisted-by: AuditCode-AI:2026.07 Link: https://patch.msgid.link/20260721115346.17236-1-security@auditcode.ai Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 956ff9b94526..75caa97becc8 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -870,9 +870,9 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 }, [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 }, [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 }, - [HWSIM_ATTR_TX_INFO] = { .type = NLA_BINARY, - .len = IEEE80211_TX_MAX_RATES * - sizeof(struct hwsim_tx_rate)}, + [HWSIM_ATTR_TX_INFO] = + NLA_POLICY_EXACT_LEN(IEEE80211_TX_MAX_RATES * + sizeof(struct hwsim_tx_rate)), [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, [HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 }, [HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 }, -- cgit v1.2.3 From b24adfed83ae3fe623d978a30d5c6c636a874821 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Thu, 9 Jul 2026 03:59:07 +0800 Subject: wifi: cfg80211: guard optional PMSR nominal time pmsr_parse_ftm() rejects a request that omits NOMINAL_TIME only for non-trigger-based PD ranging. It then reads the attribute unconditionally for every non-trigger-based request: out->ftm.nominal_time = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]); For the other non-trigger-based request types NOMINAL_TIME is optional, so tb[...] can be NULL and nla_get_u32() dereferences a NULL pointer. Keep the requirement for PD ranging and read the nominal-time value only when the attribute is present. Fixes: 8823a9b0e7af ("wifi: cfg80211: add NTB continuous ranging and FTM request type support") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5 Assisted-by: Claude:opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260708195911.84365-5-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/pmsr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 34c3625f7fd5..d1e2fae5bc0e 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -263,8 +263,9 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, "FTM: nominal time is required for PD NTB ranging"); return -EINVAL; } - out->ftm.nominal_time = - nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]); + if (tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]) + out->ftm.nominal_time = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]); if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]) out->ftm.min_time_between_measurements = -- cgit v1.2.3 From 093fbffe03f5c1bb9c10a9e5aa65b23250844403 Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Tue, 21 Jul 2026 10:23:21 +0200 Subject: MAINTAINERS: update my email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Roger Pau Monné Reviewed-by: Juergen Gross Link: https://patch.msgid.link/20260721082321.81212-1-roger@xenproject.org Signed-off-by: Jens Axboe --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 3d6db8cb608f..b88f69881035 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29134,7 +29134,7 @@ F: net/xdp/ F: tools/testing/selftests/bpf/*xsk* XEN BLOCK SUBSYSTEM -M: Roger Pau Monné +M: Roger Pau Monné L: xen-devel@lists.xenproject.org (moderated for non-subscribers) S: Supported F: drivers/block/xen* -- cgit v1.2.3 From 0c9e6367639548307d3f578f6943ce72c9d39087 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Tue, 21 Jul 2026 09:15:58 +0800 Subject: drm/vc4: Prevent shader BO mappings from becoming writable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vc4_gem_object_mmap() rejects a writable mapping of a validated shader BO, but leaves VM_MAYWRITE set. Userspace can map the BO read-only and then turn it writable with mprotect(). Validated shader BOs must stay read-only: the validator checks the instructions once and the GPU trusts them afterwards. A writable mapping lets userspace rewrite the code after validation, bypassing the validator. Clear VM_MAYWRITE on the read-only path so the mapping cannot be upgraded, as i915 already does for its read-only objects. Fixes: 463873d57014 ("drm/vc4: Add an API for creating GPU shaders in GEM BOs.") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://lore.kernel.org/dri-devel/20260720085554.B0AF01F000E9@smtp.kernel.org/ Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260721011558.1672477-1-lilinmao@kylinos.cn Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_bo.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 2161761b1f22..5e7c46dd7823 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -732,9 +732,13 @@ static int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct { struct vc4_bo *bo = to_vc4_bo(obj); - if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { - DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); - return -EINVAL; + if (bo->validated_shader) { + if (vma->vm_flags & VM_WRITE) { + DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n"); + return -EINVAL; + } + + vm_flags_clear(vma, VM_MAYWRITE); } mutex_lock(&bo->madv_lock); -- cgit v1.2.3 From 55645e4f3c6022ffb160ad3617d2b624eaa38501 Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Wed, 8 Jul 2026 18:00:01 +0800 Subject: USB: serial: option: add TDTECH MT5710-CN Add support for the TDTECH MT5710-CN (5G redcap) module based on the Huawei HiSilicon Balong chip. T: Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=3466 ProdID=3301 Rev=ff.ff S: Manufacturer=TD Tech Ltd. S: Product=TDTECH MT571X S: SerialNumber=0123456789ABCDEF C:* #Ifs= 6 Cfg#= 1 Atr=c0 MxPwr= 0mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0d Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=cdc_ncm E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=13 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=12 Driver=option E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=1c Driver=option E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=14 Driver=option E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Interface: ECM / NCM + DIAG + AT + SERIAL + GPS Signed-off-by: Chukun Pan Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 7275f4e7f569..580f06f5ce5e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2496,6 +2496,7 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */ .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x3466, 0x3301, 0xff) }, /* TDTECH MT5710-CN */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) }, -- cgit v1.2.3 From 7be2552e601c247a328a5aba6fc06ac844b94a16 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Wed, 8 Jul 2026 16:36:49 +0000 Subject: pds_core: reject component parameter in legacy firmware update The legacy firmware update path does not support per-component updates. If a user specifies a component parameter with devlink flash, reject the request with -EOPNOTSUPP rather than silently ignoring the component parameter and flashing the entire firmware image. Fixes: 49ce92fbee0b ("pds_core: add FW update feature to devlink") Signed-off-by: Nikhil P. Rao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260708163649.128620-1-nikhil.rao@amd.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/amd/pds_core/devlink.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index 2ea97e1c5939..8adae7b18898 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -90,6 +90,12 @@ int pdsc_dl_flash_update(struct devlink *dl, { struct pdsc *pdsc = devlink_priv(dl); + if (params->component) { + NL_SET_ERR_MSG_MOD(extack, + "Component update not supported by this device"); + return -EOPNOTSUPP; + } + return pdsc_firmware_update(pdsc, params->fw, extack); } -- cgit v1.2.3 From fafb66e5903c2bcfc7b7e259042a8282f18a6faa Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Mon, 13 Jul 2026 16:10:47 +0200 Subject: rtla/timerlat_top: Fix on-threshold actions firing on signal A bug was reported when rtla-timerlat-top tool performs on-threshold actions, even though no threshold was hit. This is reproduced even if no threshold is set at all: $ rtla timerlat top -q -c 0 --on-threshold shell,command='echo BAD' BAD Timer Latency ... The bug is due to incorrect logic in timerlat_top_bpf_main_loop(). The loop uses timerlat_bpf_wait(), the return values of which are: - > 0 (number of ringbuffer entries): at least 1 CPU hit threshold - = 0: time out - < 0: wait was interrupted by a signal Commit 3138df6f0cd0 ("rtla/timerlat: Exit top main loop on any non-zero wait_retval") changed the condition for "threshold hit" from "wait_reval == 1" (exactly 1 CPU hit threshold) to "wait_retval != 0", to fix a race where multiple CPUs hit the threshold at the same time. That also made it incorrectly include a signal (< 0), coming from either duration expired (SIGALRM) or user interrupt (SIGINT). Check for wait_retval greater than zero in the if condition to cover all return values correctly. Fixes: 3138df6f0cd0 ("rtla/timerlat: Exit top main loop on any non-zero wait_retval") Reported-by: Attila Fazekas Reviewed-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260713141047.687877-1-tglozar@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/timerlat_top.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 18e1071a2e24..6206a0a565ad 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -536,7 +536,7 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *tool) if (!params->quiet) timerlat_print_stats(tool); - if (wait_retval != 0) { + if (wait_retval > 0) { /* Stopping requested by tracer */ retval = common_threshold_handler(tool); if (retval) -- cgit v1.2.3 From f7074624bafc44b236ddc7159d962075c3747f2b Mon Sep 17 00:00:00 2001 From: 郑鸿源 Date: Tue, 21 Jul 2026 03:31:58 +0800 Subject: ASoC: amd: yc: Add MSI Crosshair A16 HX D7WFKG to quirk table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MSI Crosshair A16 HX D7WFKG has an internal digital microphone connected through AMD ACP6x, but the system does not expose the AcpDmicConnected ACPI property. As a result, acp_yc_mach does not bind and no DMIC capture device is created. Add a DMI quirk for this model. This was tested on a system with board MS-15PL, BIOS E15PLAMS.10B, and AMD ACP6x rev 0x62. After applying the quirk, the internal microphone appears as an acp6x DMIC capture device and records correctly. Signed-off-by: 郑鸿源 Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/20260721-master-v1-1-6f56d5a54d96@outlook.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 2f54d4e30539..0d93e45b6178 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -521,6 +521,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WHG"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Crosshair A16 HX D7WFKG"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From b877075d0baa22c225842c2f19e3ea0a9cbcbe39 Mon Sep 17 00:00:00 2001 From: Steven Price Date: Fri, 3 Jul 2026 14:48:35 +0100 Subject: arm64: Correct value returned by ESR_ELx_FSC_ADDRSZ_nL() Address size fault, level -1 is encoded as 0b101001 or 0x29 according to the Arm ARM. Correct the value to match the spec. This also matches the offset of "level -1 address size fault" in the fault_info array in fault.c. Fixes: fb8a3eba9c81 ("KVM: arm64: Only read HPFAR_EL2 when value is architecturally valid") Signed-off-by: Steven Price Reviewed-by: Marc Zyngier Signed-off-by: Will Deacon --- arch/arm64/include/asm/esr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 81c17320a588..f816f5d77f1a 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -131,7 +131,7 @@ * Annoyingly, the negative levels for Address size faults aren't laid out * contiguously (or in the desired order) */ -#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x25 : 0x2C) +#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x29 : 0x2C) #define ESR_ELx_FSC_ADDRSZ_L(n) ((n) < 0 ? ESR_ELx_FSC_ADDRSZ_nL(n) : \ (ESR_ELx_FSC_ADDRSZ + (n))) -- cgit v1.2.3 From 285f90a4d1141c7594f2368e19cbb307388eff30 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Tue, 21 Jul 2026 18:00:26 +0800 Subject: arm64/mm: Check the requested PFN range during memory removal prevent_memory_remove_notifier() advances pfn while scanning the requested range for early memory. When the loop completes, pfn is at or beyond end_pfn. Passing it to can_unmap_without_split() therefore checks a range after the one being offlined. Consequently, a valid request can be rejected based on the following range, while a request that would split a leaf mapping can be accepted if the shifted range can be unmapped without a split. This was observed with CXL DAX memory, where the final memory block was incorrectly allowed to be offlined. Pass arg->start_pfn into can_unmap_without_split() so it checks the requested range. Fixes: 95a58852b0e5 ("arm64/mm: Reject memory removal that splits a kernel leaf mapping") Signed-off-by: Richard Cheng Reviewed-by: Anshuman Khandual Signed-off-by: Will Deacon --- arch/arm64/mm/mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index a25d8beacc83..18a8b0d3714e 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -2194,7 +2194,7 @@ static int prevent_memory_remove_notifier(struct notifier_block *nb, } } - if (!can_unmap_without_split(pfn, arg->nr_pages)) + if (!can_unmap_without_split(arg->start_pfn, arg->nr_pages)) return NOTIFY_BAD; return NOTIFY_OK; -- cgit v1.2.3 From e751256486d0ded20f5a9f9863467f1dce65142f Mon Sep 17 00:00:00 2001 From: Shiming Cheng Date: Thu, 9 Jul 2026 09:46:39 +0800 Subject: net: gro: fix double aggregation of flush-marked skbs Commit 0ab03f353d36 ("net-gro: Fix GRO flush when receiving a GSO packet.") added a flush check to skb_gro_receive(), but skb_gro_receive_list() lacks the same validation. As a result, packets marked with NAPI_GRO_CB(skb)->flush may still be re-aggregated. This allows already-GRO'd packets with existing frag_list to be re-aggregated into a new GRO session, corrupting the frag_list chain structure. When skb_segment() attempts to unpack these malformed packets, it encounters invalid state and triggers a kernel panic. Scenario (Tethering/Device forwarding): 1. Driver: Generated aggregated packet P1 via LRO with frag_list 2. Dev A: Receives aggregated fraglist packet and flush flag set 3. Dev A: Re-enters GRO, skb_gro_receive_list() is called 4. Missing flush check allows re-aggregation despite flush flag 5. Frag_list chain becomes corrupted (loops or dangling refs) 6. Dev B: TX path calls skb_segment(), crashes on corrupted frag_list Root cause in skb_segment(): The check at line ~4891: if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) && (skb_headlen(list_skb) == len || sg)) { When frag_list is corrupted by double aggregation, when list_skb is a NULL pointer from skb->next, skb_headlen(list_skb) dereference NULL/corrupted pointers occurs. Call Trace: skb_headlen(NULL skb) skb_segment tcp_gso_segment tcp4_gso_segment inet_gso_segment skb_mac_gso_segment __skb_gso_segment skb_gso_segment validate_xmit_skb validate_xmit_skb_list sch_direct_xmit qdisc_restart __qdisc_run qdisc_run net_tx_action Fix: Add NAPI_GRO_CB(skb)->flush validation to the early-return check in skb_gro_receive_list(), matching the defensive programming pattern of skb_gro_receive(). Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.") Cc: stable@vger.kernel.org Signed-off-by: Shiming Cheng Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260709014704.3625-1-shiming.cheng@mediatek.com Signed-off-by: Jakub Kicinski --- net/core/gro.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/gro.c b/net/core/gro.c index 35f2f708f010..29b4d02bf519 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -229,7 +229,9 @@ done: int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb) { - if (unlikely(p->len + skb->len >= 65536)) + /* make sure to check flush flag and to not merge */ + if (unlikely(p->len + skb->len >= 65536 || + NAPI_GRO_CB(skb)->flush)) return -E2BIG; if (!pskb_may_pull(skb, skb_gro_offset(skb))) { -- cgit v1.2.3 From a4f55260f7f7d4dc4d0ee55063dfb0c457b77991 Mon Sep 17 00:00:00 2001 From: Ian Forbes Date: Tue, 23 Jun 2026 14:33:14 -0500 Subject: drm/vmwgfx: Validate vmw_surface_metadata::array_size This field comes from userspace and should be validated against specific limits depending on which Shader Model (SM) is available. Fixes: 504901dbb0b5 ("drm/vmwgfx: Refactor surface_define to use vmw_surface_metadata") Reported-by: Zero Day Initiative Cc: stable@vger.kernel.org Signed-off-by: Ian Forbes Reviewed-by: Maaz Mombasawala Signed-off-by: Zack Rusin Link: https://patch.msgid.link/20260623193314.506257-1-ian.forbes@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index b2d3927b5567..bd0563741e89 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -78,7 +78,7 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res, static int vmw_gb_surface_destroy(struct vmw_resource *res); static int vmw_gb_surface_define_internal(struct drm_device *dev, - struct drm_vmw_gb_surface_create_ext_req *req, + const struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv); static int @@ -1503,7 +1503,7 @@ int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data, */ static int vmw_gb_surface_define_internal(struct drm_device *dev, - struct drm_vmw_gb_surface_create_ext_req *req, + const struct drm_vmw_gb_surface_create_ext_req *req, struct drm_vmw_gb_surface_create_rep *rep, struct drm_file *file_priv) { @@ -1521,9 +1521,21 @@ vmw_gb_surface_define_internal(struct drm_device *dev, req->base.svga3d_flags); /* array_size must be null for non-GL3 host. */ - if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) { - VMW_DEBUG_USER("SM4 surface not supported.\n"); - return -EINVAL; + if (req->base.array_size > 0) { + if (has_sm5_context(dev_priv)) { + if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) { + VMW_DEBUG_USER("Invalid Surface Array Size.\n"); + return -EINVAL; + } + } else if (has_sm4_context(dev_priv)) { + if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) { + VMW_DEBUG_USER("Invalid Surface Array Size.\n"); + return -EINVAL; + } + } else { + VMW_DEBUG_USER("SM4+ surface not supported.\n"); + return -EINVAL; + } } if (!has_sm4_1_context(dev_priv)) { -- cgit v1.2.3 From 4bf22afe53a1de4b44b04cf677fd5199089cbdff Mon Sep 17 00:00:00 2001 From: Prashanth Kumar KR Date: Thu, 9 Jul 2026 15:20:06 +0530 Subject: amd-xgbe: fix MAC_AUTO_SW handling in CL37 AN MAC_AUTO_SW (VR_MII_DIG_CTRL1 bit 9) enables automatic XPCS speed mode switching after CL37 auto-negotiation and is only meaningful in SGMII MAC mode. The original code unconditionally set this bit on every call to xgbe_an37_set(), including when called from xgbe_an37_disable() with enable=false. This left MAC_AUTO_SW=1 after AN was disabled, causing the XPCS to autonomously switch speed from stale AN state during subsequent mode changes, breaking SGMII speed negotiation on 1G copper SFP modules. Patrick: This was breaking negotiation for all 1G SFP modules, not just copper modules. Fixes: 42fd432fe6d3 ("amd-xgbe: align CL37 AN sequence as per databook") Reported-by: Patrick Oppenlander Link: https://lore.kernel.org/netdev/CAEg67GmFS0Q4oSZkz8zWdOzckSth9_vBPiOy6a7-d697C2w2Xg@mail.gmail.com Signed-off-by: Prashanth Kumar KR Tested-by: Patrick Oppenlander Link: https://patch.msgid.link/20260709095006.3683940-1-prashanthkumar.k.r@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index fa0df6181207..12770af031eb 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -267,9 +267,14 @@ static void xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable, XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg); - reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL); - reg |= XGBE_VEND2_MAC_AUTO_SW; - XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg); + if (pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) { + reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL); + if (enable) + reg |= XGBE_VEND2_MAC_AUTO_SW; + else + reg &= ~XGBE_VEND2_MAC_AUTO_SW; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg); + } } static void xgbe_an37_restart(struct xgbe_prv_data *pdata) -- cgit v1.2.3 From 98da8ce87dd561f08fbe44f75865edc5d9b2ba5f Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 9 Jul 2026 18:31:39 +0000 Subject: soreuseport: Clear sk_reuseport_cb before failure in sk_clone(). When sk_clone() fails, sk_destruct() is called for the new socket. If the parent socket has sk->sk_reuseport_cb, the child will call reuseport_detach_sock() for the reuseport group. Let's clear sk->sk_reuseport_cb before any failure path in sk_clone(). Note that this was not a problem before the cited commit because reuseport_detach_sock() did nothing if the socket was not found in the reuseport array. Fixes: 5dc4c4b7d4e8 ("bpf: Introduce BPF_MAP_TYPE_REUSEPORT_SOCKARRAY") Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260709032007.9E4D61F000E9@smtp.kernel.org/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Willem de Bruijn Reviewed-by: Jason Xing Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260709183315.965751-2-kuniyu@google.com Signed-off-by: Jakub Kicinski --- net/core/sock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/sock.c b/net/core/sock.c index 8a59bfaa8096..fc3ff0552d68 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2544,6 +2544,8 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, cgroup_sk_clone(&newsk->sk_cgrp_data); + RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL); + rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter != NULL) @@ -2566,8 +2568,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, goto free; } - RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL); - if (bpf_sk_storage_clone(sk, newsk)) goto free; -- cgit v1.2.3 From d50557779257a00162411e3048d82971ff1f644c Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 9 Jul 2026 18:31:40 +0000 Subject: net: Call net_enable_timestamp() before failure in sk_clone(). When sk_clone() fails, sk_destruct() is called for the new socket. If the parent socket has SK_FLAGS_TIMESTAMP in sk->sk_flags, net_disable_timestamp() is called for the child socket even though net_enable_timestamp() is not called for it. Let's call net_enable_timestamp() before any failure path in sk_clone(). Fixes: 704da560c0a0 ("tcp: update the netstamp_needed counter when cloning sockets") Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260709032007.9E4D61F000E9@smtp.kernel.org/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Willem de Bruijn Reviewed-by: Jason Xing Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260709183315.965751-3-kuniyu@google.com Signed-off-by: Jakub Kicinski --- net/core/sock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/core/sock.c b/net/core/sock.c index fc3ff0552d68..504d82a3aacd 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2546,6 +2546,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL); + if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP) + net_enable_timestamp(); + rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter != NULL) @@ -2595,9 +2598,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, if (newsk->sk_prot->sockets_allocated) sk_sockets_allocated_inc(newsk); - - if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP) - net_enable_timestamp(); out: return newsk; free: -- cgit v1.2.3 From 29ab31f3f27157648f2f7e6d5e1fd9792fdf0614 Mon Sep 17 00:00:00 2001 From: LiangCheng Wang Date: Wed, 15 Jul 2026 14:49:38 +0800 Subject: wifi: brcmfmac: set F2 blocksize to 256 for BCM43752 The BCM43752 is not reliable with the default 512-byte SDIO function 2 block size: on an i.MX8MP board with an AMPAK AP6275S module at SDR104 / 200 MHz, an iperf TX stress test kills WLAN within seconds: mmc_submit_one: CMD53 sg block write failed -84 brcmf_sdio_dpc: failed backplane access over SDIO, halting operation Commit d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") set up the 43752 like the 4373 for the F2 watermark but missed the F2 block size, which the 4373 limits to 256 bytes. The vendor driver (bcmdhd) also programs a 256-byte F2 block size for this chip and runs the same hardware without errors. Group the 43752 with the 4373, matching the F2 watermark handling. With this change a 10-minute bidirectional iperf3 soak completes with zero SDIO errors at ~270 Mbit/s in each direction. Backporting note: kernels before v6.18 name this id SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, so on those trees the case label added by this patch must be adjusted to that name. Cherry-picking the rename commit 74e2ef72bd4b ("wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW)") first is not a clean alternative: on trees before v6.17 its context collides with the 43751 additions, and trees before v6.2 lack the FWVID framework it touches. Fixes: d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") Cc: stable@vger.kernel.org # see patch description, needs adjustments for <= 6.17 Signed-off-by: LiangCheng Wang Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260715-b43752-f2-blksz-v2-1-f9be49856050@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index ec487c6f2e38..869c4872d399 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -911,6 +911,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) return ret; } switch (sdiodev->func2->device) { + case SDIO_DEVICE_ID_BROADCOM_43752: case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373: f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE; break; -- cgit v1.2.3 From 75952cfc7752c52a2b692b59d34ce160d3edabb2 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 16 Jul 2026 21:45:27 +0100 Subject: arm64: dts: broadcom: bcm2712: Remove non-functional EL2 virtual timer Commit d87773de9efe1 ("clocksource/drivers/arm_arch_timer: Default to EL2 virtual timer when running VHE") causes boot to hang on Raspberry Pi 5. The newly-selected EL2 virtual timer does not generate any interrupts, even though the GIC_DIST_ENABLE_SET flag has been confirmed set via readback. It is highly unusual that this timer interrupt is non-operational because this is a standard GIC interrupt corresponding to a standard Cortex-A76 CPU timer. However, Broadcom have confirmed for this SoC: > the interrupt line was never connected in the first place as this was > not identified as being a requirement Remove the corresponding DeviceTree entry. Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/all/ea15cce1-b393-43f6-8d58-3d6f90f0c0cd@samsung.com/ Fixes: faa3381267d0 ("arm64: dts: broadcom: Add minimal support for Raspberry Pi 5") Signed-off-by: Daniel Drake Link: https://lore.kernel.org/r/20260716-bcm2712-el2-v2-1-e708f7fb42fa@reactivated.net Signed-off-by: Florian Fainelli --- arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi index 761c59d90ffc..09ff5e9959d3 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi @@ -678,8 +678,6 @@ , , - ; }; -- cgit v1.2.3 From 751bfa982b4a6de8275a552804f6971adcf08473 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Thu, 9 Jul 2026 14:48:00 +0200 Subject: geneve: fix hint header definition wrt endianness Bitfields are packed differently depending on the endianness, take it into account in the GRO hint header definition. Fixes: e0a12cbf262b ("geneve: add GRO hint output path") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260529144713.780938-1-atenart%40kernel.org Signed-off-by: Antoine Tenart Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260709124801.140632-1-atenart@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/geneve.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 396e1a113cd4..bed1c9713e0a 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -43,8 +43,17 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); #define GENEVE_OPT_GRO_HINT_LEN 1 struct geneve_opt_gro_hint { +#if defined(__LITTLE_ENDIAN_BITFIELD) u8 inner_proto_id:2, - nested_is_v6:1; + nested_is_v6:1, + rsvd:5; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 rsvd:5, + nested_is_v6:1, + inner_proto_id:2; +#else +#error "Please fix " +#endif u8 nested_nh_offset; u8 nested_tp_offset; u8 nested_hdr_len; -- cgit v1.2.3 From 447ec540233c60d6af4d68a164a5bc8ce7e975c1 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Thu, 9 Jul 2026 14:50:00 +0200 Subject: geneve: ensure the skb is writable before fixing its headers Make sure the IPv4/6 and UDP headers are writable before fixing them up in geneve_post_decap_hint. As skb_ensure_writable can reallocate the skb linear area, reload the GRO hint header pointer and only set the IPv4/6 header ones after the call. Fixes: fd0dd796576e ("geneve: use GRO hint option in the RX path") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260529144713.780938-1-atenart%40kernel.org Signed-off-by: Antoine Tenart Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260709125000.141092-1-atenart@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/geneve.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index bed1c9713e0a..bb275b30d3e5 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -586,6 +586,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, struct iphdr *iph; struct udphdr *uh; __be16 p; + int err; hint_off = geneve_sk_gro_hint_off(sk, *geneveh, &p, &len); if (!hint_off) @@ -610,12 +611,20 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, !geneve_opt_gro_hint_validate(skb->data, gro_hint))) return -EINVAL; - ipv6h = (void *)skb->data + gro_hint->nested_nh_offset; - iph = (struct iphdr *)ipv6h; total_len = skb->len - gro_hint->nested_nh_offset; if (total_len >= GRO_LEGACY_MAX_SIZE) return -E2BIG; + err = skb_ensure_writable(skb, gro_hint->nested_tp_offset + sizeof(*uh)); + if (unlikely(err)) + return err; + + *geneveh = geneve_hdr(skb); + gro_hint = geneve_opt_gro_hint(*geneveh, hint_off); + + ipv6h = (void *)skb->data + gro_hint->nested_nh_offset; + iph = (struct iphdr *)ipv6h; + /* * After stripping the outer encap, the packet still carries a * tunnel encapsulation: the nested one. -- cgit v1.2.3 From b2ff91b752b0d85e8815e7f44fd85205c4268094 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 9 Jul 2026 15:19:25 -0400 Subject: mptcp: only set DATA_FIN when a mapping is present mptcp_get_options() clears only the status group of struct mptcp_options_received; data_seq, subflow_seq and data_len are filled in by mptcp_parse_option() exclusively inside the DSS mapping block, which runs only when the DSS M (mapping present) bit is set. A peer can send a DSS option with the DATA_FIN flag set but the mapping bit clear. The parser then records mp_opt->data_fin while leaving data_len and data_seq uninitialized. For a zero-length segment mptcp_incoming_options() evaluates if (mp_opt.data_fin && mp_opt.data_len == 1 && mptcp_update_rcv_data_fin(msk, mp_opt.data_seq, mp_opt.dsn64)) which reads the uninitialized data_len and data_seq; KMSAN reports an uninit-value in mptcp_incoming_options(). The stale data_seq can also be fed into the receive-side DATA_FIN sequence tracking. Record the DATA_FIN flag only when the DSS option carries a mapping, so data_fin is never set without data_seq and data_len also being present. data_fin is part of the status group that mptcp_get_options() clears up front, so on the no-map path it stays zero and the zero-length DATA_FIN branch is simply skipped. A DATA_FIN is always transmitted together with a mapping (mptcp_write_data_fin() sets use_map along with data_seq and data_len), so legitimate DATA_FIN handling is unaffected. Move the pr_debug() that logs the parsed DSS flags below the mapping block, so it reports the final data_fin value instead of the stale one it would otherwise print before the assignment. Fixes: 43b54c6ee382 ("mptcp: Use full MPTCP-level disconnect state machine") Suggested-by: Paolo Abeni Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Reviewed-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260709191925.2811195-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index dff3fd5d3b55..1b74ca5b6a59 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -157,17 +157,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, ptr++; flags = (*ptr++) & MPTCP_DSS_FLAG_MASK; - mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0; mp_opt->dsn64 = (flags & MPTCP_DSS_DSN64) != 0; mp_opt->use_map = (flags & MPTCP_DSS_HAS_MAP) != 0; mp_opt->ack64 = (flags & MPTCP_DSS_ACK64) != 0; mp_opt->use_ack = (flags & MPTCP_DSS_HAS_ACK); - pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n", - mp_opt->data_fin, mp_opt->dsn64, - mp_opt->use_map, mp_opt->ack64, - mp_opt->use_ack); - expected_opsize = TCPOLEN_MPTCP_DSS_BASE; if (mp_opt->use_ack) { @@ -178,12 +172,18 @@ static void mptcp_parse_option(const struct sk_buff *skb, } if (mp_opt->use_map) { + mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0; if (mp_opt->dsn64) expected_opsize += TCPOLEN_MPTCP_DSS_MAP64; else expected_opsize += TCPOLEN_MPTCP_DSS_MAP32; } + pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n", + mp_opt->data_fin, mp_opt->dsn64, + mp_opt->use_map, mp_opt->ack64, + mp_opt->use_ack); + /* Always parse any csum presence combination, we will enforce * RFC 8684 Section 3.3.0 checks later in subflow_data_ready */ -- cgit v1.2.3 From 1c50efa1faf3a1a96e100b07ec7a2f3164d90bee Mon Sep 17 00:00:00 2001 From: Justin Lai Date: Thu, 9 Jul 2026 18:34:56 +0800 Subject: rtase: Workaround for TX hang caused by hardware packet parsing The hardware performs packet parsing before packet transmission. Parsing incomplete IPv4, IPv6, TCP, or UDP headers may trigger a TX hang because the hardware parser expects additional protocol header data that is not present in the packet. The hardware performs additional PTP parsing on UDP packets identified by destination ports 319/320 at the expected UDP destination port offset. If such a packet has transport data smaller than RTASE_MIN_PAD_LEN, the hardware parser expects additional packet data and may trigger a TX hang. To avoid these hardware issues, the driver applies the following workarounds. Drop malformed packets that may trigger this hardware issue before transmission. For IPv4 non-initial fragments, the hardware does not check the fragment offset before parsing the expected transport header location. As a result, these packets are still subject to transport header parsing even though they do not contain a transport header. If the transport data is shorter than the minimum transport header required by the hardware parser, pad the transport data to the minimum transport header length required by the hardware parser. Packets that also match the hardware PTP parsing conditions continue to follow the corresponding workaround. For IPv6 fragmented packets, neither of the above hardware issues occurs because the hardware only continues packet parsing when the IPv6 Base Header Next Header field directly indicates UDP. Packets carrying a Fragment Header do not continue through the subsequent packet parsing stages. For packets identified for hardware PTP parsing, pad the transport data so it reaches RTASE_MIN_PAD_LEN before transmission. Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function") Cc: stable@vger.kernel.org Signed-off-by: Justin Lai Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260709103456.83789-1-justinlai0215@realtek.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/realtek/rtase/rtase.h | 8 + drivers/net/ethernet/realtek/rtase/rtase_main.c | 197 ++++++++++++++++++++++++ 2 files changed, 205 insertions(+) diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h index 9bd6872474c1..03b12d83f6e9 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase.h +++ b/drivers/net/ethernet/realtek/rtase/rtase.h @@ -192,6 +192,12 @@ enum rtase_sw_flag_content { RTASE_SWF_MSIX_ENABLED = BIT(2), }; +enum rtase_parse_result { + RTASE_PARSE_OK, + RTASE_PARSE_SKIP, + RTASE_PARSE_DROP, +}; + #define RSVD_MASK 0x3FFFC000 struct rtase_tx_desc { @@ -363,4 +369,6 @@ struct rtase_private { #define RTASE_MSS_MASK GENMASK(28, 18) +#define RTASE_MIN_PAD_LEN 47 + #endif /* RTASE_H */ diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index 255667775f0e..4168ad9e48ea 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -1252,6 +1253,199 @@ static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev) return csum_cmd; } +static enum rtase_parse_result rtase_get_l3_proto(struct sk_buff *skb, + __be16 *proto, + u32 *network_offset) +{ + struct vlan_hdr *vh, _vh; + struct ethhdr *eh, _eh; + u32 offset = ETH_HLEN; + + eh = skb_header_pointer(skb, 0, sizeof(_eh), &_eh); + if (!eh) + return RTASE_PARSE_DROP; + + *proto = eh->h_proto; + + while (eth_type_vlan(*proto)) { + vh = skb_header_pointer(skb, offset, sizeof(_vh), &_vh); + if (!vh) + return RTASE_PARSE_DROP; + + *proto = vh->h_vlan_encapsulated_proto; + offset += VLAN_HLEN; + } + + *network_offset = offset; + + return RTASE_PARSE_OK; +} + +static bool rtase_pad_to_transport_len(struct sk_buff *skb, + u32 transport_offset, + u32 pad_to_len) +{ + u32 trans_data_len; + u32 pad_len; + + trans_data_len = skb->len - transport_offset; + if (trans_data_len >= pad_to_len) + return true; + + if (skb_is_nonlinear(skb)) { + if (skb_linearize(skb)) + return false; + } + + pad_len = pad_to_len - trans_data_len; + if (__skb_put_padto(skb, skb->len + pad_len, false)) + return false; + + return true; +} + +static enum rtase_parse_result rtase_get_transport_offset(struct sk_buff *skb, + u32 *transport_offset, + u8 *transport_proto, + u32 *pad_to_len) +{ + enum rtase_parse_result ret; + struct ipv6hdr *i6h, _i6h; + struct iphdr *ih, _ih; + bool non_first_frag; + __be16 proto; + u32 offset; + u32 no; + + ret = rtase_get_l3_proto(skb, &proto, &no); + if (ret != RTASE_PARSE_OK) + return ret; + + switch (proto) { + case htons(ETH_P_IP): + ih = skb_header_pointer(skb, no, sizeof(_ih), &_ih); + if (!ih) + return RTASE_PARSE_DROP; + + if (ih->ihl < 5) + return RTASE_PARSE_DROP; + + offset = no + ih->ihl * 4; + if (offset > skb->len) + return RTASE_PARSE_DROP; + + non_first_frag = ntohs(ih->frag_off) & IP_OFFSET; + + if (ih->protocol == IPPROTO_TCP) { + if (skb->len - offset < sizeof(struct tcphdr)) { + if (non_first_frag) { + *transport_offset = offset; + *transport_proto = IPPROTO_TCP; + *pad_to_len = sizeof(struct tcphdr); + + return RTASE_PARSE_OK; + } + + return RTASE_PARSE_DROP; + } + + return RTASE_PARSE_SKIP; + } + + if (ih->protocol != IPPROTO_UDP) + return RTASE_PARSE_SKIP; + + *transport_offset = offset; + *transport_proto = IPPROTO_UDP; + + if (skb->len - offset < sizeof(struct udphdr)) { + if (non_first_frag) { + *pad_to_len = sizeof(struct udphdr); + + return RTASE_PARSE_OK; + } + + return RTASE_PARSE_DROP; + } + + return RTASE_PARSE_OK; + + case htons(ETH_P_IPV6): + i6h = skb_header_pointer(skb, no, sizeof(_i6h), &_i6h); + if (!i6h) + return RTASE_PARSE_DROP; + + offset = no + sizeof(*i6h); + + if (i6h->nexthdr == IPPROTO_TCP) { + if (skb->len - offset < sizeof(struct tcphdr)) + return RTASE_PARSE_DROP; + + return RTASE_PARSE_SKIP; + } + + if (i6h->nexthdr != IPPROTO_UDP) + return RTASE_PARSE_SKIP; + + if (skb->len - offset < sizeof(struct udphdr)) + return RTASE_PARSE_DROP; + + *transport_offset = offset; + *transport_proto = IPPROTO_UDP; + + return RTASE_PARSE_OK; + + default: + return RTASE_PARSE_SKIP; + } +} + +static bool rtase_skb_pad(struct sk_buff *skb) +{ + enum rtase_parse_result ret; + u32 transport_offset; + __be16 *dest, _dest; + u32 trans_data_len; + u32 pad_to_len = 0; + u8 transport_proto; + u16 dest_port; + + ret = rtase_get_transport_offset(skb, &transport_offset, + &transport_proto, &pad_to_len); + if (ret == RTASE_PARSE_SKIP) { + return true; + } else if (ret == RTASE_PARSE_DROP) { + netdev_dbg(skb->dev, "drop malformed packet\n"); + return false; + } + + if (pad_to_len && + !rtase_pad_to_transport_len(skb, transport_offset, pad_to_len)) + return false; + + if (transport_proto != IPPROTO_UDP) + return true; + + trans_data_len = skb->len - transport_offset; + if (trans_data_len < offsetof(struct udphdr, len) || + trans_data_len >= RTASE_MIN_PAD_LEN) + return true; + + dest = skb_header_pointer(skb, + transport_offset + + offsetof(struct udphdr, dest), + sizeof(_dest), &_dest); + if (!dest) + return true; + + dest_port = ntohs(*dest); + if (dest_port != PTP_EV_PORT && dest_port != PTP_GEN_PORT) + return true; + + return rtase_pad_to_transport_len(skb, transport_offset, + RTASE_MIN_PAD_LEN); +} + static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb, u32 opts1, u32 opts2) { @@ -1365,6 +1559,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, opts2 |= rtase_tx_csum(skb, dev); } + if (!rtase_skb_pad(skb)) + goto err_dma_0; + frags = rtase_xmit_frags(ring, skb, opts1, opts2); if (unlikely(frags < 0)) goto err_dma_0; -- cgit v1.2.3 From 350e592ff4e30e48ffb55e142d11a73e63f4869c Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 15 Jul 2026 19:52:04 -0700 Subject: tipc: serialize udp bearer replicast list updates tipc_udp_rcast_add() and cleanup_bearer() both update ub->rcast.list with list_add_rcu() / list_del_rcu(), but nothing serializes them. The add runs from the encap receive softirq (via tipc_udp_rcast_disc()) without rtnl_lock(), so it can race the cleanup delete and corrupt the list: list_del corruption. prev->next should be ffff8880298d7ab8, but was ffff88802449ad38. (prev=ffff888027e3ec98) kernel BUG at lib/list_debug.c:62! RIP: __list_del_entry_valid_or_report+0x17a/0x200 Workqueue: events cleanup_bearer Call Trace: cleanup_bearer (net/tipc/udp_media.c:811) process_one_work (kernel/workqueue.c:3302) worker_thread (kernel/workqueue.c:3466) The bearer can be enabled from an unprivileged user namespace, as the TIPCv2 generic-netlink ops carry no GENL_ADMIN_PERM. Add a spinlock to struct udp_bearer and take it around the list_add_rcu() in tipc_udp_rcast_add() and the list_del_rcu() loop in cleanup_bearer() so the two writers can no longer corrupt the list. Reject a duplicate peer under the same lock before allocating, and remove tipc_udp_is_known_peer(). The old lockless pre-check in tipc_udp_rcast_disc() was racy: two softirqs discovering the same peer could both find it absent and add it twice. cleanup_bearer() runs from a workqueue after tipc_udp_disable() clears the bearer's up bit, so an encap softirq can still reach tipc_udp_rcast_add() and add a peer after cleanup_bearer() has already emptied the list, leaking that entry when the bearer is freed. Mark the bearer disabled under rcast_lock once the list is emptied and refuse further additions. Fixes: ef20cd4dd163 ("tipc: introduce UDP replicast") Reported-by: Xiang Mei Suggested-by: Tung Nguyen Signed-off-by: Weiming Shi Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260716025203.9332-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/tipc/udp_media.c | 56 +++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 62ae7f5b5840..230645cc01c9 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -94,6 +94,8 @@ struct udp_replicast { * @ifindex: local address scope * @work: used to schedule deferred work on a bearer * @rcast: associated udp_replicast container + * @rcast_lock: serialize updates to @rcast.list against concurrent updaters + * @disabled: bearer is being torn down; reject further @rcast.list additions */ struct udp_bearer { struct tipc_bearer __rcu *bearer; @@ -101,6 +103,8 @@ struct udp_bearer { u32 ifindex; struct work_struct work; struct udp_replicast rcast; + spinlock_t rcast_lock; + bool disabled; }; static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr) @@ -278,26 +282,6 @@ out: return err; } -static bool tipc_udp_is_known_peer(struct tipc_bearer *b, - struct udp_media_addr *addr) -{ - struct udp_replicast *rcast, *tmp; - struct udp_bearer *ub; - - ub = rcu_dereference_rtnl(b->media_ptr); - if (!ub) { - pr_err_ratelimited("UDP bearer instance not found\n"); - return false; - } - - list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) { - if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr))) - return true; - } - - return false; -} - static int tipc_udp_rcast_add(struct tipc_bearer *b, struct udp_media_addr *addr) { @@ -308,16 +292,34 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b, if (!ub) return -ENODEV; + spin_lock_bh(&ub->rcast_lock); + if (ub->disabled) { + spin_unlock_bh(&ub->rcast_lock); + return 0; + } + list_for_each_entry(rcast, &ub->rcast.list, list) { + if (!memcmp(&rcast->addr, addr, sizeof(*addr))) { + spin_unlock_bh(&ub->rcast_lock); + return 0; + } + } + rcast = kmalloc_obj(*rcast, GFP_ATOMIC); - if (!rcast) + if (!rcast) { + spin_unlock_bh(&ub->rcast_lock); return -ENOMEM; + } if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) { + spin_unlock_bh(&ub->rcast_lock); kfree(rcast); return -ENOMEM; } memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr)); + list_add_rcu(&rcast->list, &ub->rcast.list); + b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT; + spin_unlock_bh(&ub->rcast_lock); if (ntohs(addr->proto) == ETH_P_IP) pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4); @@ -325,8 +327,6 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b, else if (ntohs(addr->proto) == ETH_P_IPV6) pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6); #endif - b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT; - list_add_rcu(&rcast->list, &ub->rcast.list); return 0; } @@ -361,9 +361,6 @@ static int tipc_udp_rcast_disc(struct tipc_bearer *b, struct sk_buff *skb) return 0; } - if (likely(tipc_udp_is_known_peer(b, &src))) - return 0; - return tipc_udp_rcast_add(b, &src); } @@ -644,9 +641,6 @@ int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr) return -EINVAL; } - if (tipc_udp_is_known_peer(b, &addr)) - return 0; - return tipc_udp_rcast_add(b, &addr); } @@ -679,6 +673,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, return -ENOMEM; INIT_LIST_HEAD(&ub->rcast.list); + spin_lock_init(&ub->rcast_lock); if (!attrs[TIPC_NLA_BEARER_UDP_OPTS]) goto err; @@ -819,10 +814,13 @@ static void cleanup_bearer(struct work_struct *work) struct udp_replicast *rcast, *tmp; struct tipc_net *tn; + spin_lock_bh(&ub->rcast_lock); list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) { list_del_rcu(&rcast->list); call_rcu_hurry(&rcast->rcu, rcast_free_rcu); } + ub->disabled = true; + spin_unlock_bh(&ub->rcast_lock); tn = tipc_net(sock_net(ub->sk)); -- cgit v1.2.3 From 043c1f6d84f6cd5d23ddd508ce5209cf0a3a3f41 Mon Sep 17 00:00:00 2001 From: Alice Mikityanska Date: Sat, 11 Jul 2026 02:43:19 +0300 Subject: mailmap: update entry for Alice Mikityanska Map all my corporate and old emails and update my name. Signed-off-by: Alice Mikityanska Link: https://patch.msgid.link/20260710234319.328687-1-alice.kernel@fastmail.im Signed-off-by: Jakub Kicinski --- .mailmap | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 735470e21075..bc28ffdc9a77 100644 --- a/.mailmap +++ b/.mailmap @@ -66,6 +66,11 @@ Alex Hung Alex Shi Alex Shi Alex Shi +Alice Mikityanska +Alice Mikityanska +Alice Mikityanska +Alice Mikityanska +Alice Mikityanska Aloka Dixit Al Viro Al Viro @@ -585,8 +590,6 @@ Mauro Carvalho Chehab Mauro Carvalho Chehab Mauro Carvalho Chehab Mauro Carvalho Chehab -Maxim Mikityanskiy -Maxim Mikityanskiy Maxime Ripard Maxime Ripard Maxime Ripard -- cgit v1.2.3 From 6a905a71fd43ce8b45f05044b11491337f232c9d Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Mon, 13 Jul 2026 16:51:11 +0800 Subject: net: txgbe: fix heap overflow when reading module EEPROM txgbe_read_eeprom_hostif() always copies round_up(length, 4) bytes into the caller buffer, which ethtool allocates with exactly 'length' bytes. A non-4-aligned length therefore causes an out-of-bounds write. Copy only the remaining bytes on the final dword instead. Signed-off-by: Chenguang Zhao Reviewed-by: Jiawen Wu Reviewed-by: Jacob Keller Fixes: 9b97b6b5635b ("net: txgbe: support getting module EEPROM by page") Link: https://patch.msgid.link/20260713085111.1481884-1-chenguang.zhao@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index affea1a364ef..26d0cfc58ee2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -96,11 +96,13 @@ int txgbe_read_eeprom_hostif(struct wx *wx, dword_len = round_up(length, 4) >> 2; for (i = 0; i < dword_len; i++) { + u32 copy_len = min_t(u32, 4, length - i * 4); + value = rd32a(wx, WX_FW2SW_MBOX, i + offset); le32_to_cpus(&value); - memcpy(data, &value, 4); - data += 4; + memcpy(data, &value, copy_len); + data += copy_len; } return 0; -- cgit v1.2.3 From 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 Mon Sep 17 00:00:00 2001 From: Ilia Gavrilov Date: Thu, 9 Jul 2026 16:27:54 +0000 Subject: rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst is never initialized because inet6_init() exits before addrconf_init() is called to initialize it. An attempt to bind an RDS socket to an ipv6 address results in a crash in __ipv6_chk_addr_and_flags() KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 Call Trace: ipv6_chk_addr+0x3b/0x50 rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] rds_trans_get_preferred+0x15d/0x2d0 [rds] ? trace_hardirqs_on+0x2d/0x110 rds_bind+0x1433/0x1d60 [rds] ? rds_remove_bound+0xd50/0xd50 [rds] ? aa_af_perm+0x250/0x250 ? __might_fault+0xde/0x190 ? __sys_bind+0x1dc/0x210 __sys_bind+0x1dc/0x210 ? __ia32_sys_socketpair+0x100/0x100 ? restore_fpregs_from_fpstate+0x53/0x100 __x64_sys_bind+0x73/0xb0 ? syscall_enter_from_user_mode+0x1c/0x50 do_syscall_64+0x34/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 RIP: 0033:0x7f47f8269ea9 The following code reproduces the issue: struct sockaddr_in6 addr; s = socket(PF_RDS, SOCK_SEQPACKET, 0); memset(&addr, 0, sizeof(addr)); inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr); addr.sin6_family = AF_INET6; addr.sin6_port = htons(PORT); bind(s, &addr, sizeof(addr)); Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") Fixes: 1e2b44e78eea ("rds: Enable RDS IPv6 support") Signed-off-by: Ilia Gavrilov Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260709162723.367523-1-Ilia.Gavrilov@infotecs.ru Signed-off-by: Jakub Kicinski --- net/rds/ib.c | 4 ++++ net/rds/ib_cm.c | 4 ++++ net/rds/tcp.c | 8 +++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/net/rds/ib.c b/net/rds/ib.c index 39f87272e071..8f9cf491984f 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -429,6 +429,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, sa = (struct sockaddr *)&sin; } else { #if IS_ENABLED(CONFIG_IPV6) + if (!ipv6_mod_enabled()) { + ret = -EADDRNOTAVAIL; + goto out; + } memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr; diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 5667f0173b47..d46146887ba4 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, dp = event->param.conn.private_data; if (isv6) { #if IS_ENABLED(CONFIG_IPV6) + if (!ipv6_mod_enabled()) { + err = -EOPNOTSUPP; + goto out; + } dp_cmn = &dp->ricp_v6.dp_cmn; saddr6 = &dp->ricp_v6.dp_saddr; daddr6 = &dp->ricp_v6.dp_daddr; diff --git a/net/rds/tcp.c b/net/rds/tcp.c index a1de114d5e2e..955d92277d5a 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -366,9 +366,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, rcu_read_unlock(); } #if IS_ENABLED(CONFIG_IPV6) - ret = ipv6_chk_addr(net, addr, dev, 0); - if (ret) - return 0; + if (ipv6_mod_enabled()) { + ret = ipv6_chk_addr(net, addr, dev, 0); + if (ret) + return 0; + } #endif return -EADDRNOTAVAIL; } -- cgit v1.2.3 From ecaa37826340520664a4e5522f803ff48fc3f564 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Mon, 13 Jul 2026 17:19:11 +0800 Subject: net: txgbe: fix FDIR filter leak on remove Perfect FDIR filters can be added while the interface is down and are kept on the software list for later restore. unregister_netdev() only calls ndo_stop when the device is up, so txgbe_fdir_filter_exit() in txgbe_close() is skipped in that case and the filters are leaked on driver remove. Free the filter list from txgbe_remove() as well. Fixes: 4bdb441105dc ("net: txgbe: support Flow Director perfect filters") Signed-off-by: Chenguang Zhao Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260713091911.1614795-1-chenguang.zhao@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 20c5a295c6c2..c277863baf67 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -945,6 +945,7 @@ static void txgbe_remove(struct pci_dev *pdev) netdev = wx->netdev; wx_disable_sriov(wx); unregister_netdev(netdev); + txgbe_fdir_filter_exit(wx); timer_shutdown_sync(&wx->service_timer); cancel_work_sync(&wx->service_task); -- cgit v1.2.3 From ff04b26794a16a8a879eb4fd2c02c2d6b03850e9 Mon Sep 17 00:00:00 2001 From: HanQuan Date: Mon, 13 Jul 2026 03:20:21 +0000 Subject: sctp: fix auth_chunk_list capacity check in sctp_auth_ep_add_chunkid sctp_auth_ep_add_chunkid() uses SCTP_NUM_CHUNK_TYPES (20) as the capacity limit for ep->auth_chunk_list, allowing it to hold up to 20 chunk entries (param_hdr.length up to 24). However, the copy destination asoc->c.auth_chunks in struct sctp_cookie is only SCTP_AUTH_MAX_CHUNKS (16) entries (20 bytes). When more than 16 chunks are added, sctp_association_init() memcpy overflows the destination by up to 4 bytes. Fix by using SCTP_AUTH_MAX_CHUNKS as the capacity limit, matching the destination capacity. Fixes: 1f485649f529 ("[SCTP]: Implement SCTP-AUTH internals") Signed-off-by: HanQuan Acked-by: Xin Long Link: https://patch.msgid.link/20260713032021.3491702-1-zhoujian.zja@antgroup.com Signed-off-by: Jakub Kicinski --- net/sctp/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/auth.c b/net/sctp/auth.c index be9782760f50..c901d373af80 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -672,7 +672,7 @@ int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id) /* Check if we can add this chunk to the array */ param_len = ntohs(p->param_hdr.length); nchunks = param_len - sizeof(struct sctp_paramhdr); - if (nchunks == SCTP_NUM_CHUNK_TYPES) + if (nchunks == SCTP_AUTH_MAX_CHUNKS) return -EINVAL; p->chunks[nchunks] = chunk_id; -- cgit v1.2.3 From defd52c1eab2926d874fa95408187dac6f890ff1 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Mon, 13 Jul 2026 00:51:03 +0200 Subject: mailmap: fix wrong canonical name for mgr@kernel.org After picking up some pending patches for the kernel to work on, I realized my name in the mailmap file somehow got mixed up. When switching to my kernel.org Address some time ago, I had never the intention to use a scrambled variant of Polish and German used for my first name to be found in this file. However, so here we are. Lets fix it for good. Signed-off-by: Michael Grzeschik Link: https://patch.msgid.link/20260713-mailmap-v1-1-cb40979cb190@kernel.org Signed-off-by: Jakub Kicinski --- .mailmap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index bc28ffdc9a77..45b133c19eb5 100644 --- a/.mailmap +++ b/.mailmap @@ -598,8 +598,8 @@ Mayuresh Janorkar Md Sadre Alam Miaoqing Pan Michael Buesch -Michal Grzeschik -Michal Grzeschik +Michael Grzeschik +Michael Grzeschik Michael Riesch Michal Simek Michel Dänzer -- cgit v1.2.3 From ab0eec0ff0a421737a37f510ceab5c6ea59cd05a Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 18:02:22 +0000 Subject: pds_core: fix deadlock between reset thread and remove pci_reset_function() acquires device_lock before performing the reset. pdsc_remove() is called by the PCI core with device_lock already held. If pdsc_pci_reset_thread() is running when pdsc_remove() is called, destroy_workqueue() will block waiting for the work to complete, while the work is blocked waiting for device_lock - deadlock. Use pci_try_reset_function() which uses pci_dev_trylock() internally. This acquires both the device lock and the PCI config access lock without blocking - if either lock is contended, it returns -EAGAIN immediately. This avoids the deadlock while also ensuring proper config space access serialization during the reset. The pci_dev_get/put calls are also removed as they were unnecessary - the driver-owned workqueue is destroyed in pdsc_remove(), guaranteeing the work completes before remove returns. The PCI core holds its reference to pci_dev throughout the entire unbind sequence. Fixes: 81665adf25d2 ("pds_core: Fix pdsc_check_pci_health function to use work thread") Reported-by: sashiko-bot Closes: https://patchwork.kernel.org/comment/27002369/ Signed-off-by: Nikhil P. Rao Reviewed-by: Harshitha Ramamurthy Link: https://patch.msgid.link/20260714180223.1642792-2-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 38a2446571af..1074a022a52f 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -606,9 +606,10 @@ void pdsc_pci_reset_thread(struct work_struct *work) struct pdsc *pdsc = container_of(work, struct pdsc, pci_reset_work); struct pci_dev *pdev = pdsc->pdev; - pci_dev_get(pdev); - pci_reset_function(pdev); - pci_dev_put(pdev); + /* Use try variant to avoid deadlock with pdsc_remove(). + * If lock is contended, the watchdog timer will retry. + */ + pci_try_reset_function(pdev); } static void pdsc_check_pci_health(struct pdsc *pdsc) -- cgit v1.2.3 From 0ad134881508c36b65c1a8864f8bec53adbd3327 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 18:02:23 +0000 Subject: pds_core: fix use-after-free on workqueue during remove In pdsc_remove(), the workqueue is destroyed before pdsc_teardown() is called. This ordering allows two paths to queue work on the destroyed workqueue: 1. If pdsc_teardown() -> pdsc_devcmd_reset() times out, the error path in pdsc_devcmd_locked() queues health_work. 2. A NotifyQ event can trigger the ISR and queue work before free_irq() is called in pdsc_teardown(). Fix by moving destroy_workqueue() after pdsc_teardown() so the workqueue outlives every queuer; destroy_workqueue() then flushes any work still pending. Draining the queued work also requires ordering the teardown so the resources that work touches are freed last: - In pdsc_qcq_free(), after freeing the interrupt, cancel_work_sync() the queue's work and only then clear qcq->intx, so pdsc_process_adminq()'s read of qcq->intx for interrupt-credit return cannot race with the clear. - Free adminqcq before notifyqcq: the shared adminq ISR is released when adminqcq is freed, and the adminq work accesses notifyqcq, so both must be stopped before notifyqcq is freed. Fixes: 01ba61b55b20 ("pds_core: Add adminq processing and commands") Reported-by: sashiko-bot Closes: https://patchwork.kernel.org/comment/27002369/ Signed-off-by: Nikhil P. Rao Link: https://patch.msgid.link/20260714180223.1642792-3-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/core.c | 14 ++++++++++---- drivers/net/ethernet/amd/pds_core/main.c | 5 +++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 1074a022a52f..e39b2c9beb20 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -110,7 +110,6 @@ static void pdsc_qcq_intr_free(struct pdsc *pdsc, struct pdsc_qcq *qcq) return; pdsc_intr_free(pdsc, qcq->intx); - qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED; } static int pdsc_qcq_intr_alloc(struct pdsc *pdsc, struct pdsc_qcq *qcq) @@ -145,6 +144,12 @@ void pdsc_qcq_free(struct pdsc *pdsc, struct pdsc_qcq *qcq) pdsc_qcq_intr_free(pdsc, qcq); + /* Drain any work queued by ISR before it was freed above */ + if (qcq->work.func) + cancel_work_sync(&qcq->work); + + qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED; + if (qcq->q_base) dma_free_coherent(dev, qcq->q_size, qcq->q_base, qcq->q_base_pa); @@ -304,8 +309,11 @@ err_out: static void pdsc_core_uninit(struct pdsc *pdsc) { - pdsc_qcq_free(pdsc, &pdsc->notifyqcq); + /* Free adminqcq first: its work accesses notifyqcq, so we must + * disable its IRQ and drain its work before freeing notifyqcq. + */ pdsc_qcq_free(pdsc, &pdsc->adminqcq); + pdsc_qcq_free(pdsc, &pdsc->notifyqcq); if (pdsc->kern_dbpage) { iounmap(pdsc->kern_dbpage); @@ -479,8 +487,6 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing) { if (!pdsc->pdev->is_virtfn) pdsc_devcmd_reset(pdsc); - if (pdsc->adminqcq.work.func) - cancel_work_sync(&pdsc->adminqcq.work); pci_clear_master(pdsc->pdev); diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 22db78343eb0..638b9c7a509d 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -435,8 +435,6 @@ static void pdsc_remove(struct pci_dev *pdev) pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev); timer_shutdown_sync(&pdsc->wdtimer); - if (pdsc->wq) - destroy_workqueue(pdsc->wq); mutex_lock(&pdsc->config_lock); set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state); @@ -444,6 +442,9 @@ static void pdsc_remove(struct pci_dev *pdev) pdsc_stop(pdsc); pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING); mutex_unlock(&pdsc->config_lock); + + if (pdsc->wq) + destroy_workqueue(pdsc->wq); mutex_destroy(&pdsc->config_lock); mutex_destroy(&pdsc->devcmd_lock); -- cgit v1.2.3 From a11f0b8a204296fe7db9eaec53441012222cb004 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 20:14:56 +0000 Subject: pds_core: yield the CPU while waiting for the adminq to drain pdsc_adminq_wait_and_dec_once_unused() busy-waits for adminq_refcnt to drop to one: while (!refcount_dec_if_one(&pdsc->adminq_refcnt)) cpu_relax(); The refcount is held by pdsc_adminq_post() for the duration of an in-flight command, which can wait up to devcmd_timeout seconds (PDS_CORE_DEVCMD_TIMEOUT is 5) for the hardware to complete. cpu_relax() is not a reschedule point, so on a non-preemptible kernel this loop can spin on the CPU for several seconds, starving other tasks on that core. Add cond_resched() to the loop so the waiter yields to other runnable tasks while it polls, keeping cpu_relax() as the busy-wait hint between checks. Fixes: 7e82a8745b95 ("pds_core: Prevent race issues involving the adminq") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260629200358.2626129-1-nikhil.rao%40amd.com?part=2 Signed-off-by: Nikhil P. Rao Reviewed-by: Eric Joyner Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260714201456.1776153-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index e39b2c9beb20..04ec2569c61c 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -539,6 +539,7 @@ static void pdsc_adminq_wait_and_dec_once_unused(struct pdsc *pdsc) dev_dbg_ratelimited(pdsc->dev, "%s: adminq in use\n", __func__); cpu_relax(); + cond_resched(); } } -- cgit v1.2.3 From dd6b1cc748cd28147c113f9daa76393916ad9494 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 20:41:45 +0000 Subject: pds_core: order completion reads after the ownership check pdsc_process_adminq() and pdsc_process_notifyq() decide a completion is valid from its ownership field - the color bit for the adminq, the event id for the notifyq - then read the rest of the descriptor, with no barrier in between. On a weakly ordered architecture the CPU may read the payload first. Add dma_rmb() between the ownership read and the payload reads. Fixes: 7e82a8745b95 ("pds_core: Prevent race issues involving the adminq") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260629200358.2626129-1-nikhil.rao%40amd.com?part=2 Signed-off-by: Nikhil P. Rao Reviewed-by: Eric Joyner Link: https://patch.msgid.link/20260714204145.1782390-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/adminq.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/amd/pds_core/adminq.c b/drivers/net/ethernet/amd/pds_core/adminq.c index 097bb092bdb8..eadb4b604fbe 100644 --- a/drivers/net/ethernet/amd/pds_core/adminq.c +++ b/drivers/net/ethernet/amd/pds_core/adminq.c @@ -18,7 +18,13 @@ static int pdsc_process_notifyq(struct pdsc_qcq *qcq) comp = cq_info->comp; eid = le64_to_cpu(comp->event.eid); while (eid > pdsc->last_eid) { - u16 ecode = le16_to_cpu(comp->event.ecode); + u16 ecode; + + /* Order the payload read after the event id, the field the + * driver uses to detect a new completion. + */ + dma_rmb(); + ecode = le16_to_cpu(comp->event.ecode); switch (ecode) { case PDS_EVENT_LINK_CHANGE: @@ -101,6 +107,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq) spin_lock_irqsave(&pdsc->adminq_lock, irqflags); comp = cq->info[cq->tail_idx].comp; while (pdsc_color_match(comp->color, cq->done_color)) { + /* Order the payload reads after the color bit, the field the + * driver uses to detect a new completion. + */ + dma_rmb(); q_info = &q->info[q->tail_idx]; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); -- cgit v1.2.3 From bfa33cd513c7ceb93c5a4c30e5662acd73c0a916 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 21:07:45 +0000 Subject: pds_core: fix auxiliary device add/del races Two paths add or delete the same slot (pf->vfs[vf_id].padev): a VF's pdsc_reset_done() and the PF's devlink enable_vnet/disable_vnet handler. They serialize on config_lock, but neither guards the slot under it correctly. add() registers and stores a new auxiliary device without first checking the slot, so a second add of an already-populated slot leaks the first device. del() makes that check outside config_lock, so two concurrent dels can both pass it; the first clears the slot, and the second dereferences a NULL pointer. Check and update the slot under config_lock in both paths. Fixes: b699bdc720c0 ("pds_core: specify auxiliary_device to be created") Reported-by: sashiko-bot@kernel.org # Running on a local machine Signed-off-by: Nikhil P. Rao Reviewed-by: Brett Creeley Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260714210745.1785625-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/auxbus.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/auxbus.c b/drivers/net/ethernet/amd/pds_core/auxbus.c index 73b3481220b1..3acafe10a6d5 100644 --- a/drivers/net/ethernet/amd/pds_core/auxbus.c +++ b/drivers/net/ethernet/amd/pds_core/auxbus.c @@ -177,17 +177,21 @@ void pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf, { struct pds_auxiliary_dev *padev; - if (!*pd_ptr) - return; - mutex_lock(&pf->config_lock); + /* A concurrent del may have already torn this device down and + * cleared it. + */ padev = *pd_ptr; + if (!padev) + goto out_unlock; + pds_client_unregister(pf, padev->client_id); auxiliary_device_delete(&padev->aux_dev); auxiliary_device_uninit(&padev->aux_dev); *pd_ptr = NULL; +out_unlock: mutex_unlock(&pf->config_lock); } @@ -210,6 +214,13 @@ int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf, mutex_lock(&pf->config_lock); + /* Nothing to do if the aux device is already present. This also + * guards against a second add overwriting *pd_ptr and leaking the + * first, symmetric with the check in pdsc_auxbus_dev_del(). + */ + if (*pd_ptr) + goto out_unlock; + mask = BIT_ULL(PDSC_S_FW_DEAD) | BIT_ULL(PDSC_S_STOPPING_DRIVER); if (cf->state & mask) { -- cgit v1.2.3 From 3a660ca49e2c3807bffe0519db3cff677a5906e0 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Tue, 14 Jul 2026 21:27:13 +0000 Subject: pds_core: check for workqueue allocation failure pdsc_init_pf() does not check whether create_singlethread_workqueue() succeeded. Fail probe on failure. The workqueue is set up before the timer and mutexes, so its failure path must unwind only the earlier setup. Fixes: c2dbb0904310 ("pds_core: health timer and workqueue") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260629200358.2626129-1-nikhil.rao%40amd.com?part=2 Signed-off-by: Nikhil P. Rao Reviewed-by: Brett Creeley Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260714212713.1788438-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 638b9c7a509d..8d94a4d70395 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -238,6 +238,10 @@ static int pdsc_init_pf(struct pdsc *pdsc) /* General workqueue and timer, but don't start timer yet */ snprintf(wq_name, sizeof(wq_name), "%s.%d", PDS_CORE_DRV_NAME, pdsc->uid); pdsc->wq = create_singlethread_workqueue(wq_name); + if (!pdsc->wq) { + err = -ENOMEM; + goto err_out_unmap_bars; + } INIT_WORK(&pdsc->health_work, pdsc_health_thread); INIT_WORK(&pdsc->pci_reset_work, pdsc_pci_reset_thread); timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0); @@ -253,7 +257,7 @@ static int pdsc_init_pf(struct pdsc *pdsc) err = pdsc_setup(pdsc, PDSC_SETUP_INIT); if (err) { mutex_unlock(&pdsc->config_lock); - goto err_out_unmap_bars; + goto err_out_shutdown_timer; } err = pdsc_start(pdsc); @@ -305,13 +309,14 @@ err_out_stop: pdsc_stop(pdsc); err_out_teardown: pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING); -err_out_unmap_bars: +err_out_shutdown_timer: timer_shutdown_sync(&pdsc->wdtimer); if (pdsc->wq) destroy_workqueue(pdsc->wq); mutex_destroy(&pdsc->config_lock); mutex_destroy(&pdsc->devcmd_lock); pci_free_irq_vectors(pdsc->pdev); +err_out_unmap_bars: pdsc_unmap_bars(pdsc); err_out_release_regions: pci_release_regions(pdsc->pdev); -- cgit v1.2.3 From 4b9a5458d02e214ef2b384124ca626e3e381d778 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 14 Jul 2026 00:09:31 +0200 Subject: fs: preserve ACL_DONT_CACHE state in forget_cached_acl() The ACL_DONT_CACHE state is meant to be a constant state for the inode for filesystems that want to opt out of posix acl caching. Commit facd61053cff1 ("fuse: fixes after adapting to new posix acl api") used this facility to opt out of posix acl caching for fuse inodes with fuse server that does not negotiate FUSE_POSIX_ACL (fc->posix_acl). The commit also takes care to gate the forget_all_cached_acls() call in fuse_set_acl() on fc->posix_acl because there is no need for it, but there are other placed in fuse code which call forget_all_cached_acls() unconditional to fc->posix_acl and those cause the loss of the ACL_DONT_CACHE state. This is not only a functional bug. Properly timed, a get_acl() from this fuse filesystem can return a stale cached value, as was observed in tests, because set_acl() does not invalidate the unintentional acl cache. We could fix this in fuse, but it actually makes no sense for the vfs helper forget_cached_acl() to invalidate the ACL_DONT_CACHE state, so let it not do that to fix fuse and future users of ACL_DONT_CACHE. Fixes: facd61053cff1 ("fuse: fixes after adapting to new posix acl api") Cc: stable@vger.kernel.org Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260713220932.413004-2-amir73il@gmail.com Reviewed-by: Luis Henriques Signed-off-by: Christian Brauner (Amutable) --- fs/posix_acl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/posix_acl.c b/fs/posix_acl.c index b4bfe4ddf64e..3dc62c1c2708 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -93,6 +93,13 @@ static void __forget_cached_acl(struct posix_acl **p) { struct posix_acl *old; + /* + * ACL_DONT_CACHE is expected to be a "const" value and xchg it with + * ACL_NOT_CACHED would enable acl caching for the inode - + * clearly not what the caller has intended. + */ + if (READ_ONCE(*p) == ACL_DONT_CACHE) + return; old = xchg(p, ACL_NOT_CACHED); if (!is_uncached_acl(old)) posix_acl_release(old); -- cgit v1.2.3 From 9acb102522b92f24fba6b238b3668a4d9dfcb592 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 14 Jul 2026 00:09:32 +0200 Subject: selftests/fuse: add ACL_DONT_CACHE regression test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test that reproduces the stale ACL bug fixed by: "fs: preserve ACL_DONT_CACHE state in forget_cached_acl()" A FUSE mount that does not negotiate FUSE_POSIX_ACL initialises inodes with i_acl = ACL_DONT_CACHE. Before the fix, calling forget_all_cached_acls() (e.g. from fuse_update_get_attr() on a statx(AT_STATX_FORCE_SYNC)) would silently replace ACL_DONT_CACHE with ACL_NOT_CACHED, enabling the kernel ACL cache. A subsequent getxattr would populate the cache, and because fuse_set_acl() skips forget_all_cached_acls() for !fc->posix_acl, later ACL changes were not visible to callers — getxattr returned stale data. The test mounts a minimal libfuse3 lowlevel filesystem (no FUSE_POSIX_ACL negotiated) and: 1. Issues two getxattrs — both must reach the daemon, proving ACL_DONT_CACHE suppresses caching before any trigger. 2. Calls statx(AT_STATX_FORCE_SYNC) to trigger forget_all_cached_acls(). 3. Issues another getxattr (populates the cache on a buggy kernel). 4. Switches the daemon to a different-sized ACL (ACL_B). 5. Issues a final getxattr — expects ACL_B (44 bytes) and daemon call count 4; a buggy kernel returns stale ACL_A (28 bytes). fuse_acl_cache_test is only built when libfuse3 is detected via pkg-config. Christian Brauner says: Changed do_force_statx() to call the statx() libc wrapper instead of syscall(SYS_statx, ...) as requested by Amir after review feedback from Luis Henriques, and dropped the now unused include. Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260713220932.413004-3-amir73il@gmail.com Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/filesystems/fuse/Makefile | 10 + .../filesystems/fuse/fuse_acl_cache_test.c | 347 +++++++++++++++++++++ 2 files changed, 357 insertions(+) create mode 100644 tools/testing/selftests/filesystems/fuse/fuse_acl_cache_test.c diff --git a/tools/testing/selftests/filesystems/fuse/Makefile b/tools/testing/selftests/filesystems/fuse/Makefile index 612aad69a93a..f47141484275 100644 --- a/tools/testing/selftests/filesystems/fuse/Makefile +++ b/tools/testing/selftests/filesystems/fuse/Makefile @@ -5,6 +5,13 @@ CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES) TEST_GEN_PROGS := fusectl_test TEST_GEN_FILES := fuse_mnt +# fuse_acl_cache_test requires libfuse3; add it only when the library is present. +ACL_CFLAGS := $(shell pkg-config fuse3 --cflags 2>/dev/null) +ACL_LDLIBS := $(shell pkg-config fuse3 --libs 2>/dev/null) +ifneq ($(ACL_CFLAGS),) +TEST_GEN_PROGS += fuse_acl_cache_test +endif + include ../../lib.mk VAR_CFLAGS := $(shell pkg-config fuse --cflags 2>/dev/null) @@ -19,3 +26,6 @@ endif $(OUTPUT)/fuse_mnt: CFLAGS += $(VAR_CFLAGS) $(OUTPUT)/fuse_mnt: LDLIBS += $(VAR_LDLIBS) + +$(OUTPUT)/fuse_acl_cache_test: CFLAGS += $(ACL_CFLAGS) +$(OUTPUT)/fuse_acl_cache_test: LDLIBS += $(ACL_LDLIBS) diff --git a/tools/testing/selftests/filesystems/fuse/fuse_acl_cache_test.c b/tools/testing/selftests/filesystems/fuse/fuse_acl_cache_test.c new file mode 100644 index 000000000000..2411a6e285f1 --- /dev/null +++ b/tools/testing/selftests/filesystems/fuse/fuse_acl_cache_test.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test: FUSE ACL caching bug triggered by AT_STATX_FORCE_SYNC + * + * A FUSE mount that does not negotiate FUSE_POSIX_ACL initialises every inode + * with i_acl = i_default_acl = ACL_DONT_CACHE. When a fresh stat is needed + * (e.g. AT_STATX_FORCE_SYNC), fuse_update_get_attr() calls + * forget_all_cached_acls() before issuing FUSE_GETATTR. On an unfixed kernel, + * __forget_cached_acl() replaces ACL_DONT_CACHE with ACL_NOT_CACHED, + * inadvertently enabling the kernel ACL cache for that inode. The next + * getxattr populates the cache. Because fuse_set_acl() skips + * forget_all_cached_acls() for !fc->posix_acl mounts, any subsequent change to + * the ACL leaves the stale kernel entry in place, and the next getxattr returns + * wrong data without ever reaching the FUSE daemon. + * + * Fix (fs/posix_acl.c): __forget_cached_acl() returns early when *p is + * ACL_DONT_CACHE, preserving the "never cache" invariant for the inode's + * lifetime. + * + * Test outline: + * 1. Mount a minimal FUSE fs (no FUSE_POSIX_ACL negotiated). + * 2. lgetxattr -> daemon called, ACL_A returned, NOT cached (ACL_DONT_CACHE). + * 3. statx(AT_STATX_FORCE_SYNC) -> forget_all_cached_acls() called. + * Buggy: ACL_DONT_CACHE -> ACL_NOT_CACHED (cache enabled). + * Fixed: ACL_DONT_CACHE preserved. + * 4. lgetxattr -> daemon called, ACL_A returned. + * Buggy: result now cached (ACL_NOT_CACHED -> cached). + * Fixed: result still not cached. + * 5. Daemon switches to ACL_B internally (different size). + * 6. lgetxattr -> should return ACL_B (44 bytes). + * Buggy: cache hit, returns stale ACL_A (28 bytes). FAIL. + * Fixed: no cache, daemon called, returns ACL_B (44 bytes). PASS. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUSE_USE_VERSION 31 +#include + +#include "kselftest_harness.h" + +/* ---- ACL binary encoding ------------------------------------------------ */ +/* + * POSIX ACL v2 xattr format (little-endian): + * header: u32 version (= 0x00000002) + * entry: u16 tag | u16 perm | u32 id + * + * Entries must appear in tag-ascending order; named USER/GROUP entries + * require a MASK entry. Both ACLs pass posix_acl_from_xattr() validation. + */ + +/* ACL_A: 3 entries (USER_OBJ:rwx, GROUP_OBJ:r-x, OTHER:r-x) = 28 bytes */ +static const uint8_t acl_a[] = { + 0x02, 0x00, 0x00, 0x00, /* v2 header */ + 0x01, 0x00, 0x07, 0x00, 0xff, 0xff, 0xff, 0xff, /* USER_OBJ rwx */ + 0x04, 0x00, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff, /* GROUP_OBJ r-x */ + 0x20, 0x00, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff, /* OTHER r-x */ +}; + +/* + * ACL_B: 5 entries — adds USER uid=1 and MASK = 44 bytes. + * A named USER entry requires a MASK; all tags in ascending order. + */ +static const uint8_t acl_b[] = { + 0x02, 0x00, 0x00, 0x00, /* v2 header */ + 0x01, 0x00, 0x07, 0x00, 0xff, 0xff, 0xff, 0xff, /* USER_OBJ rwx */ + 0x02, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, /* USER uid=1 rwx */ + 0x04, 0x00, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff, /* GROUP_OBJ r-x */ + 0x10, 0x00, 0x07, 0x00, 0xff, 0xff, 0xff, 0xff, /* MASK rwx */ + 0x20, 0x00, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff, /* OTHER r-x */ +}; + +/* ---- Shared state (daemon thread <-> test thread) ----------------------- */ + +#define FILE_INO 2 +#define FILE_NAME "testfile" + +struct daemon_state { + pthread_mutex_t lock; + const uint8_t *acl; + size_t acl_size; + int getxattr_count; +}; + +/* + * Global: callbacks are stateless fns so we use a single global. + * Safe because only one test instance runs at a time. + */ +static struct daemon_state g_ds = { + .lock = PTHREAD_MUTEX_INITIALIZER, +}; + +/* ---- FUSE lowlevel callbacks -------------------------------------------- */ + +static void fs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + if (parent != FUSE_ROOT_ID || strcmp(name, FILE_NAME)) { + fuse_reply_err(req, ENOENT); + return; + } + struct fuse_entry_param e = {}; + + /* + * Long attr/entry timeouts so that normal stat() calls do not + * expire and trigger forget_all_cached_acls() on their own; + * only the explicit AT_STATX_FORCE_SYNC should trigger it. + */ + e.ino = FILE_INO; + e.generation = 1; + e.attr_timeout = 10.0; + e.entry_timeout = 10.0; + e.attr.st_ino = FILE_INO; + e.attr.st_mode = S_IFREG | 0644; + e.attr.st_nlink = 1; + fuse_reply_entry(req, &e); +} + +static void fs_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct stat st = {}; + + (void)fi; + if (ino == FUSE_ROOT_ID) { + st.st_ino = FUSE_ROOT_ID; + st.st_mode = S_IFDIR | 0755; + st.st_nlink = 2; + } else if (ino == FILE_INO) { + st.st_ino = FILE_INO; + st.st_mode = S_IFREG | 0644; + st.st_nlink = 1; + } else { + fuse_reply_err(req, ENOENT); + return; + } + fuse_reply_attr(req, &st, 10); +} + +static void fs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) +{ + if (ino != FILE_INO || + strcmp(name, "system.posix_acl_access") != 0) { + fuse_reply_err(req, ENODATA); + return; + } + + pthread_mutex_lock(&g_ds.lock); + const uint8_t *acl = g_ds.acl; + size_t acl_size = g_ds.acl_size; + g_ds.getxattr_count++; + pthread_mutex_unlock(&g_ds.lock); + + if (size == 0) + fuse_reply_xattr(req, acl_size); + else if (size < acl_size) + fuse_reply_err(req, ERANGE); + else + fuse_reply_buf(req, (const char *)acl, acl_size); +} + +static const struct fuse_lowlevel_ops fs_ops = { + .lookup = fs_lookup, + .getattr = fs_getattr, + .getxattr = fs_getxattr, +}; + +/* ---- Daemon thread ------------------------------------------------------- */ + +static void *run_daemon(void *arg) +{ + fuse_session_loop((struct fuse_session *)arg); + return NULL; +} + +/* ---- kselftest harness --------------------------------------------------- */ + +FIXTURE(acl_cache) { + struct fuse_session *se; + char mountpoint[PATH_MAX]; + char file_path[PATH_MAX]; + pthread_t thread; +}; + +FIXTURE_SETUP(acl_cache) +{ + char *fuse_argv[] = { "fuse_acl_cache_test", NULL }; + struct fuse_args args = FUSE_ARGS_INIT(1, fuse_argv); + + g_ds.acl = acl_a; + g_ds.acl_size = sizeof(acl_a); + g_ds.getxattr_count = 0; + + strcpy(self->mountpoint, "/tmp/acl_cache_test_XXXXXX"); + if (!mkdtemp(self->mountpoint)) + SKIP(return, "mkdtemp: %s", strerror(errno)); + + snprintf(self->file_path, sizeof(self->file_path), + "%s/" FILE_NAME, self->mountpoint); + + self->se = fuse_session_new(&args, &fs_ops, sizeof(fs_ops), NULL); + if (!self->se) { + rmdir(self->mountpoint); + SKIP(return, "fuse_session_new failed"); + } + + if (fuse_session_mount(self->se, self->mountpoint)) { + fuse_session_destroy(self->se); + rmdir(self->mountpoint); + SKIP(return, "fuse_session_mount failed " + "(missing fusermount3 or insufficient privileges)"); + } + + if (pthread_create(&self->thread, NULL, run_daemon, self->se)) { + fuse_session_unmount(self->se); + fuse_session_destroy(self->se); + rmdir(self->mountpoint); + SKIP(return, "pthread_create: %s", strerror(errno)); + } + + fuse_opt_free_args(&args); +} + +FIXTURE_TEARDOWN(acl_cache) +{ + fuse_session_exit(self->se); + fuse_session_unmount(self->se); + pthread_join(self->thread, NULL); + fuse_session_destroy(self->se); + rmdir(self->mountpoint); +} + +static int do_force_statx(const char *path) +{ + struct statx stx; + + return statx(AT_FDCWD, path, AT_STATX_FORCE_SYNC, STATX_BASIC_STATS, + &stx); +} + +TEST_F(acl_cache, stale_after_force_sync) +{ + char buf[512]; + ssize_t sz; + int count; + + /* + * Step 1: two getxattr calls before any statx(FORCE_SYNC). + * i_acl == ACL_DONT_CACHE. __get_acl's cmpxchg(p, ACL_NOT_CACHED, + * sentinel) finds *p != ACL_NOT_CACHED on every call, so the sentinel + * is never placed and the result is never cached. Both calls must + * reach the daemon, proving ACL_DONT_CACHE suppresses caching. + */ + sz = lgetxattr(self->file_path, "system.posix_acl_access", + buf, sizeof(buf)); + ASSERT_EQ(sz, (ssize_t)sizeof(acl_a)); + + sz = lgetxattr(self->file_path, "system.posix_acl_access", + buf, sizeof(buf)); + ASSERT_EQ(sz, (ssize_t)sizeof(acl_a)); + + pthread_mutex_lock(&g_ds.lock); + count = g_ds.getxattr_count; + pthread_mutex_unlock(&g_ds.lock); + + ASSERT_EQ(count, 2); + TH_LOG("step 1 OK: both pre-trigger getxattrs reached daemon (count=%d), " + "ACL_DONT_CACHE is working", count); + + /* + * Step 2: statx(AT_STATX_FORCE_SYNC). + * fuse_update_get_attr() calls forget_all_cached_acls() before sending + * FUSE_GETATTR. + * Buggy kernel: ACL_DONT_CACHE -> ACL_NOT_CACHED (cache enabled) + * Fixed kernel: ACL_DONT_CACHE preserved (no effect) + */ + ASSERT_EQ(do_force_statx(self->file_path), 0); + TH_LOG("step 2 OK: statx(AT_STATX_FORCE_SYNC) succeeded"); + + /* + * Step 3: getxattr — cache population attempt after the trigger. + * Buggy: *p == ACL_NOT_CACHED -> sentinel placed -> fuse_get_inode_acl + * called -> ACL_A parsed and stored in the kernel cache. + * Fixed: *p == ACL_DONT_CACHE -> sentinel placement skipped -> + * fuse_get_inode_acl called but result not cached. + * Either way the correct ACL_A is returned here. + */ + sz = lgetxattr(self->file_path, "system.posix_acl_access", + buf, sizeof(buf)); + ASSERT_EQ(sz, (ssize_t)sizeof(acl_a)); + + pthread_mutex_lock(&g_ds.lock); + count = g_ds.getxattr_count; + pthread_mutex_unlock(&g_ds.lock); + + ASSERT_EQ(count, 3); + TH_LOG("step 3 OK: post-trigger getxattr reached daemon (count=%d), " + "returned correct ACL_A (%zd bytes)", count, sz); + + /* + * Step 4: switch daemon to ACL_B (different size: 44 vs 28 bytes). + * Simulates an ACL change that fuse_set_acl() would NOT invalidate for + * !fc->posix_acl mounts (it skips forget_all_cached_acls in that case). + * On a fixed kernel the ACL was never cached, so this is moot. + */ + pthread_mutex_lock(&g_ds.lock); + g_ds.acl = acl_b; + g_ds.acl_size = sizeof(acl_b); + pthread_mutex_unlock(&g_ds.lock); + TH_LOG("step 4: daemon switched to ACL_B (%zu bytes)", sizeof(acl_b)); + + /* + * Step 5: getxattr — the decisive check. + * Buggy kernel: cache hit -> stale ACL_A (28 bytes), count stays 3. + * Fixed kernel: no cache -> daemon called -> ACL_B (44 bytes), count 4. + */ + sz = lgetxattr(self->file_path, "system.posix_acl_access", + buf, sizeof(buf)); + + pthread_mutex_lock(&g_ds.lock); + count = g_ds.getxattr_count; + pthread_mutex_unlock(&g_ds.lock); + + if (sz == (ssize_t)sizeof(acl_a)) + TH_LOG("step 5 BUG: stale ACL_A (%zd bytes) from kernel cache " + "(count=%d); ACL_DONT_CACHE corrupted by " + "forget_all_cached_acls()", sz, count); + else + TH_LOG("step 5 OK: daemon reached (count=%d), " + "fresh ACL_B (%zd bytes)", count, sz); + + EXPECT_EQ(sz, (ssize_t)sizeof(acl_b)); + EXPECT_EQ(count, 4); +} + +TEST_HARNESS_MAIN -- cgit v1.2.3 From 2eb74eef4b7eda8df593d22fb48e94ef959ec8a5 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Tue, 21 Jul 2026 13:27:55 -0400 Subject: smb: client: handle STATUS_STOPPED_ON_SYMLINK responses without a symlink target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The macOS built-in SMB server returns STATUS_STOPPED_ON_SYMLINK for a CREATE on a path whose final component is a symlink, but it does not include a Symbolic Link Error Response in the error data: both ErrorContextCount and ByteCount are zero, so the symlink target is not present in the response at all. Per [MS-SMB2] section 2.2.2 such a response should carry a valid Symbolic Link Error Response, so this is a server bug, but the target can still be retrieved with FSCTL_GET_REPARSE_POINT. Frame from a capture against macOS 26.5.2 (build 25F84): SMB2 hdr : Status=0x8000002d STATUS_STOPPED_ON_SYMLINK, Cmd=Create Error Rsp: StructureSize=0x0009 Error Context Count: 0 Byte Count: 0 Error Data: 00 symlink_data() cannot find a struct smb2_symlink_err_rsp in such a response and returns -EINVAL, which parse_create_response() propagates, so smb2_query_path_info() bails out at if (rc || !data->reparse_point) goto out; before it can retry with SMB2_OP_GET_REPARSE. stat(), readlink() and ls of any server-side symlink then fail with -EINVAL: $ ls -la Config l????????? ? ? ? ? ? Config.json $ stat Config/Config.json stat: cannot statx 'Config/Config.json': Invalid argument A 5.10 client resolves these symlinks correctly against the same server and share, so this is a regression for Apple SMB servers. Handle it in several places: - symlink_data() detects the empty response (ErrorContextCount and ByteCount both zero) and returns a distinct -ENODATA, so that "server did not send the target" can be told apart from a genuinely malformed response and only this case is worked around. - parse_create_response() treats -ENODATA like STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not carry the target either: leave the reparse tag unset and clear rc, so the existing SMB2_OP_GET_REPARSE path retrieves the target. - smb2_query_path_info() only fixes up the symlink target type when the target is already known. SMB2_OP_GET_REPARSE sets data->reparse.tag but does not parse the target out of the reparse buffer; that happens later, in reparse_info_to_fattr(). Without this check smb2_fix_symlink_target_type() is called with a NULL target and returns -EIO. This could not happen with servers that send the target inline and therefore skip SMB2_OP_GET_REPARSE. - smb2_open_file() maps -ENODATA to -EIO, matching STATUS_IO_REPARSE_TAG_NOT_HANDLED, so its callers retrieve the target with SMB2_OP_GET_REPARSE as well. Tested on Debian 13, kernel 6.18.38 (armv7), against macOS 26.5.2: symlinks now resolve, including relative, parent-traversing and directory symlinks, and reads through symlinks succeed. Cc: stable@vger.kernel.org Co-developed-by: Pali Rohár Signed-off-by: Pali Rohár Signed-off-by: Carl Johnson Signed-off-by: Steve French --- fs/smb/client/smb2file.c | 21 +++++++++++++++++++++ fs/smb/client/smb2inode.c | 23 ++++++++++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c index 5ef919bce52d..f35b6488d810 100644 --- a/fs/smb/client/smb2file.c +++ b/fs/smb/client/smb2file.c @@ -30,6 +30,19 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) u8 *end = (u8 *)err + iov->iov_len; u32 len; + /* + * Per [MS-SMB2] section 2.2.2, a STATUS_STOPPED_ON_SYMLINK response has to + * carry a Symbolic Link Error Response, so ByteCount cannot be zero. Some + * servers (e.g. the macOS built-in SMB server) violate this and return an + * empty error response, with both ErrorContextCount and ByteCount set to + * zero, i.e. without the symlink target. Detect this and return -ENODATA + * so that callers can tell "server did not send the target" apart from a + * malformed response, and retrieve the target with FSCTL_GET_REPARSE_POINT + * instead. + */ + if (!err->ErrorContextCount && !le32_to_cpu(err->ByteCount)) + return ERR_PTR(-ENODATA); + if (err->ErrorContextCount) { struct smb2_error_context_rsp *p; @@ -199,6 +212,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov, oparms->path, &data->symlink_target); + /* + * If smb2_parse_symlink_response returned -ENODATA then the + * symlink_target was not sent. Treat this as if the SMB2_open() + * failed with STATUS_IO_REPARSE_TAG_NOT_HANDLED status, which is + * indicated by the -EIO errno. + */ + if (rc == -ENODATA) + rc = -EIO; if (!rc) { memset(&data->fi, 0, sizeof(data->fi)); oparms->create_options |= OPEN_REPARSE_POINT; diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 6c9c229b91f6..213bc298cdf2 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -792,9 +792,19 @@ static int parse_create_response(struct cifs_open_info_data *data, rc = smb2_parse_symlink_response(cifs_sb, iov, full_path, &data->symlink_target); - if (rc) + if (rc != 0 && rc != -ENODATA) return rc; - tag = IO_REPARSE_TAG_SYMLINK; + /* + * -ENODATA means that the response was parsed but did not contain + * the symlink target at all (see symlink_data()). Treat it like + * STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not contain it + * either: leave the tag unset and clear rc, so that the caller + * retrieves the target with SMB2_OP_GET_REPARSE. + */ + if (rc == -ENODATA) + rc = 0; + else + tag = IO_REPARSE_TAG_SYMLINK; reparse_point = true; break; case STATUS_SUCCESS: @@ -987,7 +997,14 @@ int smb2_query_path_info(const unsigned int xid, rc = -EOPNOTSUPP; } - if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) { + /* + * If the symlink was already parsed in create response then it is needed to fix + * its type now (after the second call with OPEN_REPARSE_POINT which filled the + * data->fi.Attributes). If the symlink was not parsed in create response then + * the data->symlink_target was not filled yet and then the type will be fixed + * later after data->symlink_target is filled. + */ + if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) { bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY; rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb); } -- cgit v1.2.3 From 18a551482a4a326790698b273e76d7575a51a57d Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 20 Jul 2026 18:14:47 -0500 Subject: accel: ethosu: Fix element size accounting for cmd stream validation There are 2 issues with the element size handling in the command stream validation which result in too small of a size calculated when the element size is 16/32/64 bits. For NHWC format, the element size is simply missing from the calculation. The bitfield for the element size is different between IFM/IFM2 and OFM. IFM and IFM2 encode the precision in parameter bits 2:3, while OFM uses bits 1:2. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Acked-by: Tomeu Vizoso Link: https://patch.msgid.link/20260720231450.485221-1-robh@kernel.org Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 3401883e207f..1daff32610c7 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -194,7 +194,7 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, struct feat_matrix *fm, - u32 x, u32 y, u32 c) + u32 x, u32 y, u32 c, bool ofm) { u32 element_size, storage = fm->precision >> 14; int tile = 0; @@ -231,10 +231,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, switch ((fm->precision >> 6) & 0x3) { // format case 0: //nhwc: - addr += x * fm->stride_x + c; + element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3); + addr += x * fm->stride_x + c * element_size; break; case 1: //nhcwb16: - element_size = BIT((fm->precision >> 1) & 0x3); + element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3); addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size; break; @@ -268,7 +269,7 @@ static int calc_sizes(struct drm_device *ddev, return -EINVAL; len = feat_matrix_length(info, &st->ifm, ifm_width, - ifm_height, st->ifm.depth); + ifm_height, st->ifm.depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); if (len == U64_MAX) @@ -277,7 +278,7 @@ static int calc_sizes(struct drm_device *ddev, if (ifm2) { len = feat_matrix_length(info, &st->ifm2, st->ifm.depth, - 0, st->ofm.depth); + 0, st->ofm.depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); if (len == U64_MAX) @@ -309,7 +310,7 @@ static int calc_sizes(struct drm_device *ddev, } len = feat_matrix_length(info, &st->ofm, st->ofm.width, - st->ofm.height[2], st->ofm.depth); + st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) @@ -333,7 +334,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth; len = feat_matrix_length(info, &st->ifm, width, - height, depth); + height, depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); if (len == U64_MAX) @@ -346,7 +347,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth; len = feat_matrix_length(info, &st->ifm2, width, - height, depth); + height, depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); if (len == U64_MAX) @@ -354,7 +355,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, } len = feat_matrix_length(info, &st->ofm, st->ofm.width, - st->ofm.height[2], st->ofm.depth); + st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) -- cgit v1.2.3 From 6b7e0066294d23ad1fd37f4326c32e8090fb8b65 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 20 Jul 2026 18:14:48 -0500 Subject: accel: ethosu: Handle U85 internal chaining buffer The Ethos-U85 supports an internal chaining buffer as temporary storage between some operations. When chaining is activated, the IFM/OFM region setting selects a chaining buffer rather than a region, and the IFM/OFM base addresses don't matter. In this case, the feature matrix size calculations should be skipped. Otherwise, the command stream will be intermittently rejected depending on prior feature matrix base addresses. Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver") Acked-by: Tomeu Vizoso Link: https://patch.msgid.link/20260720231450.485221-2-robh@kernel.org Signed-off-by: Rob Herring (Arm) --- drivers/accel/ethosu/ethosu_gem.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index 1daff32610c7..d50fed64d4d9 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -192,7 +192,15 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info, return len; } -static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, +static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm) +{ + u32 storage = fm->precision >> 14; + + return !ethosu_is_u65(edev) && storage == 2; +} + +static u64 feat_matrix_length(struct ethosu_device *edev, + struct ethosu_validated_cmdstream_info *info, struct feat_matrix *fm, u32 x, u32 y, u32 c, bool ofm) { @@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, if (fm->region < 0) return U64_MAX; + if (feat_matrix_chained(edev, fm)) + return 0; + switch (storage) { case 0: if (x >= fm->width0 + 1) { @@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, tile = 1; } break; + default: + return U64_MAX; } if (fm->base[tile] == U64_MAX) return U64_MAX; @@ -251,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev, u16 op, struct cmd_state *st, bool ifm, bool ifm2, bool weight, bool scale) { + struct ethosu_device *edev = to_ethosu_device(ddev); u64 len; if (ifm) { @@ -268,7 +282,7 @@ static int calc_sizes(struct drm_device *ddev, if (ifm_height < 0 || ifm_width < 0) return -EINVAL; - len = feat_matrix_length(info, &st->ifm, ifm_width, + len = feat_matrix_length(edev, info, &st->ifm, ifm_width, ifm_height, st->ifm.depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); @@ -277,7 +291,7 @@ static int calc_sizes(struct drm_device *ddev, } if (ifm2) { - len = feat_matrix_length(info, &st->ifm2, st->ifm.depth, + len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth, 0, st->ofm.depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); @@ -309,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev, st->scale[0].base + st->scale[0].length); } - len = feat_matrix_length(info, &st->ofm, st->ofm.width, + len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width, st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) return -EINVAL; - info->output_region[st->ofm.region] = true; + if (!feat_matrix_chained(edev, &st->ofm)) + info->output_region[st->ofm.region] = true; return 0; } @@ -325,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, u16 op, struct cmd_state *st, bool ifm, bool ifm2) { + struct ethosu_device *edev = to_ethosu_device(ddev); u32 height, width, depth; u64 len; @@ -333,7 +349,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width; depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth; - len = feat_matrix_length(info, &st->ifm, width, + len = feat_matrix_length(edev, info, &st->ifm, width, height, depth, false); dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", op, st->ifm.region, st->ifm.base[0], len); @@ -346,7 +362,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev, width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width; depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth; - len = feat_matrix_length(info, &st->ifm2, width, + len = feat_matrix_length(edev, info, &st->ifm2, width, height, depth, false); dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", op, st->ifm2.region, st->ifm2.base[0], len); @@ -354,13 +370,14 @@ static int calc_sizes_elemwise(struct drm_device *ddev, return -EINVAL; } - len = feat_matrix_length(info, &st->ofm, st->ofm.width, + len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width, st->ofm.height[2], st->ofm.depth, true); dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", op, st->ofm.region, st->ofm.base[0], len); if (len == U64_MAX) return -EINVAL; - info->output_region[st->ofm.region] = true; + if (!feat_matrix_chained(edev, &st->ofm)) + info->output_region[st->ofm.region] = true; return 0; } -- cgit v1.2.3 From 47a5116e56a6b6fe1e909f244e39cd0fc26ceee4 Mon Sep 17 00:00:00 2001 From: Hidayath Khan Date: Thu, 9 Jul 2026 21:17:32 +0200 Subject: net/af_iucv: fix NULL deref in afiucv_hs_callback_syn() afiucv_hs_callback_syn() allocates the child socket with GFP_ATOMIC. If the allocation fails, nsk is NULL. The connection-refused path is entered when the listen state check fails, the accept backlog is full, or nsk is NULL. The code unconditionally calls iucv_sock_kill(nsk) in that path. iucv_sock_kill() does not accept a NULL socket pointer and immediately dereferences sk via sock_flag(sk, SOCK_ZAPPED). When nsk is NULL, calling iucv_sock_kill(nsk) results in a NULL pointer dereference. Only call iucv_sock_kill() when a child socket was successfully allocated. Fixes: 3881ac441f64 ("af_iucv: add HiperSockets transport") Cc: stable@vger.kernel.org Reviewed-by: Alexandra Winter Signed-off-by: Hidayath Khan Link: https://patch.msgid.link/20260709191732.124092-1-hidayath@linux.ibm.com Signed-off-by: Jakub Kicinski --- net/iucv/af_iucv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index e3e71d168c47..ea047bab65e7 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1886,7 +1886,8 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb) afiucv_swap_src_dest(skb); trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN; err = dev_queue_xmit(skb); - iucv_sock_kill(nsk); + if (nsk) + iucv_sock_kill(nsk); bh_unlock_sock(sk); goto out; } -- cgit v1.2.3 From 18ae07691d43183d270de8be9dc8e027906015d9 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Thu, 9 Jul 2026 21:07:18 -0400 Subject: sctp: validate stream count in sctp_process_strreset_inreq() When processing a RESET_IN_REQUEST from a peer, sctp_process_strreset_inreq() derives the stream count from the parameter length but does not check whether the resulting RESET_OUT_REQUEST would exceed SCTP_MAX_CHUNK_LEN. The OUT request header (sctp_strreset_outreq, 16 bytes) is 8 bytes larger than the IN request header (sctp_strreset_inreq, 8 bytes). Generally, the IP payload is bounded to 65535 bytes, so the stream list cannot be large enough to trigger the overflow. However, on interfaces with MTU > 65535 (e.g., loopback with IPv6 jumbograms), a stream list that fits within the incoming IN parameter can cause a __u16 overflow in sctp_make_strreset_req() when computing the OUT request size, leading to an undersized skb allocation and a kernel BUG: net/core/skbuff.c:207 skb_panic net/core/skbuff.c:2625 skb_put net/sctp/sm_make_chunk.c:1535 sctp_addto_chunk net/sctp/sm_make_chunk.c:3695 sctp_make_strreset_req net/sctp/stream.c:655 sctp_process_strreset_inreq The local setsockopt path validates the generated reset request size. However, for an incoming-only reset, it accounts for the smaller IN request even though the peer must generate an OUT request with the same stream list. Such a request cannot be completed successfully by the peer. Reject peer IN requests whose corresponding OUT request would exceed SCTP_MAX_CHUNK_LEN. Also tighten the local check so it does not send an IN request that would require an oversized OUT request from the peer. Fixes: 7f9d68ac944e ("sctp: implement sender-side procedures for SSN Reset Request Parameter") Reported-by: AutonomousCodeSecurity@microsoft.com Closes: https://lore.kernel.org/all/20260707203215.2752-1-blbllhy@gmail.com/ Suggested-by: Xin Long Signed-off-by: Cen Zhang (Microsoft) Acked-by: Xin Long Link: https://patch.msgid.link/20260710010718.20318-1-blbllhy@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/stream.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 5c2fdedea088..34ffe6c945a4 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -308,7 +308,8 @@ int sctp_send_reset_streams(struct sctp_association *asoc, goto out; param_len += str_nums * sizeof(__u16) + - sizeof(struct sctp_strreset_inreq); + (out ? sizeof(struct sctp_strreset_inreq) + : sizeof(struct sctp_strreset_outreq)); } if (param_len > SCTP_MAX_CHUNK_LEN - @@ -639,6 +640,9 @@ struct sctp_chunk *sctp_process_strreset_inreq( nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16); str_p = inreq->list_of_streams; + if (nums * sizeof(__u16) + sizeof(struct sctp_strreset_outreq) > + SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_reconf_chunk)) + goto out; for (i = 0; i < nums; i++) { if (ntohs(str_p[i]) >= stream->outcnt) { result = SCTP_STRRESET_ERR_WRONG_SSN; -- cgit v1.2.3 From f8d5e7846025f4ab15a461235f8ebae9094a361a Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Wed, 15 Jul 2026 09:50:10 +0800 Subject: sctp: avoid auth_enable sysctl UAF during netns teardown proc_sctp_do_auth() updates the SCTP control socket after changing net.sctp.auth_enable. The handler gets the per-net SCTP state from ctl->data, so an already opened sysctl file can still target a network namespace while that namespace is being torn down. SCTP previously registered its per-net sysctls from sctp_defaults_init(), while the control socket is created later from sctp_ctrlsock_init(). This exposed a window during initialization where auth_enable was writable before net->sctp.ctl_sock existed, and a teardown window where auth_enable stayed writable after inet_ctl_sock_destroy() had released the control socket. Move the per-net SCTP sysctl registration into sctp_ctrlsock_init() after sctp_ctl_sock_init() succeeds, and unregister the sysctl table before destroying the control socket in sctp_ctrlsock_exit(). If sysctl registration fails after the control socket was created, destroy the control socket in the same init path. Make sctp_sysctl_net_unregister() tolerate a missing header and clear the saved pointer so init-error and exit paths can safely share the unregister helper. Fixes: 15649fd5415e ("sctp: sysctl: auth_enable: avoid using current->nsproxy") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Co-developed-by: Qi Tang Signed-off-by: Qi Tang Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Acked-by: Xin Long Link: https://patch.msgid.link/390cd5e91ed60eea27b0b64d0468301a9e73b808.1784033357.git.roxy520tt@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/protocol.c | 20 ++++++++++++-------- net/sctp/sysctl.c | 9 +++++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index cf335494bffe..49d9740b1e0f 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1383,10 +1383,6 @@ static int __net_init sctp_defaults_init(struct net *net) net->sctp.l3mdev_accept = 1; #endif - status = sctp_sysctl_net_register(net); - if (status) - goto err_sysctl_register; - /* Allocate and initialise sctp mibs. */ status = init_sctp_mibs(net); if (status) @@ -1420,8 +1416,6 @@ err_init_proc: cleanup_sctp_mibs(net); #endif err_init_mibs: - sctp_sysctl_net_unregister(net); -err_sysctl_register: return status; } @@ -1436,7 +1430,6 @@ static void __net_exit sctp_defaults_exit(struct net *net) net->sctp.proc_net_sctp = NULL; #endif cleanup_sctp_mibs(net); - sctp_sysctl_net_unregister(net); } static struct pernet_operations sctp_defaults_ops = { @@ -1450,16 +1443,27 @@ static int __net_init sctp_ctrlsock_init(struct net *net) /* Initialize the control inode/socket for handling OOTB packets. */ status = sctp_ctl_sock_init(net); - if (status) + if (status) { pr_err("Failed to initialize the SCTP control sock\n"); + return status; + } + + status = sctp_sysctl_net_register(net); + if (status) { + inet_ctl_sock_destroy(net->sctp.ctl_sock); + net->sctp.ctl_sock = NULL; + } return status; } static void __net_exit sctp_ctrlsock_exit(struct net *net) { + sctp_sysctl_net_unregister(net); + /* Free the control endpoint. */ inet_ctl_sock_destroy(net->sctp.ctl_sock); + net->sctp.ctl_sock = NULL; } static struct pernet_operations sctp_ctrlsock_ops = { diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 15e7db9a3ab2..fca840484ebf 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -615,11 +615,16 @@ int sctp_sysctl_net_register(struct net *net) void sctp_sysctl_net_unregister(struct net *net) { + struct ctl_table_header *header = net->sctp.sysctl_header; const struct ctl_table *table; - table = net->sctp.sysctl_header->ctl_table_arg; - unregister_net_sysctl_table(net->sctp.sysctl_header); + if (!header) + return; + + table = header->ctl_table_arg; + unregister_net_sysctl_table(header); kfree(table); + net->sctp.sysctl_header = NULL; } static struct ctl_table_header *sctp_sysctl_header; -- cgit v1.2.3 From ffb2bd7ade36ec4da32c46a6eddbf4515316d08c Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Wed, 15 Jul 2026 09:50:11 +0800 Subject: sctp: close UDP tunnel sockets during netns teardown proc_sctp_do_udp_port() starts per-net SCTP UDP tunneling sockets when net.sctp.udp_port is set, and stops/restarts them when the sysctl value changes. The netns exit path does not stop these sockets, so a namespace can be torn down while its SCTP UDP tunnel sockets are still installed. Close the UDP tunnel sockets from sctp_ctrlsock_exit() after unregistering the per-net sysctl table. This prevents new sysctl writes from racing in while the sockets are being released, and closes the sockets before the control socket is destroyed. Fixes: 046c052b475e ("sctp: enable udp tunneling socks") Cc: stable@vger.kernel.org Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/b9f1f02b0780ad6a719e2413f5f0bb8eb7702d94.1782585631.git.roxy520tt%40gmail.com Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Acked-by: Xin Long Link: https://patch.msgid.link/6dab75f22855cb219e2e30a5497cab03b970ab91.1784033357.git.roxy520tt@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/protocol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 49d9740b1e0f..27c26e12f95d 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1460,6 +1460,7 @@ static int __net_init sctp_ctrlsock_init(struct net *net) static void __net_exit sctp_ctrlsock_exit(struct net *net) { sctp_sysctl_net_unregister(net); + sctp_udp_sock_stop(net); /* Free the control endpoint. */ inet_ctl_sock_destroy(net->sctp.ctl_sock); -- cgit v1.2.3 From 03d1057305ef17ac3f5936ac1580bc9a1a826e14 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 15 Jul 2026 16:25:17 +0900 Subject: net: mctp i3c: clean up notifier and buses if driver register fails mctp_i3c_mod_init() registers the I3C bus notifier and then walks the existing buses with i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL) before registering the I3C device driver. If i3c_driver_register() fails, the function returns the error directly, leaving the notifier registered and every mctp_i3c_bus object created for the existing buses allocated. The notifier is left pointing into the module that failed to load and the bus list is leaked. Mirror the module exit path on this failure: unregister the notifier and tear down the buses that were added before returning the error. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: c8755b29b58e ("mctp i3c: MCTP I3C driver") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Acked-by: Jeremy Kerr Link: https://patch.msgid.link/20260715072517.13216-1-mhun512@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/mctp/mctp-i3c.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c index 6d2bbae7477b..88d9e36cd4a2 100644 --- a/drivers/net/mctp/mctp-i3c.c +++ b/drivers/net/mctp/mctp-i3c.c @@ -731,18 +731,21 @@ static __init int mctp_i3c_mod_init(void) int rc; rc = i3c_register_notifier(&mctp_i3c_notifier); - if (rc < 0) { - i3c_driver_unregister(&mctp_i3c_driver); + if (rc < 0) return rc; - } i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL); rc = i3c_driver_register(&mctp_i3c_driver); if (rc < 0) - return rc; + goto err_unregister_notifier; return 0; + +err_unregister_notifier: + i3c_unregister_notifier(&mctp_i3c_notifier); + mctp_i3c_bus_remove_all(); + return rc; } static __exit void mctp_i3c_mod_exit(void) -- cgit v1.2.3 From a45cc646a3aa83eb4ab4c7ed2685785ea51dc5e6 Mon Sep 17 00:00:00 2001 From: Kamal Wadhwa Date: Mon, 20 Jul 2026 21:25:17 +0530 Subject: regulator: core: clamp voltage constraints before applying apply_uV machine_constraints_voltage() currently applies apply_uV against the machine-supplied [min_uV, max_uV] range, and only afterwards clamps that range down to what the regulator can actually supply (via ops->list_voltage()). If the machine-supplied range is wider than the regulator's actual range, apply_uV's rounding can pick a selector outside the (correct) clamped range, so the regulator ends up programmed outside its clamped min/max. At bring-up this shows up as a voltage read-back outside the clamped range. Fix this by moving the clamping block ahead of the apply_uV block, so apply_uV always targets an already-clamped range. Whether apply_uV should run is decided from the unclamped constraints beforehand and stored in a local bool, since clamping must not itself change whether apply_uV fires. No functional change to the clamping logic itself, only its position relative to apply_uV. Its early return 0 exits become fallthroughs since the apply_uV logic now follows it. Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Kamal Wadhwa Link: https://patch.msgid.link/20260720-b4-regulator-core-clamp-voltage-v1-1-8e5eec076a8e@oss.qualcomm.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 163 ++++++++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 73 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 1797929dfe56..2e61606fc1d0 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1220,10 +1220,98 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, { const struct regulator_ops *ops = rdev->desc->ops; int ret; + bool apply_uV; + + /* + * Decide up front, from the constraints as handed to us, whether + * apply_uV needs to run below. The clamping pass right after this + * may rewrite constraints->min_uV/max_uV (e.g. the fixed-voltage + * autoconfigure case), and we don't want that to change whether + * apply_uV fires. + */ + apply_uV = rdev->constraints->apply_uV && + rdev->constraints->min_uV && rdev->constraints->max_uV; + + /* + * Constrain machine-level voltage specs to fit the actual range + * supported by this regulator before apply_uV (below) tries to + * force hardware to a value from that range: otherwise apply_uV + * can target a constraint value that doesn't correspond to any + * real voltage selector and fail registration outright, even + * though the clamping pass would have narrowed it to a value + * the regulator can actually hit. + */ + if (ops->list_voltage && rdev->desc->n_voltages) { + int count = rdev->desc->n_voltages; + int i; + int min_uV = INT_MAX; + int max_uV = INT_MIN; + int cmin = constraints->min_uV; + int cmax = constraints->max_uV; + + /* it's safe to autoconfigure fixed-voltage supplies + * and the constraints are used by list_voltage. + */ + if (count == 1 && !cmin) { + cmin = 1; + cmax = INT_MAX; + constraints->min_uV = cmin; + constraints->max_uV = cmax; + } + + /* voltage constraints are optional */ + if ((cmin == 0) && (cmax == 0)) { + /* nothing more to do */ + + /* else require explicit machine-level constraints */ + } else if (cmin <= 0 || cmax <= 0 || cmax < cmin) { + rdev_err(rdev, "invalid voltage constraints\n"); + return -EINVAL; + + /* no need to loop voltages if range is continuous */ + } else if (rdev->desc->continuous_voltage_range) { + /* nothing more to do */ + + } else { + /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */ + for (i = 0; i < count; i++) { + int value; + + value = ops->list_voltage(rdev, i); + if (value <= 0) + continue; + + /* maybe adjust [min_uV..max_uV] */ + if (value >= cmin && value < min_uV) + min_uV = value; + if (value <= cmax && value > max_uV) + max_uV = value; + } + + /* final: [min_uV..max_uV] valid iff constraints valid */ + if (max_uV < min_uV) { + rdev_err(rdev, + "unsupportable voltage constraints %u-%uuV\n", + min_uV, max_uV); + return -EINVAL; + } + + /* use regulator's subset of machine constraints */ + if (constraints->min_uV < min_uV) { + rdev_dbg(rdev, "override min_uV, %d -> %d\n", + constraints->min_uV, min_uV); + constraints->min_uV = min_uV; + } + if (constraints->max_uV > max_uV) { + rdev_dbg(rdev, "override max_uV, %d -> %d\n", + constraints->max_uV, max_uV); + constraints->max_uV = max_uV; + } + } + } /* do we need to apply the constraint voltage */ - if (rdev->constraints->apply_uV && - rdev->constraints->min_uV && rdev->constraints->max_uV) { + if (apply_uV) { int target_min, target_max; int current_uV = regulator_get_voltage_rdev(rdev); @@ -1278,77 +1366,6 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, } } - /* constrain machine-level voltage specs to fit - * the actual range supported by this regulator. - */ - if (ops->list_voltage && rdev->desc->n_voltages) { - int count = rdev->desc->n_voltages; - int i; - int min_uV = INT_MAX; - int max_uV = INT_MIN; - int cmin = constraints->min_uV; - int cmax = constraints->max_uV; - - /* it's safe to autoconfigure fixed-voltage supplies - * and the constraints are used by list_voltage. - */ - if (count == 1 && !cmin) { - cmin = 1; - cmax = INT_MAX; - constraints->min_uV = cmin; - constraints->max_uV = cmax; - } - - /* voltage constraints are optional */ - if ((cmin == 0) && (cmax == 0)) - return 0; - - /* else require explicit machine-level constraints */ - if (cmin <= 0 || cmax <= 0 || cmax < cmin) { - rdev_err(rdev, "invalid voltage constraints\n"); - return -EINVAL; - } - - /* no need to loop voltages if range is continuous */ - if (rdev->desc->continuous_voltage_range) - return 0; - - /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */ - for (i = 0; i < count; i++) { - int value; - - value = ops->list_voltage(rdev, i); - if (value <= 0) - continue; - - /* maybe adjust [min_uV..max_uV] */ - if (value >= cmin && value < min_uV) - min_uV = value; - if (value <= cmax && value > max_uV) - max_uV = value; - } - - /* final: [min_uV..max_uV] valid iff constraints valid */ - if (max_uV < min_uV) { - rdev_err(rdev, - "unsupportable voltage constraints %u-%uuV\n", - min_uV, max_uV); - return -EINVAL; - } - - /* use regulator's subset of machine constraints */ - if (constraints->min_uV < min_uV) { - rdev_dbg(rdev, "override min_uV, %d -> %d\n", - constraints->min_uV, min_uV); - constraints->min_uV = min_uV; - } - if (constraints->max_uV > max_uV) { - rdev_dbg(rdev, "override max_uV, %d -> %d\n", - constraints->max_uV, max_uV); - constraints->max_uV = max_uV; - } - } - return 0; } -- cgit v1.2.3 From 6389eaf11d6cd3a66d10d0a5ea9cd91f242ccba2 Mon Sep 17 00:00:00 2001 From: Zhengyu He Date: Fri, 17 Jul 2026 07:45:26 -0700 Subject: spi: dt-bindings: spacemit: add K3 SPI compatible The K3 SPI controller is compatible with K1, so allow K3 device trees to use "spacemit,k1-spi" as fallback. Signed-off-by: Cody Kang Signed-off-by: Zhengyu He Link: https://patch.msgid.link/20260717-k3-com260-spi-v7-2-rc2-b4-preview-20260716-v1-2-969a1b0f783f@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml index e82c7f8d0b98..5d86cc87e4fb 100644 --- a/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml +++ b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml @@ -24,7 +24,11 @@ allOf: properties: compatible: - const: spacemit,k1-spi + oneOf: + - const: spacemit,k1-spi + - items: + - const: spacemit,k3-spi + - const: spacemit,k1-spi reg: maxItems: 1 -- cgit v1.2.3 From b65352a1bac64442ad95e64f385b40ccb9f1b0db Mon Sep 17 00:00:00 2001 From: Eddie Phillips Date: Thu, 9 Jul 2026 21:19:06 +0000 Subject: gve: fix Rx queue stall on alloc failure When the system is under extreme memory pressure, page allocations can fail during the Rx buffer refill loop. If the number of buffers posted to hardware falls below a critical low threshold and the refill loop exits due to allocation failures, the queue can stall: 1. The device drops incoming packets because there are no descriptors. 2. Since no packets are processed, no Rx completions are generated. 3. Because no completions occur, NAPI is never scheduled, preventing the refill loop from running again even after memory is freed. This results in a permanent queue stall. Resolve this by introducing a starvation recovery timer for each Rx queue. If the number of buffers posted to hardware falls below a critical low threshold, start a timer to periodically reschedule NAPI. Once NAPI runs and successfully refills the queue above the threshold, the timer is not rescheduled. The threshold is set to 32 because a single maximum-sized Receive Segment Coalescing (RSC) packet can consume up to 19 descriptors in the Rx path. Lower thresholds (such as 8 or 16) would be insufficient to process a complete maximum-sized RSC packet, risking packet drops or unexpected hardware behavior under memory pressure. Setting the threshold to 32 guarantees a safe margin to handle at least one full RSC packet. Cc: stable@vger.kernel.org Fixes: 9b8dd5e5ea48 ("gve: DQO: Add RX path") Reviewed-by: Jordan Rhee Signed-off-by: Eddie Phillips Signed-off-by: Harshitha Ramamurthy Reviewed-by: Przemek Kitszel Link: https://patch.msgid.link/20260709211906.3322883-1-hramamurthy@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/google/gve/gve.h | 3 +++ drivers/net/ethernet/google/gve/gve_rx_dqo.c | 34 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 1d66d3834f7e..c280ff35ee77 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ /* Interval to schedule a stats report update, 20000ms. */ #define GVE_STATS_REPORT_TIMER_PERIOD 20000 +#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */ /* Numbers of NIC tx/rx stats in stats report. */ #define NIC_TX_STATS_REPORT_NUM 0 @@ -341,6 +343,7 @@ struct gve_rx_ring { struct xdp_rxq_info xdp_rxq; struct xsk_buff_pool *xsk_pool; struct page_frag_cache page_cache; /* Page cache to allocate XDP frames */ + struct timer_list starvation_timer; /* for queue starvation recovery */ }; /* A TX desc ring entry */ diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 02cba280d81a..8271f731a91f 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -18,6 +18,16 @@ #include #include +static void gve_rx_starvation_timer(struct timer_list *t) +{ + struct gve_rx_ring *rx = timer_container_of(rx, t, starvation_timer); + struct gve_priv *priv = rx->gve; + struct gve_notify_block *block; + + block = &priv->ntfy_blocks[rx->ntfy_id]; + napi_schedule(&block->napi); +} + static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx) { struct device *hdev = &priv->pdev->dev; @@ -120,6 +130,7 @@ void gve_rx_stop_ring_dqo(struct gve_priv *priv, int idx) if (rx->dqo.page_pool) page_pool_disable_direct_recycling(rx->dqo.page_pool); + timer_shutdown_sync(&rx->starvation_timer); gve_remove_napi(priv, ntfy_idx); gve_rx_remove_from_block(priv, idx); gve_rx_reset_ring_dqo(priv, idx); @@ -208,8 +219,10 @@ static int gve_rx_alloc_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx, void gve_rx_start_ring_dqo(struct gve_priv *priv, int idx) { int ntfy_idx = gve_rx_idx_to_ntfy(priv, idx); + struct gve_rx_ring *rx = &priv->rx[idx]; gve_rx_add_to_block(priv, idx); + timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0); gve_add_napi(priv, ntfy_idx, gve_napi_poll_dqo); } @@ -365,6 +378,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq; struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq; struct gve_priv *priv = rx->gve; + u32 num_bufs_avail_to_hw; u32 num_avail_slots; u32 num_full_slots; u32 num_posted = 0; @@ -400,6 +414,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) } rx->fill_cnt += num_posted; + + /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors + * visible to the hardware, the hardware is in danger of starving + * and cannot trigger interrupts. + * + * We use a threshold of 32 because a single maximum-sized RSC + * packet can consume up to 19 descriptors in the Rx path. Lower + * thresholds (e.g., 8 or 16) would be unsafe as they could cause + * the device to drop/stall on a maximum-sized RSC packet. + * + * Start the timer to periodically reschedule NAPI and recover. + */ + num_bufs_avail_to_hw = + ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) - + bufq->head) & bufq->mask; + + if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) { + mod_timer(&rx->starvation_timer, + jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS)); + } } static void gve_rx_skb_csum(struct sk_buff *skb, -- cgit v1.2.3 From eaa39f9f8ac8c1d032cd26b9cd572804e9d7683f Mon Sep 17 00:00:00 2001 From: Rishikesh Jethwani Date: Thu, 9 Jul 2026 16:44:36 -0600 Subject: tls: device: push pending open record on splice EOF On kTLS device-offload sockets, sendfile() with count > EOF can reach ->splice_eof() with a fully assembled but still-open TLS record left pending. tls_device_splice_eof() only flushes partially sent records, so an abrupt close() can drop the final record and the peer receives a short file. Fix tls_device_splice_eof() to also push pending open records. This matches the software path, where splice EOF already flushes pending open records. Fixes: d4c1e80b0d1b ("tls/device: Use splice_eof() to flush") Link: https://lore.kernel.org/netdev/CAMPsyauZ+jzG9AysO0FWv6ZY0kvCUpjX_U7o=oOjCuOQ87BCgg@mail.gmail.com/ Reported-by: Nils Juenemann Signed-off-by: Rishikesh Jethwani Tested-by: Nils Juenemann Link: https://patch.msgid.link/20260709224436.1608993-2-rjethwani@purestorage.com Signed-off-by: Jakub Kicinski --- net/tls/tls_device.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 741aef09bfd3..37bb06a8e8f5 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -595,13 +595,15 @@ void tls_device_splice_eof(struct socket *sock) struct tls_context *tls_ctx = tls_get_ctx(sk); struct iov_iter iter = {}; - if (!tls_is_partially_sent_record(tls_ctx)) + if (!tls_is_partially_sent_record(tls_ctx) && + !tls_is_pending_open_record(tls_ctx)) return; mutex_lock(&tls_ctx->tx_lock); lock_sock(sk); - if (tls_is_partially_sent_record(tls_ctx)) { + if (tls_is_partially_sent_record(tls_ctx) || + tls_is_pending_open_record(tls_ctx)) { iov_iter_bvec(&iter, ITER_SOURCE, NULL, 0, 0); tls_push_data(sk, &iter, 0, 0, TLS_RECORD_TYPE_DATA); } -- cgit v1.2.3 From f8b1abed736111f914b2c567d9a3db1f71e788e8 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:41 +0200 Subject: selftests: af_unix: add USER_NS config This is required to use unshare(CLONE_NEWUSER). This has not been seen on NIPA before, because the 'af_unix' tests are executed with the 'net' ones, merging their config files. USER_NS is present in tools/testing/selftests/net/config. This issue is visible when only the af_unix config is used on top of the default one. This is the recommended way to execute selftest targets. Fixes: ac011361bd4f ("af_unix: Add test for sock_diag and UDIAG_SHOW_UID.") Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-1-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/af_unix/config | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/net/af_unix/config b/tools/testing/selftests/net/af_unix/config index b5429c15a53c..41dbb03c747e 100644 --- a/tools/testing/selftests/net/af_unix/config +++ b/tools/testing/selftests/net/af_unix/config @@ -1,3 +1,4 @@ CONFIG_AF_UNIX_OOB=y CONFIG_UNIX=y CONFIG_UNIX_DIAG=m +CONFIG_USER_NS=y -- cgit v1.2.3 From 441a820ccef9af80a9ac5a4c85b9c396e595967c Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:42 +0200 Subject: selftests: openvswitch: add config file The kselftests doc mentions that a config file should be present "if a test needs specific kernel config options enabled". This selftest requires some kernel config, but no config file was provided. We could say that a sub-target could use the parent's config file, but the kselftests doc doesn't mention anything about that. Plus the net/openvswitch target is the only net target without a config file. Here is a new config file, which is a trimmed version of the net one, with hopefully the minimal required kconfig on top of 'make defconfig'. The Fixes tag points to the introduction of the net/openvswitch target, just to help validating this target on stable kernels. Fixes: 25f16c873fb1 ("selftests: add openvswitch selftest suite") Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Eelco Chaudron Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-2-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/openvswitch/config | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tools/testing/selftests/net/openvswitch/config diff --git a/tools/testing/selftests/net/openvswitch/config b/tools/testing/selftests/net/openvswitch/config new file mode 100644 index 000000000000..c659749cd086 --- /dev/null +++ b/tools/testing/selftests/net/openvswitch/config @@ -0,0 +1,16 @@ +CONFIG_GENEVE=m +CONFIG_INET_DIAG=y +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_OVS=y +CONFIG_OPENVSWITCH=m +CONFIG_OPENVSWITCH_GENEVE=m +CONFIG_OPENVSWITCH_GRE=m +CONFIG_OPENVSWITCH_VXLAN=m +CONFIG_PSAMPLE=m +CONFIG_VETH=y +CONFIG_VLAN_8021Q=y +CONFIG_VXLAN=m -- cgit v1.2.3 From 90c792681a3732caaf7bf5bc435877736baf591a Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:43 +0200 Subject: selftests: ovpn: add IPV6 and VETH configs They are required to run the selftests: - Tests are executed in v4 and v6. - Virtual Ethernet are used between the different netns. This has not been seen on NIPA before, because the 'ovpn' tests are executed with the 'tcp_ao' ones, merging their config files. These two kernel config are present in tools/testing/selftests/net/tcp_ao/config. This issue is visible when only the ovpn config is used on top of the default one. This is the recommended way to execute selftest targets. Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module") Signed-off-by: Matthieu Baerts (NGI0) Acked-by: Antonio Quartulli Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-3-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/ovpn/config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/selftests/net/ovpn/config index d6cf033d555e..6b424762e46e 100644 --- a/tools/testing/selftests/net/ovpn/config +++ b/tools/testing/selftests/net/ovpn/config @@ -4,6 +4,7 @@ CONFIG_CRYPTO_CHACHA20POLY1305=y CONFIG_CRYPTO_GCM=y CONFIG_DST_CACHE=y CONFIG_INET=y +CONFIG_IPV6=y CONFIG_NET=y CONFIG_NETFILTER=y CONFIG_NET_UDP_TUNNEL=y @@ -11,3 +12,4 @@ CONFIG_NF_TABLES=m CONFIG_NF_TABLES_INET=y CONFIG_OVPN=m CONFIG_STREAM_PARSER=y +CONFIG_VETH=y -- cgit v1.2.3 From 61ac7049aaa86ae044e8a5b94d852218163d5bf8 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:44 +0200 Subject: selftests: ovpn: increase timeout The default timeout is 45 seconds, that's too low for a few ovpn tests. Indeed, these tests can take up to 50 seconds with some debug kernel config on NIPA. Set a timeout to 90 seconds, just to be on the safe side. Note that the Fixes tag here points to the introduction of the ovpn tests because I don't know when they started to take more than 45 seconds. That's OK because a timeout of 1.5 minutes is not exaggerated. Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module") Signed-off-by: Matthieu Baerts (NGI0) Acked-by: Antonio Quartulli Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-4-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/ovpn/settings | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/net/ovpn/settings diff --git a/tools/testing/selftests/net/ovpn/settings b/tools/testing/selftests/net/ovpn/settings new file mode 100644 index 000000000000..ba4d85f74cd6 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/settings @@ -0,0 +1 @@ +timeout=90 -- cgit v1.2.3 From 3529d75d67411497341cd804a045185d6035dff2 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:45 +0200 Subject: selftests: drv-net: increase timeout The default timeout is 45 seconds, that's too low for the xdp.py test. Indeed, this test can take up to 3 minutes with some debug kernel config on NIPA. Set a timeout to 6 minutes, just to be on the safe side. Note that the Fixes tag here points to the introduction of the xdp.py test because I don't know when this test started to take more than 45 seconds. That's OK because a timeout of 6 minutes is not exaggerated. Fixes: 1cbcb1b28b26 ("selftests: drv-net: Test XDP_PASS/DROP support") Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-5-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/settings | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/drivers/net/settings diff --git a/tools/testing/selftests/drivers/net/settings b/tools/testing/selftests/drivers/net/settings new file mode 100644 index 000000000000..eef533824a3c --- /dev/null +++ b/tools/testing/selftests/drivers/net/settings @@ -0,0 +1 @@ +timeout=360 -- cgit v1.2.3 From c25dd7439f84cf607e13d6de8cc1c79cd51f56ff Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:46 +0200 Subject: selftests: drv-net: add missing kconfig for psp.py This psp.py selftest was failing on my side when only using the drivers/net config file on top of the default one -- the recommended way to execute selftest targets. It looks like some kernel config are needed to execute the new tc commands. Note that this was not visible on NIPA, because these tests are executed with the drivers/net/hw ones, combining the two config files, and the hw one contains the missing ones. Fixes: 3f74d5bb807e ("selftests/net: Add env for container based tests") Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Wei Wang Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-6-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/config | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config index 91d4fd410914..2070e890e064 100644 --- a/tools/testing/selftests/drivers/net/config +++ b/tools/testing/selftests/drivers/net/config @@ -4,6 +4,8 @@ CONFIG_DEBUG_INFO_BTF_MODULES=n CONFIG_INET_PSP=y CONFIG_IPV6=y CONFIG_MACSEC=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_CLS_BPF=y CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y CONFIG_NETCONSOLE_EXTENDED_LOG=y @@ -11,6 +13,7 @@ CONFIG_NETDEVSIM=m CONFIG_NETKIT=y CONFIG_NET_SCH_ETF=m CONFIG_NET_SCH_FQ=m +CONFIG_NET_SCH_INGRESS=y CONFIG_PPP=y CONFIG_PPPOE=y CONFIG_VLAN_8021Q=m -- cgit v1.2.3 From e6ad44a5b1d55f5396b69d9575b2711dfeecba12 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 10 Jul 2026 20:04:47 +0200 Subject: selftests: netconsole: ignore busywait errors In these netconsole tests, bash is used with errexit (set -e). It means that if the busywait timeout, the tests finish without printing an error message. It is fine to ignore these errors, because the following validate_xxx helpers will check the content of the output file, and exit with an appropriated error message, e.g. FAIL: File was not generated. Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260710-net-sft-fix-containers-v1-7-a2915c294ef5@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh | 2 +- .../selftests/drivers/net/netconsole/netcons_fragmented_msg.sh | 4 ++-- tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh | 2 +- tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh index 96d704b8d9d9..4436567abc94 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_cmdline.sh @@ -50,7 +50,7 @@ do # Send the message echo "${MSG}: ${TARGET}" > /dev/kmsg # Wait until socat saves the file to disk - busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true # Make sure the message was received in the dst part # and exit validate_msg "${OUTPUT_FILE}" diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_fragmented_msg.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_fragmented_msg.sh index 0dc7280c3080..fc3db40c1df5 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_fragmented_msg.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_fragmented_msg.sh @@ -104,7 +104,7 @@ wait_local_port_listen "${NAMESPACE}" "${PORT}" udp # Send the message echo "${MSG}: ${TARGET}" > /dev/kmsg # Wait until socat saves the file to disk -busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" +busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true # Check if the message was not corrupted validate_fragmented_result "${OUTPUT_FILE}" @@ -117,6 +117,6 @@ disable_release_append listen_port_and_save_to "${OUTPUT_FILE}" & wait_local_port_listen "${NAMESPACE}" "${PORT}" udp echo "${MSG}: ${TARGET}" > /dev/kmsg -busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" +busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true validate_fragmented_result "${OUTPUT_FILE}" exit "${ksft_pass}" diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh index d9111f2102bc..b379dff9087e 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh @@ -108,7 +108,7 @@ do # Send the message echo "${MSG}: ${TARGET}" > /dev/kmsg # Wait until socat saves the file to disk - busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true # Make sure the message was received in the dst part # and exit validate_msg "${OUTPUT_FILE}" diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh index 3fb8c4afe3d2..7089f7bd1e34 100755 --- a/tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh +++ b/tools/testing/selftests/drivers/net/netconsole/netcons_sysdata.sh @@ -197,7 +197,7 @@ function runtest { # Send the message taskset -c "${CPU}" echo "${MSG}: ${TARGET}" > /dev/kmsg # Wait until socat saves the file to disk - busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true } # ========== # -- cgit v1.2.3 From cd170f051dba9ac146fabcd1b91726487c0cb9fa Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Fri, 10 Jul 2026 23:07:24 +0000 Subject: gtp: check skb_pull_data() return in gtp1u_send_echo_resp() gtp1u_send_echo_resp() ignores skb_pull_data()'s return value. Its caller gtp1u_udp_encap_recv() only guarantees 16 bytes (udphdr + gtp1_header), but the pull requests 20 (gtp1_header_long + udphdr). For a 16-19 byte echo request the pull fails and returns NULL without advancing skb->data; execution continues, and the following skb_push() plus the IP header pushed by iptunnel_xmit() move skb->data below skb->head, tripping skb_under_panic(). Fix it by dropping the packet when skb_pull_data() fails. skbuff: skb_under_panic: ... kernel BUG at net/core/skbuff.c:214! Call Trace: skb_push (net/core/skbuff.c:2648) iptunnel_xmit (net/ipv4/ip_tunnel_core.c:82) gtp_encap_recv (drivers/net/gtp.c:701 drivers/net/gtp.c:808 drivers/net/gtp.c:920) udp_queue_rcv_one_skb (net/ipv4/udp.c:2388) ... Kernel panic - not syncing: Fatal exception in interrupt Fixes: 9af41cc33471 ("gtp: Implement GTP echo response") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Link: https://patch.msgid.link/20260710230724.942574-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- drivers/net/gtp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index c0e38878af51..9a12cc53da00 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -669,8 +669,9 @@ static int gtp1u_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb) return -1; /* pull GTP and UDP headers */ - skb_pull_data(skb, - sizeof(struct gtp1_header_long) + sizeof(struct udphdr)); + if (!skb_pull_data(skb, sizeof(struct gtp1_header_long) + + sizeof(struct udphdr))) + return -1; gtp_pkt = skb_push(skb, sizeof(struct gtp1u_packet)); memset(gtp_pkt, 0, sizeof(struct gtp1u_packet)); -- cgit v1.2.3 From 6347c5314cee49f364aaf2e40ff15415a57a116e Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Mon, 13 Jul 2026 22:15:51 +0000 Subject: nexthop: initialize extack in nh_res_bucket_migrate() nh_res_bucket_migrate() passes an uninitialized netlink_ext_ack to call_nexthop_res_bucket_notifiers(). When nh_notifier_res_bucket_info_init() fails (e.g. the kzalloc returns -ENOMEM), the error is propagated back before any notifier sets extack._msg, and the error path formats the stale pointer with pr_err_ratelimited("%s\n", extack._msg). With CONFIG_INIT_STACK_NONE this dereferences uninitialized stack memory: Oops: general protection fault, probably for non-canonical address ... KASAN: maybe wild-memory-access in range [...] RIP: 0010:string (lib/vsprintf.c:730) vsnprintf (lib/vsprintf.c:2945) _printk (kernel/printk/printk.c:2504) nh_res_bucket_migrate (net/ipv4/nexthop.c:1816) nh_res_table_upkeep (net/ipv4/nexthop.c:1866) rtm_new_nexthop (net/ipv4/nexthop.c:3323) rtnetlink_rcv_msg (net/core/rtnetlink.c:7076) netlink_sendmsg (net/netlink/af_netlink.c:1900) Kernel panic - not syncing: Fatal exception Zero-initialize extack so _msg is NULL on error paths that never set it. Fixes: 7c37c7e00411 ("nexthop: Implement notifiers for resilient nexthop groups") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260713221551.3344650-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- net/ipv4/nexthop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index 6205bd57aa85..44fe75004cac 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -1788,8 +1788,8 @@ static bool nh_res_bucket_migrate(struct nh_res_table *res_table, bool notify_nl, bool force) { struct nh_res_bucket *bucket = &res_table->nh_buckets[bucket_index]; + struct netlink_ext_ack extack = {}; struct nh_grp_entry *new_nhge; - struct netlink_ext_ack extack; int err; new_nhge = list_first_entry_or_null(&res_table->uw_nh_entries, -- cgit v1.2.3 From 22f8aa35964e8f2ab026578f45befc9605fd1b28 Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Mon, 13 Jul 2026 17:49:35 -0300 Subject: tipc: fix infinite loop in __tipc_nl_compat_dumpit cmd->dumpit callback can return a negative errno, causing an infinite loop due to the while(len) condition. As the loop never terminates, genl_mutex is never released, and other tasks waiting on it starve in D state. Check dumpit's return value, propagate it and jump to err_out on error. Reported-by: syzbot+85d0bec020d805014a3a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=85d0bec020d805014a3a Fixes: d0796d1ef63d ("tipc: convert legacy nl bearer dump to nl compat") Signed-off-by: Helen Koike Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260713204940.647668-1-koike@igalia.com Signed-off-by: Jakub Kicinski --- net/tipc/netlink_compat.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 2a786c56c8c5..d9a4f94ea2d4 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -221,6 +221,10 @@ static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, int rem; len = (*cmd->dumpit)(buf, &cb); + if (len < 0) { + err = len; + goto err_out; + } nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) { err = nlmsg_parse_deprecated(nlmsg, GENL_HDRLEN, -- cgit v1.2.3 From ff194cffd586cbd4cc49eccb002c65f2a902a277 Mon Sep 17 00:00:00 2001 From: Youssef Samir Date: Mon, 13 Jul 2026 16:59:01 +0200 Subject: net: qrtr: ns: Raise node count limit to 512 The current node limit of 64 breaks the functionality for a number of AI200 deployments that have up to 384 nodes. Raise the limit to 512. Fixes: 27d5e84e810b ("net: qrtr: ns: Limit the total number of nodes") Cc: stable@vger.kernel.org Signed-off-by: Youssef Samir Link: https://patch.msgid.link/20260713145901.212396-1-youssef.abdulrahman@oss.qualcomm.com Signed-off-by: Jakub Kicinski --- net/qrtr/ns.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index b3f9bbcf9ab9..e5b2adb161d9 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -76,11 +76,11 @@ struct qrtr_node { * requirements. If the requirement changes in the future, these values can be * increased. */ -#define QRTR_NS_MAX_NODES 64 +#define QRTR_NS_MAX_NODES 512 #define QRTR_NS_MAX_SERVERS 256 #define QRTR_NS_MAX_LOOKUPS 64 -static u8 node_count; +static u16 node_count; static struct qrtr_node *node_get(unsigned int node_id) { -- cgit v1.2.3 From e1a9d3cc11829c5414a75eb39c704f461936eb24 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Mon, 13 Jul 2026 18:56:30 +0800 Subject: tcp: initialize standalone TCP-AO response padding tcp_v4_send_ack() and tcp_v6_send_response() construct standalone TCP responses with TCP-AO options. The option length carries the actual MAC length, but the TCP header length includes the option rounded up to a four-byte boundary. tcp_ao_hash_hdr() writes the MAC only. Thus, when the MAC length is not four-byte aligned, the one to three bytes after the MAC are left uninitialized and may be transmitted. For the normal TCP-AO hashing mode, those bytes also have to be initialized before computing the MAC. Initialize only the alignment padding in the TCP-AO branches, before hashing the header. Use TCPOPT_NOP, as in the normal TCP-AO output path. This avoids adding work to non-AO TCP responses while preserving a valid authenticated header. Fixes: decde2586b34 ("net/tcp: Add TCP-AO sign to twsk") Fixes: da7dfaa6d6f7 ("net/tcp: Consistently align TCP-AO option in the header") 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 Suggested-by: Eric Dumazet Signed-off-by: Yizhou Zhao Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260713105631.8616-1-zhaoyz24@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_ipv4.c | 3 +++ net/ipv6/tcp_ipv6.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4a46da375043..aada52769057 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -971,6 +971,9 @@ static void tcp_v4_send_ack(const struct sock *sk, key->rcv_next); arg.iov[0].iov_len += tcp_ao_len_aligned(key->ao_key); rep.th.doff = arg.iov[0].iov_len / 4; + memset((u8 *)&rep.opt[offset] + tcp_ao_maclen(key->ao_key), + TCPOPT_NOP, tcp_ao_len_aligned(key->ao_key) - + tcp_ao_len(key->ao_key)); tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[offset], key->ao_key, key->traffic_key, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 522ba45ce9b7..9e9155b1b3aa 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -923,6 +923,8 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 (tcp_ao_len(key->ao_key) << 16) | (key->ao_key->sndid << 8) | (key->rcv_next)); + memset((u8 *)topt + tcp_ao_maclen(key->ao_key), TCPOPT_NOP, + tcp_ao_len_aligned(key->ao_key) - tcp_ao_len(key->ao_key)); tcp_ao_hash_hdr(AF_INET6, (char *)topt, key->ao_key, key->traffic_key, -- cgit v1.2.3 From 88b8c6ae2ccb3ef9dbb04c8e13a4d1a98c42e922 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 23 Jun 2026 20:58:03 -0300 Subject: perf bench bpf: Add missing .gitignore file In 713eeb2279402758 ("perf build: Move BPF skeleton generation out of Makefile.perf") the bpf_skel used with 'perf bench uprobe' was moved from tools/perf/util/bpf_skel/ to tools/perf/bench/bpf_skel. Copy tools/perf/util/bpf_skel/.gitignore to that new directory so that files generated during build get ignored by git. Reported-by: Linus Torvalds Cc: Adrian Hunter Cc: Ian Rogers Cc: James Clark Cc: Jiri Olsa Cc: Namhyung Kim Fixes: 713eeb2279402758 ("perf build: Move BPF skeleton generation out of Makefile.perf") Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/bench/bpf_skel/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tools/perf/bench/bpf_skel/.gitignore diff --git a/tools/perf/bench/bpf_skel/.gitignore b/tools/perf/bench/bpf_skel/.gitignore new file mode 100644 index 000000000000..cd01455e1b53 --- /dev/null +++ b/tools/perf/bench/bpf_skel/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +.tmp +*.skel.h +vmlinux.h -- cgit v1.2.3 From 4a97144794920cb17e4a1c56c243edc757f42df9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:03 -0300 Subject: tools headers UAPI: Sync linux/const.h with the kernel sources To pick up the changes in: de9e2b3d88af3641 ("uapi: Provide DIV_ROUND_CLOSEST()") That just rebuilds perf, silencing this build warning. This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/include/uapi/linux/const.h include/uapi/linux/const.h Please see tools/include/uapi/README for further details. Cc: Cristian Ciocaltea Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/linux/const.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/include/uapi/linux/const.h b/tools/include/uapi/linux/const.h index b8f629ef135f..565f309b9df8 100644 --- a/tools/include/uapi/linux/const.h +++ b/tools/include/uapi/linux/const.h @@ -50,4 +50,22 @@ #define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +/* + * Divide positive or negative dividend by positive or negative divisor + * and round to closest integer. Result is undefined for negative + * divisors if the dividend variable type is unsigned and for negative + * dividends if the divisor variable type is unsigned. + */ +#define __KERNEL_DIV_ROUND_CLOSEST(x, divisor) \ +({ \ + __typeof__(x) __x = x; \ + __typeof__(divisor) __d = divisor; \ + \ + (((__typeof__(x))-1) > 0 || \ + ((__typeof__(divisor))-1) > 0 || \ + (((__x) > 0) == ((__d) > 0))) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ +}) + #endif /* _UAPI_LINUX_CONST_H */ -- cgit v1.2.3 From edc148762eae074bc722ffb25c3a4dba5be33e50 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:06 -0300 Subject: tools headers UAPI: Sync linux/rtnetlink.h with the kernel sources To pick up the changes in: b588019e85f49069 ("rtnetlink: add RTEXT_FILTER_NAME_ONLY support") That just rebuilds perf, silencing this build warning. This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/include/uapi/linux/rtnetlink.h include/uapi/linux/rtnetlink.h Please see tools/include/uapi/README for further details. Cc: Eric Dumazet Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/uapi/linux/rtnetlink.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/include/uapi/linux/rtnetlink.h b/tools/include/uapi/linux/rtnetlink.h index dab9493c791b..27265fd31e5f 100644 --- a/tools/include/uapi/linux/rtnetlink.h +++ b/tools/include/uapi/linux/rtnetlink.h @@ -840,6 +840,7 @@ enum { #define RTEXT_FILTER_CFM_CONFIG (1 << 5) #define RTEXT_FILTER_CFM_STATUS (1 << 6) #define RTEXT_FILTER_MST (1 << 7) +#define RTEXT_FILTER_NAME_ONLY (1 << 8) /* End of information exported to user level */ -- cgit v1.2.3 From 5bbaca187c5e4f5afd7cb05cf40ce46046eac7f9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:08 -0300 Subject: tools headers: Sync linux/gfp_types.h with the kernel sources To pick up the changes in: b56ca146a2b27501 ("vmalloc: add __GFP_SKIP_KASAN support") 6a288a4ddb4a9944 ("mm/page_alloc: fix initialization of tags of the huge zero folio with init_on_free") That just rebuilds perf, silencing this build warning. This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/include/linux/gfp_types.h include/linux/gfp_types.h Please see tools/include/uapi/README for further details. Cc: David Hildenbrand (Arm) Cc: Muhammad Usama Anjum Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/linux/gfp_types.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/include/linux/gfp_types.h b/tools/include/linux/gfp_types.h index 6c75df30a281..54ca0c88bab6 100644 --- a/tools/include/linux/gfp_types.h +++ b/tools/include/linux/gfp_types.h @@ -273,17 +273,17 @@ enum { * * %__GFP_ZERO returns a zeroed page on success. * - * %__GFP_ZEROTAGS zeroes memory tags at allocation time if the memory itself - * is being zeroed (either via __GFP_ZERO or via init_on_alloc, provided that - * __GFP_SKIP_ZERO is not set). This flag is intended for optimization: setting - * memory tags at the same time as zeroing memory has minimal additional - * performance impact. + * %__GFP_ZEROTAGS zeroes memory tags at allocation time. Setting memory tags at + * the same time as zeroing memory (e.g., with __GFP_ZERO) has minimal + * additional performance impact. However, __GFP_ZEROTAGS also zeroes the tags + * even if memory is not getting zeroed at allocation time (e.g., + * with init_on_free). * * %__GFP_SKIP_KASAN makes KASAN skip unpoisoning on page allocation. * Used for userspace and vmalloc pages; the latter are unpoisoned by - * kasan_unpoison_vmalloc instead. For userspace pages, results in - * poisoning being skipped as well, see should_skip_kasan_poison for - * details. Only effective in HW_TAGS mode. + * kasan_unpoison_vmalloc instead. If passed to vmalloc, kasan_unpoison_vmalloc + * is skipped too. For userspace pages, results in poisoning being skipped as + * well, see should_skip_kasan_poison for details. Only effective in HW_TAGS mode. */ #define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN) #define __GFP_COMP ((__force gfp_t)___GFP_COMP) -- cgit v1.2.3 From e619443bcc393f4e1c249393d19e6ec837f5c424 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:10 -0300 Subject: tools headers x86 cpufeatures: Sync with the kernel sources To pick up the changes in: 5ce9cc5a232b9928 ("coco/tdx-host: Don't expose P-SEAMLDR information on CPUs with erratum") 7658b9343a8f65e5 ("KVM: SVM: add GMET bit definitions") de0bfdc7137d5132 ("KVM: x86: Advertise AVX512 Bit Matrix Multiply (BMM) to userspace") That just rebuilds perf, silencing this build warning. This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/arch/x86/include/asm/cpufeatures.h arch/x86/include/asm/cpufeatures.h Please see tools/include/uapi/README for further details. Cc: Chao Gao Cc: Nikunj A Dadhania Cc: Paolo Bonzini Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/asm/cpufeatures.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 86d17b195e79..1b4a48bff18f 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -379,6 +379,7 @@ #define X86_FEATURE_AVIC (15*32+13) /* "avic" Virtual Interrupt Controller */ #define X86_FEATURE_V_VMSAVE_VMLOAD (15*32+15) /* "v_vmsave_vmload" Virtual VMSAVE VMLOAD */ #define X86_FEATURE_VGIF (15*32+16) /* "vgif" Virtual GIF */ +#define X86_FEATURE_GMET (15*32+17) /* Guest Mode Execution Trap */ #define X86_FEATURE_X2AVIC (15*32+18) /* "x2avic" Virtual x2apic */ #define X86_FEATURE_V_SPEC_CTRL (15*32+20) /* "v_spec_ctrl" Virtual SPEC_CTRL */ #define X86_FEATURE_VNMI (15*32+25) /* "vnmi" Virtual NMI */ @@ -473,6 +474,7 @@ #define X86_FEATURE_GP_ON_USER_CPUID (20*32+17) /* User CPUID faulting */ #define X86_FEATURE_PREFETCHI (20*32+20) /* Prefetch Data/Instruction to Cache Level */ +#define X86_FEATURE_AVX512_BMM (20*32+23) /* AVX512 Bit Matrix Multiply instructions */ #define X86_FEATURE_ERAPS (20*32+24) /* Enhanced Return Address Predictor Security */ #define X86_FEATURE_SBPB (20*32+27) /* Selective Branch Prediction Barrier */ #define X86_FEATURE_IBPB_BRTYPE (20*32+28) /* MSR_PRED_CMD[IBPB] flushes all branch type predictions */ @@ -572,4 +574,5 @@ #define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 8) /* "its_native_only" CPU is affected by ITS, VMX is not affected */ #define X86_BUG_TSA X86_BUG( 1*32+ 9) /* "tsa" CPU is affected by Transient Scheduler Attacks */ #define X86_BUG_VMSCAPE X86_BUG( 1*32+10) /* "vmscape" CPU is affected by VMSCAPE attacks from guests */ +#define X86_BUG_SEAMRET_INVD_VMCS X86_BUG( 1*32+11) /* "seamret_invd_vmcs" SEAMRET from P-SEAMLDR clears the current VMCS */ #endif /* _ASM_X86_CPUFEATURES_H */ -- cgit v1.2.3 From df3f28cd6861dcbdaab2d3025048eaaf950b1486 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:13 -0300 Subject: tools arch x86: Sync the msr-index.h copy with the kernel sources To pick up the changes in: e93a93f11490992a ("KVM: x86: Virtualize AMD CPUID faulting") That introduce these new entries in the string tables: $ tools/perf/trace/beauty/tracepoints/x86_msr.sh > before.txt $ cp arch/x86/include/asm/msr-index.h tools/arch/x86/include/asm/msr-index.h $ tools/perf/trace/beauty/tracepoints/x86_msr.sh > after.txt $ diff -u before.txt after.txt @@ -349,6 +349,7 @@ [0xc00102b2 - x86_AMD_V_KVM_MSRs_offset] = "AMD_CPPC_CAP2", [0xc00102b3 - x86_AMD_V_KVM_MSRs_offset] = "AMD_CPPC_REQ", [0xc00102b4 - x86_AMD_V_KVM_MSRs_offset] = "AMD_CPPC_STATUS", + [0xc00102b5 - x86_AMD_V_KVM_MSRs_offset] = "AMD_CPPC_REQ2", [0xc00102f0 - x86_AMD_V_KVM_MSRs_offset] = "AMD_PPIN_CTL", [0xc00102f1 - x86_AMD_V_KVM_MSRs_offset] = "AMD_PPIN", [0xc0010300 - x86_AMD_V_KVM_MSRs_offset] = "AMD_SAMP_BR_FROM", This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/arch/x86/include/asm/msr-index.h arch/x86/include/asm/msr-index.h Please see tools/include/uapi/README for further details. Cc: Jim Mattson Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/asm/msr-index.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index eff29645719b..18c4be75e927 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -674,6 +674,9 @@ #define MSR_AMD64_DC_CFG 0xc0011022 #define MSR_AMD64_TW_CFG 0xc0011023 +#define MSR_AMD64_FP_CFG 0xc0011028 +#define MSR_AMD64_FP_CFG_ZEN1_DENORM_FIX_BIT 9 + #define MSR_AMD64_DE_CFG 0xc0011029 #define MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT 1 #define MSR_AMD64_DE_CFG_LFENCE_SERIALIZE BIT_ULL(MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT) @@ -698,6 +701,8 @@ #define MSR_AMD64_IBSBRTARGET 0xc001103b #define MSR_AMD64_ICIBSEXTDCTL 0xc001103c #define MSR_AMD64_IBSOPDATA4 0xc001103d +#define MSR_AMD64_IBSOPCTL2 0xc001103e +#define MSR_AMD64_IBSFETCHCTL2 0xc001103f #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ #define MSR_AMD64_SVM_AVIC_DOORBELL 0xc001011b #define MSR_AMD64_VM_PAGE_FLUSH 0xc001011e @@ -765,12 +770,14 @@ #define MSR_AMD_CPPC_CAP2 0xc00102b2 #define MSR_AMD_CPPC_REQ 0xc00102b3 #define MSR_AMD_CPPC_STATUS 0xc00102b4 +#define MSR_AMD_CPPC_REQ2 0xc00102b5 /* Masks for use with MSR_AMD_CPPC_CAP1 */ #define AMD_CPPC_LOWEST_PERF_MASK GENMASK(7, 0) #define AMD_CPPC_LOWNONLIN_PERF_MASK GENMASK(15, 8) #define AMD_CPPC_NOMINAL_PERF_MASK GENMASK(23, 16) #define AMD_CPPC_HIGHEST_PERF_MASK GENMASK(31, 24) +#define AMD_CPPC_FLOOR_PERF_CNT_MASK GENMASK_ULL(39, 32) /* Masks for use with MSR_AMD_CPPC_REQ */ #define AMD_CPPC_MAX_PERF_MASK GENMASK(7, 0) @@ -778,6 +785,9 @@ #define AMD_CPPC_DES_PERF_MASK GENMASK(23, 16) #define AMD_CPPC_EPP_PERF_MASK GENMASK(31, 24) +/* Masks for use with MSR_AMD_CPPC_REQ2 */ +#define AMD_CPPC_FLOOR_PERF_MASK GENMASK(7, 0) + /* AMD Performance Counter Global Status and Control MSRs */ #define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS 0xc0000300 #define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301 @@ -889,6 +899,7 @@ #define MSR_K7_HWCR_IRPERF_EN_BIT 30 #define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT) #define MSR_K7_HWCR_CPUID_USER_DIS_BIT 35 +#define MSR_K7_HWCR_CPUID_USER_DIS BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT) #define MSR_K7_FID_VID_CTL 0xc0010041 #define MSR_K7_FID_VID_STATUS 0xc0010042 #define MSR_K7_HWCR_CPB_DIS_BIT 25 -- cgit v1.2.3 From 34ab6d10f82f56d18a0840b89d1c2e8e72216cb5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:13 -0300 Subject: tools headers: Sync UAPI drm/drm.h with kernel sources To pick up the changes in: a1b6cf8e5e7e9102 ("drm: uapi: Use SPDX in DRM core uAPI headers") dc2d30e7db8321a6 ("drm/doc: document DRM_IOCTL_SYNCOBJ_EVENTFD") That don't result in changes to the string tables generated from this header. This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/perf/trace/beauty/include/uapi/drm/drm.h include/uapi/drm/drm.h Please see tools/include/uapi/README for further details. Cc: Laurent Pinchart Cc: Simon Ser Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/include/uapi/drm/drm.h | 27 ++++++++------------------ 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/tools/perf/trace/beauty/include/uapi/drm/drm.h b/tools/perf/trace/beauty/include/uapi/drm/drm.h index 27cc159c1d27..bc7ef7684099 100644 --- a/tools/perf/trace/beauty/include/uapi/drm/drm.h +++ b/tools/perf/trace/beauty/include/uapi/drm/drm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Header for the Direct Rendering Manager * @@ -11,25 +12,6 @@ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _DRM_H_ @@ -1323,6 +1305,13 @@ extern "C" { */ #define DRM_IOCTL_MODE_GETFB2 DRM_IOWR(0xCE, struct drm_mode_fb_cmd2) +/** + * DRM_IOCTL_SYNCOBJ_EVENTFD - Register an eventfd to be signalled by a syncobj. + * + * This can be used to integrate a syncobj in an event loop. + * + * The IOCTL argument is a struct drm_syncobj_eventfd. + */ #define DRM_IOCTL_SYNCOBJ_EVENTFD DRM_IOWR(0xCF, struct drm_syncobj_eventfd) /** -- cgit v1.2.3 From dd1e6fb8ffdfe71b6f9812b1e5add25a48f231a5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:13 -0300 Subject: perf beauty: Update copy of linux/socket.h with the kernel sources To pick up the changes in: 4987a5763fd5ab72 ("net: block MSG_NO_SHARED_FRAGS in sendmsg()") That don't result in changes to the string tables generated from this header. This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/perf/trace/beauty/include/linux/socket.h include/linux/socket.h Please see tools/include/uapi/README for further details. Cc: Jann Horn Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/include/linux/socket.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h index 3ffdfe184b23..2a8d7b14f1d1 100644 --- a/tools/perf/trace/beauty/include/linux/socket.h +++ b/tools/perf/trace/beauty/include/linux/socket.h @@ -356,7 +356,7 @@ struct ucred { /* Flags to be cleared on entry by sendmsg and sendmmsg syscalls */ #define MSG_INTERNAL_SENDMSG_FLAGS \ - (MSG_SPLICE_PAGES | MSG_SENDPAGE_NOPOLICY | MSG_SENDPAGE_DECRYPTED) + (MSG_SPLICE_PAGES | MSG_SENDPAGE_NOPOLICY | MSG_SENDPAGE_DECRYPTED | MSG_NO_SHARED_FRAGS) /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */ #define SOL_IP 0 -- cgit v1.2.3 From 42bfbb3e8d1ce4611c407c0f9d0d2ffea51b67b9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:13 -0300 Subject: tools headers: Sync UAPI linux/fs.h with the kernel sources To pick up the changes in: 45e57cfb7b10b64f ("fs: Clarify FS_CASEFOLD_FL semantics in UAPI header") That don't result in changes to the string tables generated from this header. This addresses this perf build warning: Warning: Kernel ABI header differences: diff -u tools/perf/trace/beauty/include/uapi/linux/fs.h include/uapi/linux/fs.h Please see tools/include/uapi/README for further details. Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/include/uapi/linux/fs.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/perf/trace/beauty/include/uapi/linux/fs.h b/tools/perf/trace/beauty/include/uapi/linux/fs.h index 2ea4c81df08f..bd87262f2e34 100644 --- a/tools/perf/trace/beauty/include/uapi/linux/fs.h +++ b/tools/perf/trace/beauty/include/uapi/linux/fs.h @@ -395,7 +395,16 @@ struct file_attr { #define FS_DAX_FL 0x02000000 /* Inode is DAX */ #define FS_INLINE_DATA_FL 0x10000000 /* Reserved for ext4 */ #define FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ -#define FS_CASEFOLD_FL 0x40000000 /* Folder is case insensitive */ +/* + * FS_CASEFOLD_FL indicates case-insensitive name lookup. The + * bit is most often reported on directories, where it controls + * lookups of entries within. Filesystems that derive + * case-insensitivity from mount or volume state may also report + * it on non-directory inodes; userspace must not assume the bit + * is directory-only. FS_XFLAG_CASEFOLD reports the same + * information read-only via FS_IOC_FSGETXATTR. + */ +#define FS_CASEFOLD_FL 0x40000000 #define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ -- cgit v1.2.3 From fac520e43a60230b24026f462a2b63e4d170566e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 25 Jun 2026 13:02:16 -0300 Subject: tools headers: Sync KVM headers with the kernel sources To pick up the changes in: 103ff3a50e3a50a9 ("KVM: s390: Add capability to support 2G hugepages") 229132c309d667bb ("LoongArch: KVM: Add DMSINTC device support") 2619da73bb2f10d8 ("KVM: x86: Use __DECLARE_FLEX_ARRAY() for UAPI structures with VLAs") 4aebd7d5c72f805e ("KVM: s390: Add KVM capability for ESA mode guests") 4f256d5770febb9d ("KVM: x86: nSVM: Save/restore gPAT with KVM_{GET,SET}_NESTED_STATE") 822790ab01495d67 ("KVM: x86: Define KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT") 8800dbf6614aad10 ("KVM: arm64: Allow userspace to create protected VMs when pKVM is enabled") bf8f3cec939db68e ("KVM: s390: vsie: Refactor handle_stfle") c547c51ff4d44c78 ("KVM: arm64: gic-v5: Add ARM_VGIC_V5 device to KVM headers") d7507a94a0720223 ("KVM: SVM: Treat exit_code as an unsigned 64-bit value through all of KVM") This addresses these perf build warnings: Warning: Kernel ABI header differences: diff -u tools/include/uapi/linux/kvm.h include/uapi/linux/kvm.h diff -u tools/arch/x86/include/uapi/asm/kvm.h arch/x86/include/uapi/asm/kvm.h diff -u tools/arch/x86/include/uapi/asm/svm.h arch/x86/include/uapi/asm/svm.h diff -u tools/arch/s390/include/uapi/asm/kvm.h arch/s390/include/uapi/asm/kvm.h Please see tools/include/uapi/README for further details. Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/s390/include/uapi/asm/kvm.h | 1 + tools/arch/x86/include/uapi/asm/kvm.h | 14 ++++++++------ tools/arch/x86/include/uapi/asm/svm.h | 32 ++++++++++++++++---------------- tools/include/uapi/linux/kvm.h | 20 +++++++++++++++----- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/tools/arch/s390/include/uapi/asm/kvm.h b/tools/arch/s390/include/uapi/asm/kvm.h index 60345dd2cba2..4192769b5ce0 100644 --- a/tools/arch/s390/include/uapi/asm/kvm.h +++ b/tools/arch/s390/include/uapi/asm/kvm.h @@ -444,6 +444,7 @@ struct kvm_s390_vm_cpu_machine { #define KVM_S390_VM_CPU_FEAT_PFMFI 11 #define KVM_S390_VM_CPU_FEAT_SIGPIF 12 #define KVM_S390_VM_CPU_FEAT_KSS 13 +#define KVM_S390_VM_CPU_FEAT_ASTFLEIE2 14 struct kvm_s390_vm_cpu_feat { __u64 feat[16]; }; diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h index 0d4538fa6c31..1585ec804066 100644 --- a/tools/arch/x86/include/uapi/asm/kvm.h +++ b/tools/arch/x86/include/uapi/asm/kvm.h @@ -197,13 +197,13 @@ struct kvm_msrs { __u32 nmsrs; /* number of msrs in entries */ __u32 pad; - struct kvm_msr_entry entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_msr_entry, entries); }; /* for KVM_GET_MSR_INDEX_LIST */ struct kvm_msr_list { __u32 nmsrs; /* number of msrs in entries */ - __u32 indices[]; + __DECLARE_FLEX_ARRAY(__u32, indices); }; /* Maximum size of any access bitmap in bytes */ @@ -245,7 +245,7 @@ struct kvm_cpuid_entry { struct kvm_cpuid { __u32 nent; __u32 padding; - struct kvm_cpuid_entry entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_cpuid_entry, entries); }; struct kvm_cpuid_entry2 { @@ -267,7 +267,7 @@ struct kvm_cpuid_entry2 { struct kvm_cpuid2 { __u32 nent; __u32 padding; - struct kvm_cpuid_entry2 entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_cpuid_entry2, entries); }; /* for KVM_GET_PIT and KVM_SET_PIT */ @@ -398,7 +398,7 @@ struct kvm_xsave { * the contents of CPUID leaf 0xD on the host. */ __u32 region[1024]; - __u32 extra[]; + __DECLARE_FLEX_ARRAY(__u32, extra); }; #define KVM_MAX_XCRS 16 @@ -477,6 +477,7 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8) #define KVM_X86_QUIRK_IGNORE_GUEST_PAT (1 << 9) #define KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM (1 << 10) +#define KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT (1 << 11) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 @@ -532,6 +533,7 @@ struct kvm_svm_nested_state_data { struct kvm_svm_nested_state_hdr { __u64 vmcb_pa; + __u64 gpat; }; /* for KVM_CAP_NESTED_STATE */ @@ -566,7 +568,7 @@ struct kvm_pmu_event_filter { __u32 fixed_counter_bitmap; __u32 flags; __u32 pad[4]; - __u64 events[]; + __DECLARE_FLEX_ARRAY(__u64, events); }; #define KVM_PMU_EVENT_ALLOW 0 diff --git a/tools/arch/x86/include/uapi/asm/svm.h b/tools/arch/x86/include/uapi/asm/svm.h index 650e3256ea7d..010a45c9f614 100644 --- a/tools/arch/x86/include/uapi/asm/svm.h +++ b/tools/arch/x86/include/uapi/asm/svm.h @@ -103,38 +103,38 @@ #define SVM_EXIT_VMGEXIT 0x403 /* SEV-ES software-defined VMGEXIT events */ -#define SVM_VMGEXIT_MMIO_READ 0x80000001 -#define SVM_VMGEXIT_MMIO_WRITE 0x80000002 -#define SVM_VMGEXIT_NMI_COMPLETE 0x80000003 -#define SVM_VMGEXIT_AP_HLT_LOOP 0x80000004 -#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005 +#define SVM_VMGEXIT_MMIO_READ 0x80000001ull +#define SVM_VMGEXIT_MMIO_WRITE 0x80000002ull +#define SVM_VMGEXIT_NMI_COMPLETE 0x80000003ull +#define SVM_VMGEXIT_AP_HLT_LOOP 0x80000004ull +#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005ull #define SVM_VMGEXIT_SET_AP_JUMP_TABLE 0 #define SVM_VMGEXIT_GET_AP_JUMP_TABLE 1 -#define SVM_VMGEXIT_PSC 0x80000010 -#define SVM_VMGEXIT_GUEST_REQUEST 0x80000011 -#define SVM_VMGEXIT_EXT_GUEST_REQUEST 0x80000012 -#define SVM_VMGEXIT_AP_CREATION 0x80000013 +#define SVM_VMGEXIT_PSC 0x80000010ull +#define SVM_VMGEXIT_GUEST_REQUEST 0x80000011ull +#define SVM_VMGEXIT_EXT_GUEST_REQUEST 0x80000012ull +#define SVM_VMGEXIT_AP_CREATION 0x80000013ull #define SVM_VMGEXIT_AP_CREATE_ON_INIT 0 #define SVM_VMGEXIT_AP_CREATE 1 #define SVM_VMGEXIT_AP_DESTROY 2 -#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018 -#define SVM_VMGEXIT_SAVIC 0x8000001a +#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018ull +#define SVM_VMGEXIT_SAVIC 0x8000001aull #define SVM_VMGEXIT_SAVIC_REGISTER_GPA 0 #define SVM_VMGEXIT_SAVIC_UNREGISTER_GPA 1 #define SVM_VMGEXIT_SAVIC_SELF_GPA ~0ULL -#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd -#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe +#define SVM_VMGEXIT_HV_FEATURES 0x8000fffdull +#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffeull #define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \ /* SW_EXITINFO1[3:0] */ \ (((((u64)reason_set) & 0xf)) | \ /* SW_EXITINFO1[11:4] */ \ ((((u64)reason_code) & 0xff) << 4)) -#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffff +#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffffull /* Exit code reserved for hypervisor/software use */ -#define SVM_EXIT_SW 0xf0000000 +#define SVM_EXIT_SW 0xf0000000ull -#define SVM_EXIT_ERR -1 +#define SVM_EXIT_ERR -1ull #define SVM_EXIT_REASONS \ { SVM_EXIT_READ_CR0, "read_cr0" }, \ diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index d0c0c8605976..419011097fa8 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -542,7 +543,7 @@ struct kvm_coalesced_mmio { struct kvm_coalesced_mmio_ring { __u32 first, last; - struct kvm_coalesced_mmio coalesced_mmio[]; + __DECLARE_FLEX_ARRAY(struct kvm_coalesced_mmio, coalesced_mmio); }; #define KVM_COALESCED_MMIO_MAX \ @@ -592,7 +593,7 @@ struct kvm_clear_dirty_log { /* for KVM_SET_SIGNAL_MASK */ struct kvm_signal_mask { __u32 len; - __u8 sigset[]; + __DECLARE_FLEX_ARRAY(__u8, sigset); }; /* for KVM_TPR_ACCESS_REPORTING */ @@ -703,6 +704,11 @@ struct kvm_enable_cap { #define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL #define KVM_VM_TYPE_ARM_IPA_SIZE(x) \ ((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK) + +#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31) +#define KVM_VM_TYPE_ARM_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \ + KVM_VM_TYPE_ARM_PROTECTED) + /* * ioctls for /dev/kvm fds: */ @@ -989,6 +995,8 @@ struct kvm_enable_cap { #define KVM_CAP_ARM_SEA_TO_USER 245 #define KVM_CAP_S390_USER_OPEREXEC 246 #define KVM_CAP_S390_KEYOP 247 +#define KVM_CAP_S390_VSIE_ESAMODE 248 +#define KVM_CAP_S390_HPAGE_2G 249 struct kvm_irq_routing_irqchip { __u32 irqchip; @@ -1051,7 +1059,7 @@ struct kvm_irq_routing_entry { struct kvm_irq_routing { __u32 nr; __u32 flags; - struct kvm_irq_routing_entry entries[]; + __DECLARE_FLEX_ARRAY(struct kvm_irq_routing_entry, entries); }; #define KVM_IRQFD_FLAG_DEASSIGN (1 << 0) @@ -1142,7 +1150,7 @@ struct kvm_dirty_tlb { struct kvm_reg_list { __u64 n; /* number of regs */ - __u64 reg[]; + __DECLARE_FLEX_ARRAY(__u64, reg); }; struct kvm_one_reg { @@ -1224,6 +1232,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC + KVM_DEV_TYPE_LOONGARCH_DMSINTC, +#define KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_ARM_VGIC_V5, #define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 @@ -1610,7 +1620,7 @@ struct kvm_stats_desc { #ifdef __KERNEL__ char name[KVM_STATS_NAME_SIZE]; #else - char name[]; + __DECLARE_FLEX_ARRAY(char, name); #endif }; -- cgit v1.2.3 From 0ab78ead2481adb52f9eb5b403865c529f6f2348 Mon Sep 17 00:00:00 2001 From: Markus Breitenberger Date: Mon, 13 Jul 2026 19:16:19 +0200 Subject: net: stmmac: intel: skip SerDes reconfig when rate is unchanged intel_mac_finish() is registered as the phylink mac_finish() callback for the Elkhart Lake SGMII ports. phylink calls it at the end of every major link reconfiguration, including the initial one during probe. The callback selects the PMC ModPHY LCPLL programming for the requested MAC-side interface and then power-cycles the SerDes. On Elkhart Lake that ModPHY is also used by the on-die AHCI SATA PHY. Reapplying the programming during the initial boot-time link-up disturbs the shared analog block while it is still driving SATA, so the SATA link fails to train: ata1: SATA link down (SStatus 1 SControl 300) The disk carrying the root filesystem is never detected and the system hangs at rootwait. Ethernet itself comes up normally, which makes the failure look unrelated to the network driver. Before mac_finish() runs, the legacy SerDes power-up path has already programmed SERDES_GCR0 for the current interface. The 1G and 2.5G ModPHY tables selected by mac_finish() correspond to the SerDes lane rate, so read that rate back from SERDES_GCR0 and skip the PMC reprogramming and SerDes power-cycle when it already matches the selected interface. This keeps the disruptive reprogramming out of the boot path when the SerDes is configured correctly, while preserving the previous behavior when a real SGMII/1000BASE-X to 2500BASE-X rate change is needed. If the register read fails, reconfigure as before. Fixes: a42f6b3f1cc1 ("net: stmmac: configure SerDes according to the interface mode") Cc: stable@vger.kernel.org Signed-off-by: Markus Breitenberger Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260713171619.192452-1-bre@breiti.cc Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index b8d467ba6d72..4d207f41a43b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -525,6 +525,32 @@ static int intel_set_reg_access(const struct pmc_serdes_regs *regs, int max_regs return ret; } +/* + * Return true if the SerDes lane rate must change to serve @interface. + * If the current rate cannot be determined, reconfigure as before. + */ +static bool intel_serdes_needs_reconfig(struct stmmac_priv *priv, + struct intel_priv_data *intel_priv, + phy_interface_t interface) +{ + u32 cur_rate, want_rate; + int data; + + if (!intel_priv->mdio_adhoc_addr) + return true; + + data = mdiobus_read(priv->mii, intel_priv->mdio_adhoc_addr, + SERDES_GCR0); + if (data < 0) + return true; + + cur_rate = (data & SERDES_RATE_MASK) >> SERDES_RATE_PCIE_SHIFT; + want_rate = interface == PHY_INTERFACE_MODE_2500BASEX ? + SERDES_RATE_PCIE_GEN2 : SERDES_RATE_PCIE_GEN1; + + return cur_rate != want_rate; +} + static int intel_mac_finish(struct net_device *ndev, void *intel_data, unsigned int mode, @@ -536,6 +562,11 @@ static int intel_mac_finish(struct net_device *ndev, int max_regs = 0; int ret = 0; + if (!intel_serdes_needs_reconfig(priv, intel_priv, interface)) { + priv->plat->phy_interface = interface; + return 0; + } + ret = intel_tsn_lane_is_available(ndev, intel_priv); if (ret < 0) { netdev_info(priv->dev, "No TSN lane available to set the registers.\n"); -- cgit v1.2.3 From e8a8d54c2d508891c142a928fc7d298c4c8bd0dd Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Tue, 21 Jul 2026 18:55:51 -0500 Subject: cifs: prevent readdir from changing file size due to stale directory metadata Windows Server's directory enumeration metadata lags behind the actual file size after a write+close or rename. A concurrent readdir() in the window between close() returning to userspace and stat() being called overwrites the correct cached i_size with the stale server value, causing stat() to return the wrong size. Once _cifsFileInfo_put() removes the last writable handle from openFileList, is_size_safe_to_change() permits readdir to overwrite i_size. smb2_close_getattr() then stamps cifs_i->time = jiffies, making the corrupt cached value appear fresh to the next stat(). The existing check (see Fixes:) only blocked stale size updates while an active RW lease was held, not after the last writable handle closes. Add cifsInodeInfo->time_last_write, written via smp_store_release() at writable close and on setattr/truncate. is_size_safe_to_change() checks is_inode_writable() first (acquiring open_file_lock), then rejects a readdir size update if time_last_write falls within acregmax jiffies. The spinlock release in _cifsFileInfo_put() forms a store-release barrier that pairs with the spin_lock() (load-acquire) in is_inode_writable(), ensuring the subsequent smp_load_acquire() on time_last_write observes any update from a concurrent close(). When a size update is rejected and the server value differs from the cached one, cifs_i->time is cleared to force a fresh QUERY_INFO on the next stat(). readdir is also blocked from changing i_size while writable handles are open or an RW lease is held, even on direct-IO mounts. For deferred close (closetimeo > 0), time_last_write is refreshed at the actual server close in smb2_deferred_work_close() and in the cifs_close_deferred_file*() drain paths invoked by lease/oplock breaks and tcon teardown, anchoring the protection window to the real close time rather than the earlier userspace close. time_last_write == 0 skips the time_before() check to avoid false positives near boot on 32-bit systems where jiffies starts close to INITIAL_JIFFIES. Does not reproduce against Samba or with actimeo=0. Fixes: e4b61f3b1c67 ("cifs: prevent updating file size from server if we have a read/write lease") Signed-off-by: Frank Sorenson Signed-off-by: Steve French --- fs/smb/client/cifsfs.c | 1 + fs/smb/client/cifsglob.h | 1 + fs/smb/client/file.c | 69 ++++++++++++++++++++++++++++++++++++++++++++---- fs/smb/client/inode.c | 6 +++++ fs/smb/client/misc.c | 27 ++++++++++++++++--- 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 66b9104e7ca2..1788d93a2522 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -440,6 +440,7 @@ cifs_alloc_inode(struct super_block *sb) return NULL; cifs_inode->cifsAttrs = ATTR_ARCHIVE; /* default */ cifs_inode->time = 0; + cifs_inode->time_last_write = 0; /* * Until the file is open and we have gotten oplock info back from the * server, can not assume caching of file data or metadata. diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 08e94633a9c1..79e4e84f8985 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -1566,6 +1566,7 @@ struct cifsInodeInfo { spinlock_t writers_lock; unsigned int writers; /* Number of writers on this inode */ unsigned long time; /* jiffies of last update of inode */ + unsigned long time_last_write; /* jiffies of last writable close or truncate */ u64 uniqueid; /* server inode number */ u64 createtime; /* creation time on server */ __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */ diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 968740e7c9c3..b279a44be729 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -1423,11 +1423,21 @@ void smb2_deferred_work_close(struct work_struct *work) { struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, deferred.work); + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + spin_lock(&cinode->deferred_lock); cifs_del_deferred_close(cfile); cfile->deferred_close_scheduled = false; - spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + spin_unlock(&cinode->deferred_lock); + /* + * Refresh time_last_write immediately before the actual server close + * so the protection window is anchored to the real close time, not + * the earlier userspace close time stored by cifs_close(). + */ + if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&cinode->time_last_write, jiffies); + } _cifsFileInfo_put(cfile, true, false); } @@ -1457,6 +1467,10 @@ int cifs_close(struct inode *inode, struct file *file) if (file->private_data != NULL) { cfile = file->private_data; file->private_data = NULL; + if (file->f_mode & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&cinode->time_last_write, jiffies); + } dclose = kmalloc_obj(struct cifs_deferred_close); if ((cfile->status_file_deleted == false) && (smb2_can_defer_close(inode, dclose))) { @@ -3225,13 +3239,26 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode) bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, bool from_readdir) { + struct cifs_sb_info *cifs_sb; + unsigned long tlw; + if (!cifsInode) return true; + cifs_sb = CIFS_SB(cifsInode); + if (is_inode_writable(cifsInode) || ((cifsInode->oplock & CIFS_CACHE_RW_FLG) != 0 && from_readdir)) { /* This inode is open for write at least once */ - struct cifs_sb_info *cifs_sb = CIFS_SB(cifsInode); + + /* + * Readdir data is unreliable when we have writable handles or + * an exclusive lease -- never allow it to change i_size, even + * on direct-IO mounts where the server's directory metadata + * can still lag behind the actual file state. + */ + if (from_readdir) + return false; if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_DIRECT_IO) { /* since no page cache to corrupt on directio @@ -3243,8 +3270,40 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, return true; return false; - } else - return true; + } + + /* + * No writable handles open. Check whether we are within the attribute + * cache validity window of a recent local modification. + * + * For the close() path: cifs_close() calls smp_store_release() on + * time_last_write before _cifsFileInfo_put() removes the handle under + * open_file_lock. That spin_unlock() is a store-release that pairs + * with the spin_lock() (load-acquire) in is_inode_writable() above, + * so if is_inode_writable() returned false the smp_load_acquire() + * below is guaranteed to observe any time_last_write update from a + * concurrent close(). + * + * For the setattr/truncate paths: those callers use smp_store_release() + * directly; the smp_load_acquire() below pairs with that store. There + * is no shared lock between setattr and readdir, so this relies on + * acquire-release semantics alone. The store propagation latency on + * weakly-ordered architectures (nanoseconds) is negligible relative to + * the acregmax window (seconds) and the readdir RPC round-trip + * (milliseconds), making this a sound design choice in practice. + * + * time_last_write == 0 means the inode has never been written locally; + * skip the window check to avoid false positives near boot time when + * jiffies is still close to INITIAL_JIFFIES on 32-bit systems. + */ + if (from_readdir) { + /* Pairs with smp_store_release() at close and truncate sites. */ + tlw = smp_load_acquire(&cifsInode->time_last_write); + if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax)) + return false; + } + + return true; } void cifs_oplock_break(struct work_struct *work) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index deed04dd9b91..b2806371bfde 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -237,6 +237,8 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) { i_size_write(inode, fattr->cf_eof); inode->i_blocks = CIFS_INO_BLOCKS(fattr->cf_bytes); + } else if (from_readdir && i_size_read(inode) != fattr->cf_eof) { + cifs_i->time = 0; } if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) { @@ -3277,6 +3279,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if ((attrs->ia_valid & ATTR_SIZE) && attrs->ia_size != i_size_read(inode)) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&cifsInode->time_last_write, jiffies); truncate_setsize(inode, attrs->ia_size); netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true); fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); @@ -3478,6 +3482,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if ((attrs->ia_valid & ATTR_SIZE) && attrs->ia_size != i_size_read(inode)) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&cifsInode->time_last_write, jiffies); truncate_setsize(inode, attrs->ia_size); netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true); fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index b9c59b2cf76a..fa8cfc4d97f3 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -524,7 +524,14 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) spin_unlock(&cifs_inode->open_file_lock); list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, false, false); + struct cifsFileInfo *cfile = tmp_list->cfile; + + if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(cfile, false, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -557,7 +564,14 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon) spin_unlock(&tcon->open_file_lock); list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, true, false); + struct cifsFileInfo *cfile = tmp_list->cfile; + + if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(cfile, true, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -626,7 +640,14 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, spin_unlock(&tcon->open_file_lock); list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, true, false); + struct cifsFileInfo *cfile = tmp_list->cfile; + + if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(cfile, true, false); list_del(&tmp_list->list); kfree(tmp_list); } -- cgit v1.2.3 From c2f2e83e3bbc5483730fd4ee903182761f1ae50f Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Tue, 21 Jul 2026 18:55:52 -0500 Subject: cifs: fix cifsFileInfo leak on kmalloc failure in deferred close drain paths In cifs_close_deferred_file(), cifs_close_all_deferred_files(), and cifs_close_deferred_file_under_dentry(), when a pending deferred close is cancelled via cancel_delayed_work(), the subsequent kmalloc_obj() to add the file to the local processing list may fail under memory pressure. The loop breaks immediately, but the cancelled work is no longer pending (it would have called _cifsFileInfo_put()), and the cfile is never added to file_head for processing. The cifsFileInfo reference and the open server handle both leak. Fix by saving the cfile that failed allocation in a local variable, breaking as before, and calling _cifsFileInfo_put() on it after releasing the lock. Any files later in the iteration are unaffected since their deferred work is still pending and will fire normally. Fixes: e3fc065682eb ("cifs: Deferred close performance improvements") Signed-off-by: Frank Sorenson Signed-off-by: Steve French --- fs/smb/client/misc.c | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index fa8cfc4d97f3..6edebc0807ea 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -497,7 +497,7 @@ cifs_del_deferred_close(struct cifsFileInfo *cfile) void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) { - struct cifsFileInfo *cfile = NULL; + struct cifsFileInfo *cfile = NULL, *failed_cfile = NULL; struct file_list *tmp_list, *tmp_next_list; LIST_HEAD(file_head); @@ -514,8 +514,10 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC); - if (tmp_list == NULL) + if (tmp_list == NULL) { + failed_cfile = cfile; break; + } tmp_list->cfile = cfile; list_add_tail(&tmp_list->list, &file_head); } @@ -523,6 +525,15 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) } spin_unlock(&cifs_inode->open_file_lock); + if (failed_cfile) { + if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(failed_cfile, false, false); + } + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { struct cifsFileInfo *cfile = tmp_list->cfile; @@ -540,7 +551,7 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) void cifs_close_all_deferred_files(struct cifs_tcon *tcon) { - struct cifsFileInfo *cfile; + struct cifsFileInfo *cfile, *failed_cfile = NULL; struct file_list *tmp_list, *tmp_next_list; LIST_HEAD(file_head); @@ -554,8 +565,10 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon) tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC); - if (tmp_list == NULL) + if (tmp_list == NULL) { + failed_cfile = cfile; break; + } tmp_list->cfile = cfile; list_add_tail(&tmp_list->list, &file_head); } @@ -563,6 +576,15 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon) } spin_unlock(&tcon->open_file_lock); + if (failed_cfile) { + if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(failed_cfile, true, false); + } + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { struct cifsFileInfo *cfile = tmp_list->cfile; @@ -618,7 +640,7 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, struct dentry *dentry) { struct file_list *tmp_list, *tmp_next_list; - struct cifsFileInfo *cfile; + struct cifsFileInfo *cfile, *failed_cfile = NULL; LIST_HEAD(file_head); spin_lock(&tcon->open_file_lock); @@ -631,14 +653,25 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC); - if (tmp_list == NULL) + if (tmp_list == NULL) { + failed_cfile = cfile; break; + } tmp_list->cfile = cfile; list_add_tail(&tmp_list->list, &file_head); } } spin_unlock(&tcon->open_file_lock); + if (failed_cfile) { + if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { + /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ + smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, + jiffies); + } + _cifsFileInfo_put(failed_cfile, true, false); + } + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { struct cifsFileInfo *cfile = tmp_list->cfile; -- cgit v1.2.3 From ba712ecfd942b68b21a4b0a5daaf72f6616cc66d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 15 Jul 2026 05:55:41 +0000 Subject: ppp: annotate concurrent dev->stats accesses dev->stats fields can be updated concurrently from multiple CPUs without synchronization. Use DEV_STATS_INC() for stats increments and DEV_STATS_READ() when reading dev->stats in ppp_get_stats64() and ppp_get_stats() to avoid data races. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Qingfang Deng Link: https://patch.msgid.link/20260715055541.1147542-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ppp/ppp_generic.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 717c1d3aa953..ef54e0a0462a 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -1482,7 +1482,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) outf: kfree_skb(skb); - ++dev->stats.tx_dropped; + DEV_STATS_INC(dev, tx_dropped); return NETDEV_TX_OK; } @@ -1532,11 +1532,11 @@ ppp_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr, static void ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64) { - stats64->rx_errors = dev->stats.rx_errors; - stats64->tx_errors = dev->stats.tx_errors; - stats64->rx_dropped = dev->stats.rx_dropped; - stats64->tx_dropped = dev->stats.tx_dropped; - stats64->rx_length_errors = dev->stats.rx_length_errors; + stats64->rx_errors = DEV_STATS_READ(dev, rx_errors); + stats64->tx_errors = DEV_STATS_READ(dev, tx_errors); + stats64->rx_dropped = DEV_STATS_READ(dev, rx_dropped); + stats64->tx_dropped = DEV_STATS_READ(dev, tx_dropped); + stats64->rx_length_errors = DEV_STATS_READ(dev, rx_length_errors); dev_fetch_sw_netstats(stats64, dev->tstats); } @@ -1889,7 +1889,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) drop: kfree_skb(skb); - ++ppp->dev->stats.tx_errors; + DEV_STATS_INC(ppp->dev, tx_errors); return 1; } @@ -2156,7 +2156,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) err_linearize: if (ppp->debug & 1) netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); - ++ppp->dev->stats.tx_errors; + DEV_STATS_INC(ppp->dev, tx_errors); ++ppp->nxseq; return 1; /* abandon the frame */ } @@ -2329,7 +2329,7 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) if (!ppp_decompress_proto(skb)) { kfree_skb(skb); if (ppp) { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); ppp_receive_error(ppp); } goto done; @@ -2391,7 +2391,7 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) static void ppp_receive_error(struct ppp *ppp) { - ++ppp->dev->stats.rx_errors; + DEV_STATS_INC(ppp->dev, rx_errors); if (ppp->vj) slhc_toss(ppp->vj); } @@ -2658,7 +2658,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) */ if (seq_before(seq, ppp->nextseq)) { kfree_skb(skb); - ++ppp->dev->stats.rx_dropped; + DEV_STATS_INC(ppp->dev, rx_dropped); ppp_receive_error(ppp); return; } @@ -2694,7 +2694,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) if (pskb_may_pull(skb, 2)) ppp_receive_nonmp_frame(ppp, skb); else { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); kfree_skb(skb); ppp_receive_error(ppp); } @@ -2800,7 +2800,7 @@ ppp_mp_reconstruct(struct ppp *ppp) if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) && (PPP_MP_CB(head)->BEbits & B)) { if (len > ppp->mrru + 2) { - ++ppp->dev->stats.rx_length_errors; + DEV_STATS_INC(ppp->dev, rx_length_errors); netdev_printk(KERN_DEBUG, ppp->dev, "PPP: reconstructed packet" " is too long (%d)\n", len); @@ -2855,7 +2855,7 @@ ppp_mp_reconstruct(struct ppp *ppp) " missed pkts %u..%u\n", ppp->nextseq, PPP_MP_CB(head)->sequence-1); - ++ppp->dev->stats.rx_dropped; + DEV_STATS_INC(ppp->dev, rx_dropped); ppp_receive_error(ppp); } @@ -3322,8 +3322,8 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) st->p.ppp_opackets += tx_packets; st->p.ppp_obytes += tx_bytes; } - st->p.ppp_ierrors = ppp->dev->stats.rx_errors; - st->p.ppp_oerrors = ppp->dev->stats.tx_errors; + st->p.ppp_ierrors = DEV_STATS_READ(ppp->dev, rx_errors); + st->p.ppp_oerrors = DEV_STATS_READ(ppp->dev, tx_errors); if (!vj) return; st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed; -- cgit v1.2.3 From 97ab5aa5177cfa8759bd3861d71e047ea084a829 Mon Sep 17 00:00:00 2001 From: Hongbo Li Date: Tue, 21 Jul 2026 15:05:18 +0800 Subject: MAINTAINERS: update Hongbo Li's email address Since the original email account is no longer in use, I have updated it to my current email address. Signed-off-by: Hongbo Li Acked-by: Chao Yu Acked-by: Gao Xiang Signed-off-by: Gao Xiang --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a674e36529f7..4ac000af898f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9617,7 +9617,7 @@ M: Chao Yu R: Yue Hu R: Jeffle Xu R: Sandeep Dhavale -R: Hongbo Li +R: Hongbo Li R: Chunhai Guo L: linux-erofs@lists.ozlabs.org S: Maintained -- cgit v1.2.3 From da4082e91acabc1498611ed8ccc53f0610baefc6 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Sat, 27 Jun 2026 12:13:34 -0700 Subject: wifi: mt76: mt7921: drop TXRX_NOTIFY on non-mmio buses PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7921_rx_check() and mt7921_queue_rx_skb() dispatch it to mt7921_mac_tx_free() on every bus. mt7921_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on USB and SDIO it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Call Trace: mt7921_mac_tx_free+0x64/0x310 [mt7921_common] mt7921_rx_check+0x5f/0xf0 [mt7921_common] mt76u_rx_worker+0x1b9/0x620 [mt76_usb] Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices"). Fixes: 48fab5bbef40 ("mt76: mt7921: introduce mt7921s support") Cc: stable@vger.kernel.org Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260627191336.20223-2-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 1c2377d0a53d..f7d54472da1b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -570,8 +570,9 @@ bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ - mt7921_mac_tx_free(dev, data, len); /* mmio */ + if (!mt76_is_mmio(mdev)) + return false; + mt7921_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: for (rxd += 2; rxd + 8 <= end; rxd += 8) @@ -600,7 +601,10 @@ void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ + if (!mt76_is_mmio(mdev)) { + napi_consume_skb(skb, 1); + break; + } mt7921_mac_tx_free(dev, skb->data, skb->len); napi_consume_skb(skb, 1); break; -- cgit v1.2.3 From feeff151c83e7f0ffcdedcad5343852d23d1f6e1 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Sat, 27 Jun 2026 12:13:35 -0700 Subject: wifi: mt76: mt7925: drop TXRX_NOTIFY on non-mmio buses PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7925_rx_check() and mt7925_queue_rx_skb() dispatch it to mt7925_mac_tx_free() on every bus. mt7925_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on USB it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Call Trace: mt7925_mac_tx_free+0x58/0x350 [mt7925_common] mt7925_rx_check+0xe2/0x130 [mt7925_common] mt76u_rx_worker+0x1b9/0x620 [mt76_usb] Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices"). Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Cc: stable@vger.kernel.org Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260627191336.20223-3-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 0641a7131d7c..2f9871792ea1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1203,8 +1203,9 @@ bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ - mt7925_mac_tx_free(dev, data, len); /* mmio */ + if (!mt76_is_mmio(mdev)) + return false; + mt7925_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: for (rxd += 4; rxd + 12 <= end; rxd += 12) @@ -1240,7 +1241,10 @@ void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, switch (type) { case PKT_TYPE_TXRX_NOTIFY: - /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */ + if (!mt76_is_mmio(mdev)) { + napi_consume_skb(skb, 1); + break; + } mt7925_mac_tx_free(dev, skb->data, skb->len); napi_consume_skb(skb, 1); break; -- cgit v1.2.3 From 39afc46c0243d10b7795e6e6cf4ae91f41732120 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Sat, 27 Jun 2026 12:13:36 -0700 Subject: wifi: mt76: mt7615: drop TXRX_NOTIFY on non-mmio buses PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7615_rx_check() and mt7615_queue_rx_skb() dispatch it to mt7615_mac_tx_free() on every bus. mt7615_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on the mt7663 USB and SDIO buses it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker. Same defect as the mt7921 and mt7925 patches in this series. Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices"). Fixes: eb99cc95c3b6 ("mt76: mt7615: introduce mt7663u support") Cc: stable@vger.kernel.org Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260627191336.20223-4-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index ce0051468501..aad232c5a6fa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -1601,6 +1601,8 @@ bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len) switch (type) { case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) + return false; mt7615_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: @@ -1634,6 +1636,10 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, dev_kfree_skb(skb); break; case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) { + dev_kfree_skb(skb); + break; + } mt7615_mac_tx_free(dev, skb->data, skb->len); dev_kfree_skb(skb); break; -- cgit v1.2.3 From 13b7e6a96a005c656d38f3da51581deaf9866375 Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Wed, 8 Jul 2026 16:43:47 +0200 Subject: wifi: mt76: Disable napi when removing device Unloading the mt7915e module with a MT7916 triggers multiples WARN in __netif_napi_del_locked() and in page_pool_disable_direct_recycling() because the driver does not disable the napi before destroying it. This is troublesome since on MT7916 it is required to unload the module and reinsert it with a different enable_6ghz parameter to change the frequency. The system generally becomes unstable after reinserting the module. Fix it by disabling napi before deleting it. Also, do not delete napi on WED queues since napi is neither used nor initialized on them. Fixes: 17f1de56df05 ("mt76: add common code shared between multiple chipsets") Signed-off-by: Nicolas Cavallari Link: https://patch.msgid.link/20260708144615.24092-1-nicolas.cavallari@green-communications.fr Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index f8c2fe5f2f58..322041859217 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -1189,7 +1189,10 @@ void mt76_dma_cleanup(struct mt76_dev *dev) mt76_for_each_q_rx(dev, i) { struct mt76_queue *q = &dev->q_rx[i]; - netif_napi_del(&dev->napi[i]); + if (!mt76_queue_is_wed_rro(q)) { + napi_disable(&dev->napi[i]); + netif_napi_del(&dev->napi[i]); + } mt76_dma_rx_cleanup(dev, q); page_pool_destroy(q->page_pool); -- cgit v1.2.3 From 96ea44f2269f30364cffa054ee3a87e595bef0d4 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 15:55:39 +0800 Subject: wifi: mt76: mt7925: guard link STA in decap offload mt7925_sta_set_decap_offload() iterates over the vif valid_links mask when updating decap offload state for an MLO station. The station may not have a link STA for every valid link of the vif, so mt792x_sta_to_link() can return NULL for a link that belongs to the vif but not to the station. The function currently dereferences mlink before checking whether the link WCID is ready. If mlink is NULL, setting or clearing MT_WCID_FLAG_HDR_TRANS dereferences a NULL pointer. Skip links without a station link before touching mlink->wcid. Fixes: b859ad65309a ("wifi: mt76: mt7925: add link handling in mt7925_sta_set_decap_offload") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260708075539.726200-1-lgs201920130244@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index a9059866b701..2b6cc8e253c0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1713,6 +1713,9 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw, mconf = mt792x_vif_to_link(mvif, i); mlink = mt792x_sta_to_link(msta, i); + if (!mlink) + continue; + if (enabled) set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags); else -- cgit v1.2.3 From 8e9db062654a388d0fa587acbeeae68dd33eba41 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sat, 20 Jun 2026 23:53:32 +0800 Subject: wifi: mt76: mt7915: guard HE capability lookups mt7915_mcu_bss_he_tlv() and mt7915_mcu_sta_bfer_tlv() both run after checking HE support, then dereference the HE PHY capability returned by mt76_connac_get_he_phy_cap(). That helper can return NULL when no capability entry matches the vif type. Fetch the capability before appending the TLV and skip the HE-specific setup when no matching capability is available. Fixes: e6d557a78b60 ("mt76: mt7915: rely on mt76_connac_get_phy utilities") Signed-off-by: Ruoyu Wang Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260620155332.81120-1-ruoyuw560@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 4a381d351e61..e8fe86f93309 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -595,6 +595,8 @@ mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!cap) + return; tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he)); @@ -1177,13 +1179,12 @@ mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy, } static void -mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, - struct mt7915_phy *phy, struct sta_rec_bf *bf) +mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, + const struct ieee80211_sta_he_cap *vc, + struct sta_rec_bf *bf) { struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; - const struct ieee80211_sta_he_cap *vc = - mt76_connac_get_he_phy_cap(phy->mt76, vif); const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); @@ -1242,6 +1243,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_phy *phy = mvif->phy; + const struct ieee80211_sta_he_cap *vc = NULL; int tx_ant = hweight8(phy->mt76->chainmask) - 1; struct sta_rec_bf *bf; struct tlv *tlv; @@ -1260,6 +1262,12 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, if (!ebf && !dev->ibf) return; + if (sta->deflink.he_cap.has_he && ebf) { + vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!vc) + return; + } + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); bf = (struct sta_rec_bf *)tlv; @@ -1268,7 +1276,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, * ht: iBF only, since mac80211 lacks of eBF support */ if (sta->deflink.he_cap.has_he && ebf) - mt7915_mcu_sta_bfer_he(sta, vif, phy, bf); + mt7915_mcu_sta_bfer_he(sta, vc, bf); else if (sta->deflink.vht_cap.vht_supported) mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf); else if (sta->deflink.ht_cap.ht_supported) -- cgit v1.2.3 From 2c1fb2335f5e3afb34f91bc07ecb63517c328090 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 21 Jun 2026 15:24:59 +0200 Subject: wifi: mt76: connac: fix possible NULL-pointer deref in mt76_connac_mcu_uni_bss_he_tlv() mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it. Fixes: d0e274af2f2e4 ("mt76: mt76_connac: create mcu library") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260621-mt76_connac_get_he_phy_cap-fix-v1-1-ed4ccf7a0363@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 6596c9e198f4..58b0b15e4fd6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1458,6 +1458,8 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif, struct bss_info_uni_he *he; cap = mt76_connac_get_he_phy_cap(phy, vif); + if (!cap) + return; he = (struct bss_info_uni_he *)tlv; he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; -- cgit v1.2.3 From 8d1b6738c1ab48c086b17e7994034aca94258931 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 21 Jun 2026 15:25:00 +0200 Subject: wifi: mt76: mt7925: fix possible NULL-pointer deref in mt7925_mcu_bss_he_tlv() mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it. Fixes: c948b5da6bbec ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260621-mt76_connac_get_he_phy_cap-fix-v1-2-ed4ccf7a0363@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index e94fa544ff20..cb265a6fc7ad 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2773,6 +2773,8 @@ mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, link_conf->vif); + if (!cap) + return; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); -- cgit v1.2.3 From e858cf6bf99880343348ff1e8c942aaff1d9d592 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 21 Jun 2026 15:25:01 +0200 Subject: wifi: mt76: mt7996: check pointer returned by mt76_connac_get_he_phy_cap() mt76_connac_get_he_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it. Fixes: 98686cd21624c ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260621-mt76_connac_get_he_phy_cap-fix-v1-3-ed4ccf7a0363@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index f119f023bcd5..c868b1356894 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -935,6 +935,8 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!cap) + return; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); @@ -1855,17 +1857,18 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta, { struct ieee80211_sta_he_cap *pc = &link_sta->he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; - const struct ieee80211_sta_he_cap *vc = - mt76_connac_get_he_phy_cap(phy->mt76, vif); - const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + const struct ieee80211_he_cap_elem *ve; + const struct ieee80211_sta_he_cap *vc; u8 snd_dim, sts; + vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); if (!vc) return; bf->tx_mode = MT_PHY_TYPE_HE_SU; + ve = &vc->he_cap_elem; mt7996_mcu_sta_sounding_rate(bf, phy); -- cgit v1.2.3 From 7fd35e8c0548e97258f64f47c98a891173b8e35e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 6 Jul 2026 16:28:57 -0700 Subject: wifi: mt76: fix MAC address for non OF pcie cards If seems the check for err is wrong as the proper macaddr gets written to from the EEPROM itself. Meaning checking err from of_get_mac_address is wrong as the proper macaddr has been written by this point. Closes: https://lore.kernel.org/linux-wireless/30a90714-02d8-45f2-a7f1-4cfe0627d50b@skade.local/ Reported-by: Klara Modin Closes: https://lore.kernel.org/all/ajRmlyx_AEGybykL@soda.int.kasm.eu/ Reported-by: Tobias Klausmann Fixes: 31ee1582717e ("wifi: mt76: fix of_get_mac_address error handling") Signed-off-by: Rosen Penev Tested-by: Tobias Klausmann Tested-by: Klara Modin Tested-by: John Rowley Link: https://patch.msgid.link/20260706232857.807044-1-rosenp@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c index b99d7452800f..afdb73661866 100644 --- a/drivers/net/wireless/mediatek/mt76/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -181,7 +181,7 @@ mt76_eeprom_override(struct mt76_phy *phy) if (err == -EPROBE_DEFER) return err; - if (err) { + if (!is_valid_ether_addr(phy->macaddr)) { eth_random_addr(phy->macaddr); dev_info(dev->dev, "Invalid MAC address, using random address %pM\n", -- cgit v1.2.3 From 7981aca2bd28a1f7ad7eeab89715442a95b1f72e Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Mon, 20 Jul 2026 16:27:36 -0700 Subject: wifi: mt76: restrict NPU/PPE active checks to MMIO devices mt76_npu_device_active() and mt76_ppe_device_active() read dev->mmio.npu and dev->mmio.ppe_dev. The mmio, usb and sdio bus structs share a union in struct mt76_dev, so on USB and SDIO these read unrelated data from the usb/sdio struct, which is non-NULL in practice. mt76_npu_device_active() then returns true on USB, and mt76_rx_poll_complete() takes the offload path and skips mt76_rx_aggr_reorder(). RX A-MPDU subframes are delivered out of order and the peer's TCP stack treats that as loss: heavy retransmissions and reduced throughput in AP mode. Seen on mt7921u, mt7925u, mt76x2u and mt76x0u. Gate both helpers on mt76_is_mmio() so they only run for the bus type that owns the mmio union member. Fixes: 7fb554b1b623 ("wifi: mt76: Introduce the NPU generic layer") Cc: stable@vger.kernel.org Tested-by: Nick Morrow Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260720232640.41293-1-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 07955555f84d..a32ba7c23f92 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1736,12 +1736,12 @@ static inline int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex, static inline bool mt76_npu_device_active(struct mt76_dev *dev) { - return !!rcu_access_pointer(dev->mmio.npu); + return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.npu); } static inline bool mt76_ppe_device_active(struct mt76_dev *dev) { - return !!rcu_access_pointer(dev->mmio.ppe_dev); + return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.ppe_dev); } static inline int mt76_npu_send_msg(struct airoha_npu *npu, int ifindex, -- cgit v1.2.3 From 7cd57ff6c6263519e6e463cbc2e0898828a70c42 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Jun 2026 22:13:19 +0200 Subject: wifi: mt76: fix airoha_npu dependency tracking There is a new build failure with MT7996E=m MT76_CORE=y and NET_AIROHA_NPU=m: ld.lld: error: undefined symbol: airoha_npu_get ld.lld: error: undefined symbol: airoha_npu_put >>> referenced by npu.c >>> drivers/net/wireless/mediatek/mt76/npu.o:(mt76_npu_init) in archive vmlinux.a Fix this by reworking the dependency for the MT7996_NPU to only allow enabling that when mt76_core can link against the npu driver. To make sure this gets caught more easily in the future when additional mt76 variants need the same dependency, also turn CONFIG_MT76_NPU into a tristate symbol that has the same dependency. Fixes: 7fb554b1b623 ("wifi: mt76: Introduce the NPU generic layer") Acked-by: Lorenzo Bianconi Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260612201519.4054683-1-arnd@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/Kconfig | 4 ++-- drivers/net/wireless/mediatek/mt76/Makefile | 6 +++++- drivers/net/wireless/mediatek/mt76/mt76.h | 2 +- drivers/net/wireless/mediatek/mt76/mt7996/Kconfig | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig index 502303622a53..2ca96e0527c0 100644 --- a/drivers/net/wireless/mediatek/mt76/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -38,8 +38,8 @@ config MT792x_USB select MT76_USB config MT76_NPU - bool - depends on MT76_CORE + tristate + depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig" diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile index 1d42adfe8030..cacdd2b13d05 100644 --- a/drivers/net/wireless/mediatek/mt76/Makefile +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -12,7 +12,11 @@ mt76-y := \ mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \ tx.o agg-rx.o mcu.o wed.o scan.o channel.o -mt76-$(CONFIG_MT76_NPU) += npu.o +ifdef CONFIG_MT76_NPU +# CONFIG_MT76_NPU is tristate to simplify dependency tracking, +# but it behaves as a bool symbol here. +mt76-y += npu.o +endif mt76-$(CONFIG_PCI) += pci.o mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index a32ba7c23f92..3822eb8fd88f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1647,7 +1647,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state); int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len); -#ifdef CONFIG_MT76_NPU +#if IS_ENABLED(CONFIG_MT76_NPU) void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb, u32 info); int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig index 5503d03bf62c..5742bce12fbb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig @@ -16,6 +16,6 @@ config MT7996E config MT7996_NPU bool "MT7996 (PCIe) NPU support" depends on MT7996E - depends on NET_AIROHA_NPU=y || MT7996E=NET_AIROHA_NPU + depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU select MT76_NPU default n -- cgit v1.2.3 From bd8b2ec838184236c3fcbf738a926328836adf12 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Tue, 16 Jun 2026 11:10:16 -0500 Subject: wifi: mt76: mt7925: fix crash in reset link replay During reset recovery, mt7925_vif_connect_iter() replays firmware state for links tracked in mvif->valid_links. After MLO link changes or MCU timeout recovery, the driver bitmap can temporarily contain a link whose mac80211 bss_conf has already gone away. This can pass a NULL bss_conf to mt76_connac_mcu_uni_add_dev(), matching the crash where x1, the second argument, is NULL: pc : mt76_connac_mcu_uni_add_dev+0x8c/0x1f8 [mt76_connac_lib] lr : mt7925_vif_connect_iter+0x9c/0x168 [mt7925_common] x2 : ffffff80a77f6018 x1 : 0000000000000000 x0 : ffffff8099402080 Call trace: mt76_connac_mcu_uni_add_dev+0x8c/0x1f8 [mt76_connac_lib] mt7925_vif_connect_iter+0x9c/0x168 [mt7925_common] mt7925_mac_reset_work+0x264/0x2f8 [mt7925_common] Skip missing bss_conf entries before replaying the link. Non-MLO AP/STA reset replay is unchanged because the helper still returns &vif->bss_conf for the legacy link. Fixes: 14061994184d ("wifi: mt76: mt7925: add link handling in mt7925_vif_connect_iter") Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260616161016.19346-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 2f9871792ea1..6b0cd1996ecb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1288,6 +1288,9 @@ mt7925_vif_connect_iter(void *priv, u8 *mac, for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { bss_conf = mt792x_vif_to_bss_conf(vif, i); + if (!bss_conf) + continue; + mconf = mt792x_vif_to_link(mvif, i); mt76_connac_mcu_uni_add_dev(&dev->mphy, bss_conf, &mconf->mt76, -- cgit v1.2.3 From 2fffc472bec490c8357defcee9c075ca74467352 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 21 Jun 2026 15:25:02 +0200 Subject: wifi: mt76: mt7996: fix possible NULL-pointer deref in mt7996_mcu_sta_bfer_eht() mt76_connac_get_eht_phy_cap routine can theoretically return NULL so check cap pointer before dereferencing it. Fixes: ba01944adee9f ("wifi: mt76: mt7996: add EHT beamforming support") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260621-mt76_connac_get_he_phy_cap-fix-v1-4-ed4ccf7a0363@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index c868b1356894..2e83f4b79c87 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1924,14 +1924,18 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta, struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp; - const struct ieee80211_sta_eht_cap *vc = - mt76_connac_get_eht_phy_cap(phy->mt76, vif); - const struct ieee80211_eht_cap_elem_fixed *ve = &vc->eht_cap_elem; u8 nss_mcs = u8_get_bits(eht_nss->bw._80.rx_tx_mcs9_max_nss, IEEE80211_EHT_MCS_NSS_RX) - 1; + const struct ieee80211_eht_cap_elem_fixed *ve; + const struct ieee80211_sta_eht_cap *vc; u8 snd_dim, sts; + vc = mt76_connac_get_eht_phy_cap(phy->mt76, vif); + if (!vc) + return; + bf->tx_mode = MT_PHY_TYPE_EHT_MU; + ve = &vc->eht_cap_elem; mt7996_mcu_sta_sounding_rate(bf, phy); -- cgit v1.2.3 From 4c92afb4c143526d340545ca581e88e6952ea511 Mon Sep 17 00:00:00 2001 From: Satyanarayana K V P Date: Tue, 21 Jul 2026 10:52:14 +0530 Subject: drm/xe/vf: Add drm_dev guards when detaching CCS read/write buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CCS read/write buffers are freed during BO destruction. In some cases, BOs may be destroyed after the device is unbound but while the DRM structure remains valid, leading to NULL pointer dereferences when accessing device resources. BUG: kernel NULL pointer dereference, address: 0000000000000000 PGD 0 P4D 0 Oops: Oops: 0000 [#1] SMP NOPTI CPU: 0 UID: 0 PID: 9376 Comm: xe_pat Not tainted 7.2.0-rc2+ #1 PREEMPT(lazy) RIP: 0010:xe_sriov_vf_ccs_rw_update_bb_addr+0x4d/0xa0 [xe] RSP: 0018:ffffcf304110b9c8 EFLAGS: 00010246 RAX: ffff8a85c38a0a00 RBX: 00000000810ef000 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff8a85c39c1888 RBP: ffffcf304110b9e8 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: ffff8a85c39c1888 R13: 0000000000000000 R14: ffff8a85c39b4f28 R15: ffff8a85c3885000 FS: 0000000000000000(0000) GS:ffff8a878b809000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 000000010314a002 CR4: 0000000000772ef0 PKRU: 55555554 Call Trace: xe_migrate_ccs_rw_copy_clear+0x98/0x120 [xe] xe_sriov_vf_ccs_detach_bo+0x2c/0x60 [xe] xe_ttm_bo_delete_mem_notify+0xc8/0xe0 [xe] ttm_bo_cleanup_memtype_use+0x26/0x80 [ttm] ttm_bo_release+0x29e/0x2d0 [ttm] ttm_bo_fini+0x39/0x70 [ttm] xe_gem_object_free+0x1f/0x30 [xe] drm_gem_object_free+0x1d/0x40 ttm_bo_vm_close+0x5f/0x90 [ttm] remove_vma+0x2c/0x70 tear_down_vmas+0x63/0xf0 exit_mmap+0x20d/0x3f0 __mmput+0x45/0x170 mmput+0x31/0x40 do_exit+0x2ba/0xac0 do_group_exit+0x2d/0xb0 __x64_sys_exit_group+0x18/0x20 x64_sys_call+0x14a0/0x2390 do_syscall_64+0xdd/0x640 ? count_memcg_events+0xea/0x240 ? handle_mm_fault+0x1ec/0x2f0 Fixes: 864690cf4dd6 ("drm/xe/vf: Attach and detach CCS copy commands with BO") Signed-off-by: Satyanarayana K V P Cc: Matthew Brost Cc: Michal Wajdeczko Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260721052215.2267228-2-satyanarayana.k.v.p@intel.com (cherry picked from commit 1ae415a6eefe5004954a1d352b1718faca8844ef) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_migrate.c | 16 ++++++++++------ drivers/gpu/drm/xe/xe_migrate.h | 3 ++- drivers/gpu/drm/xe/xe_sriov_vf_ccs.c | 14 ++++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index 7d28290e7d1c..be787f331768 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -1313,6 +1313,7 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, * content. * @src_bo: The buffer object @src is currently bound to. * @read_write : Creates BB commands for CCS read/write. + * @bound: Device is bound * * Directly clearing the BB lacks atomicity and can lead to undefined * behavior if the vCPU is halted mid-operation during the clearing @@ -1325,7 +1326,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, * Returns: None. */ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, - enum xe_sriov_vf_ccs_rw_ctxs read_write) + enum xe_sriov_vf_ccs_rw_ctxs read_write, + bool bound) { struct xe_mem_pool_node *bb = src_bo->bb_ccs[read_write]; struct xe_device *xe = xe_bo_device(src_bo); @@ -1339,13 +1341,15 @@ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, bb_pool = ctx->mem.ccs_bb_pool; scoped_guard(mutex, xe_mem_pool_bo_swap_guard(bb_pool)) { - xe_mem_pool_swap_shadow_locked(bb_pool); + if (bound) { + xe_mem_pool_swap_shadow_locked(bb_pool); - cs = xe_mem_pool_node_cpu_addr(bb); - memset(cs, MI_NOOP, bb->sa_node.size); - xe_sriov_vf_ccs_rw_update_bb_addr(ctx); + cs = xe_mem_pool_node_cpu_addr(bb); + memset(cs, MI_NOOP, bb->sa_node.size); + xe_sriov_vf_ccs_rw_update_bb_addr(ctx); - xe_mem_pool_sync_shadow_locked(bb); + xe_mem_pool_sync_shadow_locked(bb); + } xe_mem_pool_free_node(bb); src_bo->bb_ccs[read_write] = NULL; } diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index 78e5b63f3ebe..c3a268b01768 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -142,7 +142,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q, enum xe_sriov_vf_ccs_rw_ctxs read_write); void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo, - enum xe_sriov_vf_ccs_rw_ctxs read_write); + enum xe_sriov_vf_ccs_rw_ctxs read_write, + bool bound); struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate); struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate); diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c index 6787564629c6..a8c831fbee3b 100644 --- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c +++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c @@ -3,6 +3,8 @@ * Copyright © 2025 Intel Corporation */ +#include + #include "instructions/xe_mi_commands.h" #include "instructions/xe_gpu_commands.h" #include "xe_bb.h" @@ -446,7 +448,7 @@ err_unwind: */ for_each_ccs_rw_ctx(ctx_id) { if (bo->bb_ccs[ctx_id]) - xe_migrate_ccs_rw_copy_clear(bo, ctx_id); + xe_migrate_ccs_rw_copy_clear(bo, ctx_id, true); } return err; } @@ -466,19 +468,27 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo) struct xe_device *xe = xe_bo_device(bo); enum xe_sriov_vf_ccs_rw_ctxs ctx_id; struct xe_mem_pool_node *bb; + bool bound; + int idx; xe_assert(xe, IS_VF_CCS_READY(xe)); if (!xe_bo_has_valid_ccs_bb(bo)) return 0; + bound = drm_dev_enter(&xe->drm, &idx); + for_each_ccs_rw_ctx(ctx_id) { bb = bo->bb_ccs[ctx_id]; if (!bb) continue; - xe_migrate_ccs_rw_copy_clear(bo, ctx_id); + xe_migrate_ccs_rw_copy_clear(bo, ctx_id, bound); } + + if (bound) + drm_dev_exit(idx); + return 0; } -- cgit v1.2.3 From 1f1be4ba643fb67410d753111c37ae42a2e2a814 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 21 Jul 2026 13:43:53 -0700 Subject: drm/pagemap: Clear driver-provided PFNs from migration PFN array DRM pagemap overloads the migration PFN array to store driver-provided PFNs before calling migrate_vma_*() to finalize the migration. If an error occurs during the incremental copy phase, the migration PFN entries are reverted to their original state. After reverting the device-folio mutations, clear any remaining driver-provided PFNs to avoid confusing the migrate_vma_*() helpers. Also clear any driver-provided PFNs if populate_devmem_pfn() fails, as a precaution against stale entries being interpreted as migration PFNs. Reported-by: Sashiko Fixes: 3902846af36b ("drm/pagemap Fix error paths in drm_pagemap_migrate_to_devmem") Fixes: ec265e1f1cfc ("drm/pagemap: Support source migration over interconnect") Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260721204353.1082632-1-matthew.brost@intel.com --- drivers/gpu/drm/drm_pagemap.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 15c78eca180b..892b325fa99b 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -727,8 +727,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, } err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst); - if (err) - goto err_aborted_migration; + if (err) { + npages = 0; + goto err_finalize; + } own_pages = 0; @@ -807,8 +809,11 @@ next: msecs_to_jiffies(mdetails->timeslice_ms); err_finalize: - if (err) + if (err) { drm_pagemap_migration_unlock_put_pages(npages, migrate.dst); + for (i = npages; i < npages_in_range(start, end); ++i) + migrate.dst[i] = 0; + } err_aborted_migration: migrate_vma_pages(&migrate); -- cgit v1.2.3 From 04b177544a040cbafab760d6b766381c6b22e0a8 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 21 Jul 2026 15:32:44 -0700 Subject: drm/pagemap: Guard HPAGE_PMD_ORDER use with CONFIG_ARCH_ENABLE_THP_MIGRATION HPAGE_PMD_SHIFT expands to BUILD_BUG() when CONFIG_PGTABLE_HAS_HUGE_LEAVES is not set, causing a compile error when both CONFIG_TRANSPARENT_HUGEPAGE and CONFIG_HUGETLB_PAGE are disabled: drivers/gpu/drm/drm_pagemap.c:480:12: error: call to '__compiletime_assert_458' declared with 'error' attribute: BUILD_BUG failed 480 | order = HPAGE_PMD_ORDER; | ^ include/linux/huge_mm.h:117:26: note: expanded from macro 'HPAGE_PMD_ORDER' 117 | #define HPAGE_PMD_ORDER (HPAGE_PMD_SHIFT-PAGE_SHIFT) | ^ include/linux/huge_mm.h:113:28: note: expanded from macro 'HPAGE_PMD_SHIFT' 113 | #define HPAGE_PMD_SHIFT ({ BUILD_BUG(); 0; }) Define DRM_PAGEMAP_PMD_ORDER, which maps to HPAGE_PMD_ORDER when CONFIG_ARCH_ENABLE_THP_MIGRATION is enabled and to -1 otherwise. This is safe because all code paths that use DRM_PAGEMAP_PMD_ORDER are reachable only when CONFIG_ARCH_ENABLE_THP_MIGRATION is enabled. Fixes: 139ab31aea8a ("drm/pagemap: Correct cpages calculation for migrate_vma_setup") Reported-by: kernel test robot Closes: https://lore.kernel.org/r/202607201914.LpAGsbXs-lkp@intel.com/ Cc: Jan Stancek Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Tested-by: Jan Stancek Reviewed-by: Himal Prasad Ghimiray Link: https://patch.msgid.link/20260721223244.1102276-1-matthew.brost@intel.com --- drivers/gpu/drm/drm_pagemap.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 892b325fa99b..4a794544b7dc 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -12,6 +12,12 @@ #include #include +#if IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION) +#define DRM_PAGEMAP_PMD_ORDER HPAGE_PMD_ORDER +#else +#define DRM_PAGEMAP_PMD_ORDER (-1) +#endif + /** * DOC: Overview * @@ -579,7 +585,7 @@ static int drm_pagemap_cpages(unsigned long *migrate_pfn, unsigned long npages) order = folio_order(folio); cpages += NR_PAGES(order); } else if (migrate_pfn[i] & MIGRATE_PFN_COMPOUND) { - order = HPAGE_PMD_ORDER; + order = DRM_PAGEMAP_PMD_ORDER; cpages += NR_PAGES(order); } @@ -767,10 +773,11 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, if (migrate.src[i] & MIGRATE_PFN_COMPOUND) { drm_WARN_ONCE(dpagemap->drm, src_page && - folio_order(page_folio(src_page)) != HPAGE_PMD_ORDER, + folio_order(page_folio(src_page)) != + DRM_PAGEMAP_PMD_ORDER, "Unexpected folio order\n"); - order = HPAGE_PMD_ORDER; + order = DRM_PAGEMAP_PMD_ORDER; migrate.dst[i] |= MIGRATE_PFN_COMPOUND; for (j = 1; j < NR_PAGES(order) && i + j < npages; j++) -- cgit v1.2.3 From e4779e2a16d600892aaf743438f6ce8cc4eb3c4c Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Mon, 20 Jul 2026 21:08:30 +0530 Subject: phy: zynqmp: fix clock error handling in xpsgtr_phy_init() Propagate clk_prepare_enable() failures to the caller instead of returning success, and disable the reference clock on initialization error paths to avoid leaking clock references when phy_exit() is not called. Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") Signed-off-by: Radhey Shyam Pandey Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index fe6b4925d166..c8230f2bda62 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy) { struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); struct xpsgtr_dev *gtr_dev = gtr_phy->dev; - int ret = 0; + int ret; mutex_lock(>r_dev->gtr_mutex); /* Configure and enable the clock when peripheral phy_init call */ - if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) + ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); + if (ret) goto out; /* Skip initialization if not required. */ @@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy) if (gtr_dev->tx_term_fix) { ret = xpsgtr_phy_tx_term_fix(gtr_phy); if (ret < 0) - goto out; + goto out_disable_clk; gtr_dev->tx_term_fix = false; } @@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy) */ ret = xpsgtr_configure_pll(gtr_phy); if (ret) - goto out; + goto out_disable_clk; xpsgtr_lane_set_protocol(gtr_phy); @@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy) break; } + goto out; + +out_disable_clk: + clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); out: mutex_unlock(>r_dev->gtr_mutex); return ret; -- cgit v1.2.3 From f3506e15cf72e94f62d5f2d173e5b7008f644cde Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Mon, 20 Jul 2026 21:08:31 +0530 Subject: phy: zynqmp: fix runtime PM leak on probe allocation failure Allocate saved_regs before pm_runtime_resume_and_get() so a devm_kmalloc() failure does not leave an unreleased runtime PM usage counter. Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") Signed-off-by: Radhey Shyam Pandey Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index c8230f2bda62..2138f5399821 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev) return PTR_ERR(provider); } + gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, + sizeof(save_reg_address), + GFP_KERNEL); + if (!gtr_dev->saved_regs) + return -ENOMEM; + pm_runtime_set_active(gtr_dev->dev); pm_runtime_enable(gtr_dev->dev); @@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev) return ret; } - gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, - sizeof(save_reg_address), - GFP_KERNEL); - if (!gtr_dev->saved_regs) - return -ENOMEM; - return 0; } -- cgit v1.2.3 From 7cb34f6c4fe8a68af621d870abe63bfca2275dd6 Mon Sep 17 00:00:00 2001 From: Shelley Yang Date: Mon, 25 May 2026 16:38:59 +0800 Subject: wifi: brcmfmac: fix 802.1X-SHA256 call trace warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on wpa_auth as 1x_256 mode, need to set up "use_fwsup" with BRCMF_PROFILE_FWSUP_1X. Or it will happen trace warning when call brcmf_cfg80211_set_pmk(). [ 4481.831101] ------------[ cut here ]------------ [ 4481.831102] WARNING: CPU: 1 PID: 2997 at drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c:7242 brcmf_cfg80211_set_pmk+0x77/0xd0 [brcmfmac] [...] [ 4481.831202] Call Trace: [ 4481.831204]   [ 4481.831205]  nl80211_set_pmk+0x183/0x250 [cfg80211] [ 4481.831233]  genl_family_rcv_msg_doit+0xea/0x150 [ 4481.831237]  genl_rcv_msg+0x104/0x240 [ 4481.831239]  ? cfg80211_probe_status+0x2c0/0x2c0 [cfg80211] [ 4481.831257]  ? genl_family_rcv_msg_doit+0x150/0x150 [ 4481.831259]  netlink_rcv_skb+0x4e/0x100 [ 4481.831261]  genl_rcv+0x24/0x40 [ 4481.831262]  netlink_unicast+0x236/0x380 [ 4481.831264]  netlink_sendmsg+0x250/0x4b0 [ 4481.831266]  sock_sendmsg+0x5c/0x70 [ 4481.831269]  ____sys_sendmsg+0x236/0x2b0 [ 4481.831271]  ? copy_msghdr_from_user+0x6d/0xa0 [ 4481.831272]  ___sys_sendmsg+0x86/0xd0 [ 4481.831274]  ? avc_has_perm+0x8c/0x1a0 [ 4481.831276]  ? preempt_count_add+0x6a/0xa0 [ 4481.831279]  ? sock_has_perm+0x82/0xa0 [ 4481.831280]  __sys_sendmsg+0x57/0xa0 [ 4481.831282]  do_syscall_64+0x38/0x90 [ 4481.831284]  entry_SYSCALL_64_after_hwframe+0x63/0xcd [ 4481.831286] RIP: 0033:0x7fd270d369b4 Fixes: 2526ff21aa77 ("brcmfmac: support 4-way handshake offloading for 802.1X") Signed-off-by: Shelley Yang Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260525083859.581246-1-shelley.yang@infineon.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 0b55d445895f..89f61710a210 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2146,7 +2146,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) sme->crypto.akm_suites[0]); return -EINVAL; } - } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_1X_SHA256)) { switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA2_AUTH_UNSPECIFIED; -- cgit v1.2.3 From faaddd811c5099f11a5f52e68a6b31a5898cda4f Mon Sep 17 00:00:00 2001 From: Sunho Park Date: Tue, 14 Jul 2026 19:42:30 +0900 Subject: USB: serial: io_edgeport: cap received transmit credits The interrupt-status packet reports transmit credits returned by the device. edge_interrupt_callback() adds the 16-bit value to txCredits without checking maxTxCredits. edge_write() uses txCredits minus the software FIFO count as the amount of data that fits. Since the FIFO is allocated with maxTxCredits bytes, txCredits exceeding maxTxCredits can cause OOB write in ring buffer. Cap accumulated credits at maxTxCredits. Conforming devices should never hit the cap. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5 Signed-off-by: Sunho Park Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 34ccf7820537..eaa8f00716c8 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -646,7 +646,8 @@ static void edge_interrupt_callback(struct urb *urb) if (edge_port && edge_port->open) { spin_lock_irqsave(&edge_port->ep_lock, flags); - edge_port->txCredits += txCredits; + edge_port->txCredits = min(edge_port->txCredits + txCredits, + edge_port->maxTxCredits); spin_unlock_irqrestore(&edge_port->ep_lock, flags); dev_dbg(dev, "%s - txcredits for port%d = %d\n", -- cgit v1.2.3 From b5e24d6377f9062aff79c3c493bcbe923deb380c Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Wed, 22 Jul 2026 04:07:07 +0000 Subject: ALSA: usb-audio: Add iface reset and delay quirk for Generic USB Headphone Setting up the interface when suspended/resuming fails on this card. Adding a reset and delay quirk will eliminate this problem. usb 1-1: New USB device found, idVendor=0124, idProduct=0c21 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: USB Headphone usb 1-1: Manufacturer: Generic usb 1-1: SerialNumber: 20210726905926 Signed-off-by: Lianqin Hu Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/TYUPR06MB6217CBB68C8F868C076A4353D2C12@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 0de7c9d94259..49b56287cdb9 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2208,6 +2208,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x0020, 0x0b21, /* GHW-123P */ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x0124, 0x0c21, /* Generic USB Headphone */ + 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 */ -- cgit v1.2.3 From 704de5697008ea5caa04bb6a7266624699801ddc Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 21 Jul 2026 19:35:38 +0800 Subject: ALSA: hda/realtek: Add quirk for HP Pavilion All-in-One 27-ca1xxx This HP All-in-One desktop (model 27-ca1xxx) uses an ALC274 codec and has no sound from either the internal speakers or the headphone jack. The audio output requires a combination of a specific verb setup and GPIO configuration to become functional. Additionally, the power_save feature must be disabled; otherwise, audible clicks or pops occur during power state transitions. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220694 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260721113539.317561-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 35 +++++++++++++++++++++++++++++++++++ sound/hda/controllers/intel.c | 2 ++ 2 files changed, 37 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 379e1458f4ac..fcc60e64137d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3863,6 +3863,16 @@ static void alc288_fixup_surface_swap_dacs(struct hda_codec *codec, spec->gen.preferred_dacs = preferred_pairs; } +static void alc274_fixup_hp_89e9_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_INIT) { + /* need to toggle GPIO to enable the amp */ + snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x03, 0); + msleep(100); + snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x00, 0); + } +} enum { ALC269_FIXUP_GPIO2, ALC269_FIXUP_SONY_VAIO, @@ -4213,6 +4223,8 @@ enum { ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX, ALC287_FIXUP_ACER_MICMUTE_LED, ALC236_FIXUP_DELL_HP_POP_NOISE, + ALC274_FIXUP_HP_89E9_GPIO, + ALC274_FIXUP_HP_VERBS, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6855,6 +6867,28 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_invalidate_dacs, }, + [ALC274_FIXUP_HP_89E9_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_hp_89e9_amp, + }, + [ALC274_FIXUP_HP_VERBS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc580 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5757 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0060 }, + { } + }, + .chained = true, + .chain_id = ALC274_FIXUP_HP_89E9_GPIO, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -7231,6 +7265,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { 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, 0x8a6b, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS), 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), diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 4b03c64e72ab..28c55c5a2d2e 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2306,6 +2306,8 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0), /* https://bugzilla.kernel.org/show_bug.cgi?id=220210 */ SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0), + /* https://bugzilla.kernel.org/show_bug.cgi?id=220694 */ + SND_PCI_QUIRK(0x103c, 0x8a6b, "HP 89E9", 0), {} }; -- cgit v1.2.3 From f73a8edc2ccc6ec72c37d5c578e7592d2e1f9922 Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Fri, 3 Jul 2026 11:41:54 +0000 Subject: arm64: make huge_ptep_get handled unaligned addresses huge_ptep_get() can be handed a virtual address pointing to the middle of a contpmd/contpte mapped hugetlb folio (examples of callers are pagemap_hugetlb_range, page_mapped_in_vma). The arm64 helper rewalks the pgtables in find_num_contig to answer whether the huge pte we have maps a contpmd or a contpte hugetlb folio, and returns CONT_PMDS or CONT_PTES, so that it can collect a/d bits over the contiguous ptes. We can falsely return CONT_PTES instead of CONT_PMDS if the addr is not aligned. On systems where CONT_PTES != CONT_PMDS (meaning page size is 16K), we could collect excess A/D bit state, meaning extra work for the kernel. Even worse, we may iterate beyond the PTE table and dereference a garbage ptep pointer to access physical memory we don't own. Since the ptep pointer is a linear map address, we may run off the end of the linear map or into a hole, dereference a VA not mapped into the kernel pgtables and cause kernel panic. Fix this by aligning the pmdp pointer down to a contpmd base before checking equality with the passed huge pte pointer, to correctly answer whether the huge pte is the base of a contpmd block. Fixes: 29cb80519689 ("arm64: hugetlb: Cleanup huge_pte size discovery mechanisms") Cc: stable@vger.kernel.org Acked-by: David Hildenbrand (Arm) Signed-off-by: Dev Jain Acked-by: Muchun Song Signed-off-by: Will Deacon --- arch/arm64/mm/hugetlbpage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index 30772a909aea..8e799c1fe0aa 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -87,7 +87,7 @@ static int find_num_contig(struct mm_struct *mm, unsigned long addr, p4dp = p4d_offset(pgdp, addr); pudp = pud_offset(p4dp, addr); pmdp = pmd_offset(pudp, addr); - if ((pte_t *)pmdp == ptep) { + if ((pte_t *)PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS) == ptep) { *pgsize = PMD_SIZE; return CONT_PMDS; } -- cgit v1.2.3 From a8e72879cd0d8422c0b47d6d3c1802274fe73b98 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Wed, 8 Jul 2026 16:22:21 +0800 Subject: ovl: fix trusted xattr escape prefix matching In the trusted.* xattr namespace, ovl_is_escaped_xattr() compares one byte less than the escaped overlay xattr prefix length. This makes it match "trusted.overlay.overlay" without requiring the trailing dot. As a result, an xattr such as "trusted.overlay.overlayfoo" is incorrectly treated as an escaped overlay xattr. This can be reproduced by setting "trusted.overlay.overlayfoo" on a lower file and listing xattrs through an overlay mount. listxattr() then exposes it as "trusted.overlay.oo", and a following getxattr() on that listed name fails with ENODATA. Compare the full escaped prefix, including the trailing dot, so similarly-prefixed private xattrs are not misclassified. Fixes: dad02fad84cbc ("ovl: Support escaped overlay.* xattrs") Signed-off-by: Yichong Chen Link: https://patch.msgid.link/20260708082221.633602-1-chenyichong@uniontech.com Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner (Amutable) --- fs/overlayfs/xattrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c index aa95855c7023..859e80ae6f40 100644 --- a/fs/overlayfs/xattrs.c +++ b/fs/overlayfs/xattrs.c @@ -13,7 +13,7 @@ static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name) OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0; else return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX, - OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0; + OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN) == 0; } static bool ovl_is_own_xattr(struct super_block *sb, const char *name) -- cgit v1.2.3 From bb6bc13c53e211d9148ed2eab3e689c5cd5c75da Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 22 Jul 2026 13:50:53 +0200 Subject: pidfs: preserve thread pidfds reopened by file handle PIDFD_THREAD shares O_EXCL. do_dentry_open() clears O_EXCL after pidfs_export_open() validates the flags, so open_by_handle_at() silently turns a thread pidfd into a process pidfd. Restore PIDFD_THREAD on the opened file, matching pidfs_alloc_file(). Signed-off-by: Li Chen Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260716052726.1032092-1-me@linux.beauty Signed-off-by: Christian Brauner (Amutable) --- fs/pidfs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index aaa609ddab04..c20ffd747ff5 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -939,12 +939,18 @@ static int pidfs_export_permission(struct handle_to_path_ctx *ctx, static struct file *pidfs_export_open(const struct path *path, unsigned int oflags) { + struct file *file; + /* * Clear O_LARGEFILE as open_by_handle_at() forces it and raise * O_RDWR as pidfds always are. */ oflags &= ~O_LARGEFILE; - return dentry_open(path, oflags | O_RDWR, current_cred()); + file = dentry_open(path, oflags | O_RDWR, current_cred()); + /* do_dentry_open() strips O_EXCL, which encodes PIDFD_THREAD. */ + if (!IS_ERR(file)) + file->f_flags |= oflags & PIDFD_THREAD; + return file; } static const struct export_operations pidfs_export_operations = { -- cgit v1.2.3 From 58af123ec7aa7eab19e938b3777c41c02abda1b8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 22 Jul 2026 13:50:53 +0200 Subject: selftests/pidfd: check PIDFD_THREAD survives open_by_handle_at() Verify that a thread pidfd reopened via open_by_handle_at() still reports PIDFD_THREAD in F_GETFL. Signed-off-by: Li Chen Reviewed-by: Jan Kara Link: https://patch.msgid.link/20260716052726.1032092-1-me@linux.beauty Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/pidfd/pidfd_file_handle_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/pidfd/pidfd_file_handle_test.c b/tools/testing/selftests/pidfd/pidfd_file_handle_test.c index 68918734dcf3..1e03ae9575fe 100644 --- a/tools/testing/selftests/pidfd/pidfd_file_handle_test.c +++ b/tools/testing/selftests/pidfd/pidfd_file_handle_test.c @@ -373,6 +373,7 @@ TEST_F(file_handle, open_by_handle_at_valid_flags) O_CLOEXEC | O_EXCL); ASSERT_GE(pidfd, 0); + ASSERT_NE(fcntl(pidfd, F_GETFL) & PIDFD_THREAD, 0); ASSERT_EQ(fstat(pidfd, &st2), 0); ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino); -- cgit v1.2.3 From 927b89700e9fdba61902f8828dbf9b5f29f40ea7 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 22 Jul 2026 13:56:28 +0200 Subject: pidfs: add pidfs_dentry_open() helper Both pidfs_alloc_file() and pidfs_export_open() need to force O_RDWR and reapply the pidfd flags that do_dentry_open() strips. Move the common logic into a helper. PIDFD_AUTOKILL is now part of the restore mask in the file handle path as well, but pidfs_export_permission() rejects O_TRUNC, so this is a no-op there. But warn nonetheless. Link: https://patch.msgid.link/20260722-esszimmer-umsetzen-nennt-ed5fc604300a@brauner Signed-off-by: Christian Brauner (Amutable) --- fs/pidfs.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index c20ffd747ff5..695215aa2a58 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -915,6 +915,20 @@ static struct dentry *pidfs_fh_to_dentry(struct super_block *sb, return path.dentry; } +static struct file *pidfs_dentry_open(const struct path *path, + unsigned int flags, + const struct cred *cred) +{ + struct file *file; + + /* pidfds are always O_RDWR. */ + file = dentry_open(path, flags | O_RDWR, cred); + /* do_dentry_open() strips O_EXCL and O_TRUNC. */ + if (!IS_ERR(file)) + file->f_flags |= flags & (PIDFD_THREAD | PIDFD_AUTOKILL); + return file; +} + /* * Make sure that we reject any nonsensical flags that users pass via * open_by_handle_at(). Note that PIDFD_THREAD is defined as O_EXCL, and @@ -939,18 +953,14 @@ static int pidfs_export_permission(struct handle_to_path_ctx *ctx, static struct file *pidfs_export_open(const struct path *path, unsigned int oflags) { - struct file *file; - /* - * Clear O_LARGEFILE as open_by_handle_at() forces it and raise - * O_RDWR as pidfds always are. + * Opening via file handle may never raise PIDFD_AUTOKILL. That can + * only be done at task creation! */ - oflags &= ~O_LARGEFILE; - file = dentry_open(path, oflags | O_RDWR, current_cred()); - /* do_dentry_open() strips O_EXCL, which encodes PIDFD_THREAD. */ - if (!IS_ERR(file)) - file->f_flags |= oflags & PIDFD_THREAD; - return file; + if (WARN_ON_ONCE(oflags & PIDFD_AUTOKILL)) + return ERR_PTR(-EINVAL); + /* Clear O_LARGEFILE as open_by_handle_at() forces it. */ + return pidfs_dentry_open(path, oflags & ~O_LARGEFILE, current_cred()); } static const struct export_operations pidfs_export_operations = { @@ -1114,7 +1124,6 @@ static struct file_system_type pidfs_type = { struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags) { - struct file *pidfd_file; struct path path __free(path_put) = {}; int ret; @@ -1132,16 +1141,7 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags) VFS_WARN_ON_ONCE(!pid->attr); flags &= ~PIDFD_STALE; - flags |= O_RDWR; - pidfd_file = dentry_open(&path, flags, current_cred()); - /* - * Raise PIDFD_THREAD and PIDFD_AUTOKILL explicitly as - * do_dentry_open() strips O_EXCL and O_TRUNC. - */ - if (!IS_ERR(pidfd_file)) - pidfd_file->f_flags |= (flags & (PIDFD_THREAD | PIDFD_AUTOKILL)); - - return pidfd_file; + return pidfs_dentry_open(&path, flags, current_cred()); } void __init pidfs_init(void) -- cgit v1.2.3 From 1acef6d85bfd98bd9dfe1f08bffa397a4dda8a6f Mon Sep 17 00:00:00 2001 From: Karthikeyan KS Date: Fri, 12 Jun 2026 19:07:43 +0000 Subject: soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read put_fifo_with_discard() acts as both producer and consumer on the kfifo: it calls kfifo_skip() (advances out) and kfifo_put() (advances in) from the IRQ handler without synchronizing with snoop_file_read(), which also consumes via kfifo_to_user(). On SMP systems this concurrent access can leave (in - out) larger than the ring buffer, so __kfifo_to_user()'s clamp to (in - out) is ineffective and kfifo_copy_to_user() can attempt a copy_to_user() past the kmalloc-2k backing store: usercopy: Kernel memory exposure attempt detected from SLUB object 'kmalloc-2k' (offset 0, size 2049)! kernel BUG at mm/usercopy.c! Call trace: usercopy_abort __check_heap_object __check_object_size kfifo_copy_to_user __kfifo_to_user snoop_file_read vfs_read Serialize kfifo access with a per-channel spinlock shared between the IRQ handler (producer) and the file reader (consumer). Annotate @fifo with __guarded_by(&lock) and opt the driver into context analysis so the compiler enforces that all fifo access holds the lock. Fixes: 3772e5da4454 ("drivers/misc: Aspeed LPC snoop output using misc chardev") Signed-off-by: Karthikeyan KS Signed-off-by: Andrew Jeffery --- drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-lpc-snoop.c | 38 ++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index b35d74592964..b5188dcde37a 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o +CONTEXT_ANALYSIS_aspeed-lpc-snoop.o := y diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c index 28e491fffc7d..b09dca34099f 100644 --- a/drivers/soc/aspeed/aspeed-lpc-snoop.c +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -74,7 +75,8 @@ struct aspeed_lpc_snoop_channel_cfg { struct aspeed_lpc_snoop_channel { const struct aspeed_lpc_snoop_channel_cfg *cfg; bool enabled; - struct kfifo fifo; + spinlock_t lock; + struct kfifo fifo __guarded_by(&lock); wait_queue_head_t wq; struct miscdevice miscdev; }; @@ -114,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file); + u8 *buf __free(kfree) = NULL; unsigned int copied; int ret = 0; @@ -125,9 +128,16 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer, if (ret == -ERESTARTSYS) return -EINTR; } - ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); - if (ret) - return ret; + + count = min_t(size_t, count, SNOOP_FIFO_SIZE); + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + copied = kfifo_out_spinlocked(&chan->fifo, buf, count, &chan->lock); + if (copied && copy_to_user(buffer, buf, copied)) + return -EFAULT; return copied; } @@ -151,11 +161,13 @@ static const struct file_operations snoop_fops = { /* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val) { - if (!kfifo_initialized(&chan->fifo)) - return; - if (kfifo_is_full(&chan->fifo)) - kfifo_skip(&chan->fifo); - kfifo_put(&chan->fifo, val); + scoped_guard(spinlock, &chan->lock) { + if (!kfifo_initialized(&chan->fifo)) + return; + if (kfifo_is_full(&chan->fifo)) + kfifo_skip(&chan->fifo); + kfifo_put(&chan->fifo, val); + } wake_up_interruptible(&chan->wq); } @@ -239,9 +251,11 @@ static int aspeed_lpc_enable_snoop(struct device *dev, if (!channel->miscdev.name) return -ENOMEM; - rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL); - if (rc) - return rc; + scoped_guard(spinlock_init, &channel->lock) { + rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + } rc = misc_register(&channel->miscdev); if (rc) -- cgit v1.2.3 From 7a6004c230a7931569666a235e99d443b2319b66 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 17 Jul 2026 08:14:30 -0300 Subject: mailmap: Update Maíra Canal's email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My university email will cease to exist in the next few days, so map it to a personal email address. Reviewed-by: André Almeida Acked-by: Maíra Canal Link: https://patch.msgid.link/20260717111559.2759217-1-maira.canal@usp.br Signed-off-by: Maíra Canal --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 12f3acdebd72..e8fa40478028 100644 --- a/.mailmap +++ b/.mailmap @@ -539,6 +539,7 @@ Maciej W. Rozycki Maciej W. Rozycki Maharaja Kennadyrajan Maheshwar Ajja +Maíra Canal Malathi Gottam Manikanta Pubbisetty Manivannan Sadhasivam -- cgit v1.2.3 From 87b2a82e135ba81e49d82b59e3d72468cb66ea98 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 16 Jul 2026 17:22:45 +0800 Subject: xfs: clear zapped attr fork state when bmap repair finds no attr fork xfstests xfs/377 can make xfs_scrub repeatedly check and repair the attr block map after inode repair zaps an attr fork. When inode repair zaps an attr fork, it records XFS_SICK_INO_BMBTA_ZAPPED so that scrub/repair can revisit the attr fork block map. If the fork has been reset to an empty state and removed, BMBTA repair has no attr fork mappings to rebuild and can return success. The post-repair scrub then runs with XREP_ALREADY_FIXED set, which means xchk_file_looks_zapped() deliberately ignores the stale zapped health bit and asks xchk_bmap() to check the current attr fork. For an absent attr fork, xchk_bmap() returns -ENOENT. Returning that error prevents xchk_bmap_attr() from marking XFS_SICK_INO_BMBTA_ZAPPED healthy, leaving the zapped health state behind even though there are no attr fork mappings left to check. Treat -ENOENT during post-repair BMBTA revalidation as a clean result for the zapped attr fork: clear XFS_SICK_INO_BMBTA_ZAPPED and return success. Keep the existing -ENOENT behavior for ordinary scrubs of absent attr forks. Fixes: d9041681dd2f ("xfs: set inode sick state flags when we zap either ondisk fork") Cc: stable@vger.kernel.org # v6.8 Reviewed-by: "Darrick J. Wong" Signed-off-by: Qiang Ma Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/bmap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c index 70028da1aacc..401c278725d2 100644 --- a/fs/xfs/scrub/bmap.c +++ b/fs/xfs/scrub/bmap.c @@ -1170,6 +1170,11 @@ xchk_bmap_attr( } error = xchk_bmap(sc, XFS_ATTR_FORK); + /* A repaired, empty attr fork no longer has mappings to check. */ + if (error == -ENOENT && (sc->flags & XREP_ALREADY_FIXED)) { + xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_BMBTA_ZAPPED); + return 0; + } if (error) return error; -- cgit v1.2.3 From cc3144da377de5fb422d44a2311f978623f7c900 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 14 Jul 2026 14:56:12 -0700 Subject: xfs: fix off-by-one in rtrefcount btree root level validation xfs_rtrefcountbt_compute_maxlevels() sets mp->m_rtrefc_maxlevels = min(d_maxlevels, r_maxlevels) + 1; where the trailing "+ 1" already accounts for the inode-root level, so the deepest valid on-disk root level is m_rtrefc_maxlevels - 1 and a cursor must satisfy bc_nlevels <= bc_maxlevels (= m_rtrefc_maxlevels). The two on-disk validation paths, xfs_rtrefcountbt_verify() and xfs_iformat_rtrefcount(), check the root level with ">" instead of ">=", so a crafted rtreflink (metadir + realtime + reflink) image whose /rtgroups/N.refcount inode has bb_level == m_rtrefc_maxlevels is accepted on mount. xfs_rtrefcountbt_init_cursor() then sets bc_nlevels = bb_level + 1, exceeding bc_maxlevels by one. Since the xfs_rtrefcountbt_cur slab object is sized for exactly bc_maxlevels entries, the first btree op on such a cursor indexes bc_levels[m_rtrefc_maxlevels] past the end of the object. This is reached by the first rtrefcount cursor built after mount, via log/CoW recovery (xfs_reflink_recover_cow() during xfs_mountfs()) or an FS_IOC_GETFSMAP over the realtime device. Reject a root level equal to m_rtrefc_maxlevels, matching the ">=" form already used by the sibling data-device refcount/rmap verifiers and the in-memory rtrmap verifier. BUG: KASAN: slab-out-of-bounds in xfs_btree_lookup (fs/xfs/libxfs/xfs_btree.c:2101) Write of size 2 at addr ffff888018391658 by task exploit/144 xfs_btree_lookup (fs/xfs/libxfs/xfs_btree.c:2101) xfs_btree_query_range (fs/xfs/libxfs/xfs_btree.c:5308) xfs_refcount_recover_cow_leftovers (fs/xfs/libxfs/xfs_refcount.c:2113) xfs_reflink_recover_cow (fs/xfs/xfs_reflink.c:1085) xlog_recover_finish (fs/xfs/xfs_log_recover.c:3551) xfs_mountfs (fs/xfs/xfs_mount.c:1158) xfs_fs_fill_super (fs/xfs/xfs_super.c:1940) get_tree_bdev_flags (fs/super.c:1634) vfs_get_tree (fs/super.c:1694) path_mount (fs/namespace.c:4161) __x64_sys_mount (fs/namespace.c:4367) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) The buggy address belongs to the cache xfs_rtrefcountbt_cur of size 216 The buggy address is located 8 bytes to the right of allocated 216-byte region [ffff888018391578, ffff888018391650) Kernel panic - not syncing: Fatal exception Cc: stable@vger.kernel.org # v6.14 Fixes: 9abe03a0e4f978 ("xfs: introduce realtime refcount btree ondisk definitions") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/libxfs/xfs_rtrefcount_btree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_rtrefcount_btree.c b/fs/xfs/libxfs/xfs_rtrefcount_btree.c index f27b80a199ba..22acc1411aac 100644 --- a/fs/xfs/libxfs/xfs_rtrefcount_btree.c +++ b/fs/xfs/libxfs/xfs_rtrefcount_btree.c @@ -201,7 +201,7 @@ xfs_rtrefcountbt_verify( if (fa) return fa; level = be16_to_cpu(block->bb_level); - if (level > mp->m_rtrefc_maxlevels) + if (level >= mp->m_rtrefc_maxlevels) return __this_address; return xfs_btree_fsblock_verify(bp, mp->m_rtrefc_mxr[level != 0]); @@ -651,7 +651,7 @@ xfs_iformat_rtrefcount( numrecs = be16_to_cpu(dfp->bb_numrecs); level = be16_to_cpu(dfp->bb_level); - if (level > mp->m_rtrefc_maxlevels || + if (level >= mp->m_rtrefc_maxlevels || xfs_rtrefcount_droot_space_calc(level, numrecs) > dsize) { xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE); return -EFSCORRUPTED; -- cgit v1.2.3 From 813f8136a2ce1fee266d02a7df73db6e8a541604 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Wed, 15 Jul 2026 09:17:23 +0200 Subject: xfs: bounds-check buffer log item's dirty bitmap xlog_recover_do_reg_buffer() replays each dirty region described by a buffer log item's bitmap into the buffer read for that item: memcpy(xfs_buf_offset(bp, (uint)bit << XFS_BLF_SHIFT), item->ri_buf[i].iov_base, nbits << XFS_BLF_SHIFT); The destination offset (bit/nbits, from the logged dirty bitmap) and the buffer size (from the logged blf_len) are both attacker-controlled and otherwise unrelated, yet the only thing bounding the copy is an ASSERT(), which compiles away on production kernels. A crafted image logging a small blf_len together with a bitmap bit past the end of that buffer drives the memcpy() past the buffer's allocation, corrupting adjacent kernel heap during mount-time log recovery. This is reachable by anyone who can get a crafted image mounted -- the malicious-filesystem threat model XFS already guards against elsewhere. Turn the ASSERT() into a real XFS_IS_CORRUPT() check that aborts recovery of the buffer with -EFSCORRUPTED, consistent with the validate-and-fail idiom already used in xlog_recover_do_inode_buffer() and xfs_dquot_item_recover.c. xlog_recover_do_reg_buffer() therefore becomes STATIC int and its three callers propagate the error. Found and confirmed with KASAN on a CONFIG_XFS_DEBUG=n build: the crafted image trips a slab-out-of-bounds write before this change and fails recovery cleanly with -EFSCORRUPTED after it. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Reviewed-by: "Darrick J. Wong" Reviewed-by: Brian Foster Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf_item_recover.c | 57 +++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c index 02b95b89d1b5..240deb3f7827 100644 --- a/fs/xfs/xfs_buf_item_recover.c +++ b/fs/xfs/xfs_buf_item_recover.c @@ -461,7 +461,7 @@ xlog_recover_validate_buf_type( * given buffer. The bitmap in the buf log format structure indicates * where to place the logged data. */ -STATIC void +STATIC int xlog_recover_do_reg_buffer( struct xfs_mount *mp, struct xlog_recover_item *item, @@ -489,8 +489,24 @@ xlog_recover_do_reg_buffer( ASSERT(nbits > 0); ASSERT(item->ri_buf[i].iov_base != NULL); ASSERT(item->ri_buf[i].iov_len % XFS_BLF_CHUNK == 0); - ASSERT(BBTOB(bp->b_length) >= - ((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT)); + /* + * The bitmap is only trustworthy to the extent that it + * describes a region that actually fits inside the buffer we + * read in based on the (attacker-controlled) blf_len. Do not + * rely on an ASSERT() for this -- it compiles away entirely on + * non-DEBUG kernels, which is exactly where this matters, so + * validate it for real and abort recovery of this buffer rather + * than copying past the end of it. + */ + if (XFS_IS_CORRUPT(mp, BBTOB(bp->b_length) < + ((uint)bit << XFS_BLF_SHIFT) + + (nbits << XFS_BLF_SHIFT))) { + xfs_alert(mp, + "Bad buffer log item dirty bitmap (bit %d, nbits %d) for %d-byte buffer at daddr 0x%llx.", + bit, nbits, BBTOB(bp->b_length), + xfs_buf_daddr(bp)); + return -EFSCORRUPTED; + } /* * The dirty regions logged in the buffer, even though @@ -544,6 +560,7 @@ xlog_recover_do_reg_buffer( ASSERT(i == item->ri_total); xlog_recover_validate_buf_type(mp, bp, buf_f, current_lsn); + return 0; } /* @@ -552,10 +569,10 @@ xlog_recover_do_reg_buffer( * (ie. USR or GRP), then just toss this buffer away; don't recover it. * Else, treat it as a regular buffer and do recovery. * - * Return false if the buffer was tossed and true if we recovered the buffer to - * indicate to the caller if the buffer needs writing. + * Return 0 if the buffer was not recovered (tossed), 1 if it was recovered and + * needs writing, or a negative errno if recovery of the buffer failed. */ -STATIC bool +STATIC int xlog_recover_do_dquot_buffer( struct xfs_mount *mp, struct xlog *log, @@ -564,6 +581,7 @@ xlog_recover_do_dquot_buffer( struct xfs_buf_log_format *buf_f) { uint type; + int error; trace_xfs_log_recover_buf_dquot_buf(log, buf_f); @@ -571,7 +589,7 @@ xlog_recover_do_dquot_buffer( * Filesystems are required to send in quota flags at mount time. */ if (!mp->m_qflags) - return false; + return 0; type = 0; if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF) @@ -584,10 +602,12 @@ xlog_recover_do_dquot_buffer( * This type of quotas was turned off, so ignore this buffer */ if (log->l_quotaoffs_flag & type) - return false; + return 0; - xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN); - return true; + error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN); + if (error) + return error; + return 1; } /* @@ -724,7 +744,9 @@ xlog_recover_do_primary_sb_buffer( xfs_rgnumber_t orig_rgcount = mp->m_sb.sb_rgcount; int error; - xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn); + error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn); + if (error) + return error; if (orig_agcount == 0) { xfs_alert(mp, "Trying to grow file system without AGs"); @@ -1081,11 +1103,11 @@ xlog_recover_buf_commit_pass2( goto out_release; } else if (buf_f->blf_flags & (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) { - bool dirty; - - dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f); - if (!dirty) + error = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f); + if (error <= 0) goto out_release; + /* write dirty buffer */ + error = 0; } else if ((xfs_blft_from_flags(buf_f) & XFS_BLFT_SB_BUF) && xfs_buf_daddr(bp) == 0) { error = xlog_recover_do_primary_sb_buffer(mp, item, bp, buf_f, @@ -1105,7 +1127,10 @@ xlog_recover_buf_commit_pass2( xfs_buf_relse(rtsb_bp); } } else { - xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn); + error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, + current_lsn); + if (error) + goto out_release; } /* -- cgit v1.2.3 From d852729c5f4f830fbe7413df032e29459b3daf83 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Sun, 19 Jul 2026 23:11:24 +0800 Subject: xfs: handle NULL b_addr in xfs_buf_free When xfs_buf_alloc_backing_mem() fails, xfs_buf_free() is called with bp->b_addr still NULL. The code falls through to the folio_put path which calls virt_to_folio(NULL), dereferencing an invalid address and causing a kernel crash. Call Trace: xfs_buf_free+0x25f/0x510 xfs_buf_alloc+0xc98/0x19b0 xfs_buf_find_insert+0x55/0x14d0 xfs_buf_get_map+0x122b/0x17c0 xfbtree_init_leaf_block+0x11c/0x4a0 xfbtree_init+0x1bb/0x460 xrep_rmap_setup_scan+0x100/0x1f0 xrep_rmapbt+0x41/0xc0 Fix this by skipping folio_put() when bp->b_addr is NULL. Fixes: 5076a6040ca1 ("xfs: support in-memory buffer cache targets") Reported-by: syzbot+94c22d92f72f5a235b7d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=94c22d92f72f5a235b7d Signed-off-by: Yun Zhou Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index e1465e950acc..48d7dfd3e15f 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -114,7 +114,7 @@ xfs_buf_free( vfree(bp->b_addr); else if (bp->b_flags & _XBF_KMEM) kfree(bp->b_addr); - else + else if (bp->b_addr) folio_put(virt_to_folio(bp->b_addr)); call_rcu(&bp->b_rcu, xfs_buf_free_callback); -- cgit v1.2.3 From 7aa67044e7d8b6eebc44b25745f16c83ccc40a94 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 20 Jul 2026 16:08:47 +0200 Subject: xfs: update BDI {io,ra}_pages values based on the RT device limits When using XFS with a main device on an SSD that stores metadata and a RT device to store data on a HDD, we fail to take the I/O sizes for the RT device into accounting, leading to up to 5% slower read performance when using an SSD for metadata vs storing data and metadata on the HDD. Fix this up by taking the RT settings into account at mount an restoring the old settings at unmount time, unless the BDI settings have changed from those set by XFS. Reported-by: Filip Blagojevic Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: Damien Le Moal Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_mount.h | 7 +++++++ fs/xfs/xfs_super.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 66a02d1b9ad7..216a38a354e7 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -349,6 +349,13 @@ typedef struct xfs_mount { /* Index of uuid record in the uuid xarray. */ unsigned int m_uuid_table_index; + + /* + * Old io_pages/ra_pages valued in the main bdev BDI, and our initial + * calculated values. + */ + unsigned long m_old_io_pages, m_initial_io_pages; + unsigned long m_old_ra_pages, m_initial_ra_pages; } xfs_mount_t; #define M_IGEO(mp) (&(mp)->m_ino_geo) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index eac7f9503805..323aa1228b6c 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -545,6 +545,52 @@ xfs_open_devices( return error; } +/* + * When using a RT device some or all data I/O is using the RT device, but + * the BDI is inherited from the main data device. When the underlying block + * device for the RT device has larger I/O sizes, the BDI settings might be + * incorrect, which is especially bad if the main device is a SSD and the + * RT device is a HDD, as the io_opt fixup in blk_apply_bdi_limits is missing + * for this case. + * + * Update the BDI values to the max of the data and RT device to cover our + * bases. + */ +static void +xfs_update_bdi_rahead( + struct xfs_mount *mp) +{ + struct backing_dev_info *rt_bdi = + mp->m_rtdev_targp->bt_bdev->bd_disk->bdi; + struct backing_dev_info *sb_bdi = mp->m_super->s_bdi; + + mp->m_old_io_pages = sb_bdi->io_pages; + mp->m_old_ra_pages = sb_bdi->ra_pages; + + sb_bdi->io_pages = mp->m_initial_io_pages = + max(sb_bdi->io_pages, rt_bdi->io_pages); + sb_bdi->ra_pages = mp->m_initial_ra_pages = + max(sb_bdi->ra_pages, rt_bdi->ra_pages); +} + +static void +xfs_restore_bdi_rahead( + struct xfs_mount *mp) +{ + struct backing_dev_info *sb_bdi = mp->m_super->s_bdi; + + if (sb_bdi->io_pages == mp->m_initial_io_pages) + sb_bdi->io_pages = mp->m_old_io_pages; + else + xfs_info(mp, "io_pages changed from %lu to %lu, not restoring.", + mp->m_initial_io_pages, sb_bdi->io_pages); + if (sb_bdi->ra_pages == mp->m_initial_ra_pages) + sb_bdi->ra_pages = mp->m_old_ra_pages; + else + xfs_info(mp, "ra_pages changed from %lu to %lu, not restoring.", + mp->m_initial_ra_pages, sb_bdi->ra_pages); +} + /* * Setup xfs_mount buffer target pointers based on superblock */ @@ -582,6 +628,7 @@ xfs_setup_devices( mp->m_sb.sb_sectsize, mp->m_sb.sb_rblocks); if (error) return error; + xfs_update_bdi_rahead(mp); } return 0; @@ -2280,8 +2327,12 @@ static void xfs_kill_sb( struct super_block *sb) { + struct xfs_mount *mp = XFS_M(sb); + + if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) + xfs_restore_bdi_rahead(mp); kill_block_super(sb); - xfs_mount_free(XFS_M(sb)); + xfs_mount_free(mp); } static struct file_system_type xfs_fs_type = { -- cgit v1.2.3 From 270ffcd9b0a46254fbf3079d0e3341db7d7ec0e6 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:23:46 -0700 Subject: xfs: check cowextsize in xrep_inode_cowextsize LOLLM points out that the function that corrects cowextsize should check i_cowextsize, not i_extsize. Cc: stable@vger.kernel.org # v6.14 Fixes: a9600db96f74af ("xfs: detect and repair misaligned rtinherit directory cowextsize hints") 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 3ec41c198351..b88427a4460c 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -1960,7 +1960,7 @@ xrep_inode_cowextsize( /* Fix misaligned CoW extent size hints on a directory. */ if ((sc->ip->i_diflags & XFS_DIFLAG_RTINHERIT) && (sc->ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) && - sc->ip->i_extsize % sc->mp->m_sb.sb_rextsize > 0) { + xfs_extlen_to_rtxmod(sc->mp, sc->ip->i_cowextsize) > 0) { sc->ip->i_cowextsize = 0; sc->ip->i_diflags2 &= ~XFS_DIFLAG2_COWEXTSIZE; } -- cgit v1.2.3 From 61606f8846a9da8ab7d2d36ff4617134c9d6df1b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:24:02 -0700 Subject: xfs: fix transaction block reservation in xrep_rtbitmap LOLLM pointed out an inconsistency in the block reservation code in xrep_rtbitmap. The first is that the reservation computation is not consistent between the code that sets up the repair and the code that tries to avoid exceeding the transaction reservation once we know how big the rtbitmap really must be. As a result, the logic doesn't work. In fixing that, a second problem emerges: if we do readjust, we ask for the entire reservation all over again. We really only need the delta, so ask only for that. Fix all these problems by hoisting the computation to a trivial helper so that it gets used in both places. Cc: stable@vger.kernel.org # v6.14 Fixes: 8defee8dff2b20 ("xfs: online repair of realtime bitmaps for a realtime group") 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_repair.c | 46 +++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/fs/xfs/scrub/rtbitmap_repair.c b/fs/xfs/scrub/rtbitmap_repair.c index dc64902d6c25..442a17bf9720 100644 --- a/fs/xfs/scrub/rtbitmap_repair.c +++ b/fs/xfs/scrub/rtbitmap_repair.c @@ -36,6 +36,24 @@ /* rt bitmap content repairs */ +/* + * Reserve enough blocks to write out a completely new bitmap file, plus twice + * as many blocks as we would need if we can only allocate one block per data + * fork mapping. This should cover the preallocation of the temporary file and + * exchanging the extent mappings. + * + * We cannot use xfs_exchmaps_estimate because we have not yet constructed the + * replacement bitmap and therefore do not know how many extents it will use. + * By the time we do, we will have a dirty transaction (which we cannot drop + * because we cannot drop the rtbitmap ILOCK) and cannot ask for more + * reservation. + */ +static inline unsigned long long +xrep_rtbitmap_calc_blocks(struct xfs_mount *mp, unsigned long long blocks) +{ + return blocks + (xfs_bmbt_calc_size(mp, blocks) * 2); +} + /* Set up to repair the realtime bitmap for this group. */ int xrep_setup_rtbitmap( @@ -56,20 +74,7 @@ xrep_setup_rtbitmap( if (error) return error; - /* - * Reserve enough blocks to write out a completely new bitmap file, - * plus twice as many blocks as we would need if we can only allocate - * one block per data fork mapping. This should cover the - * preallocation of the temporary file and exchanging the extent - * mappings. - * - * We cannot use xfs_exchmaps_estimate because we have not yet - * constructed the replacement bitmap and therefore do not know how - * many extents it will use. By the time we do, we will have a dirty - * transaction (which we cannot drop because we cannot drop the - * rtbitmap ILOCK) and cannot ask for more reservation. - */ - blocks += xfs_bmbt_calc_size(mp, blocks) * 2; + blocks = xrep_rtbitmap_calc_blocks(mp, mp->m_sb.sb_rbmblocks); if (blocks > UINT_MAX) return -EOPNOTSUPP; @@ -512,7 +517,7 @@ xrep_rtbitmap( struct xchk_rtbitmap *rtb = sc->buf; struct xfs_mount *mp = sc->mp; struct xfs_group *xg = rtg_group(sc->sr.rtg); - unsigned long long blocks = 0; + unsigned long long blocks; unsigned int busy_gen; int error; @@ -532,15 +537,20 @@ xrep_rtbitmap( * figure out if we need to adjust the block reservation in the * transaction. */ - blocks = xfs_bmbt_calc_size(mp, rtb->rbmblocks); + blocks = xrep_rtbitmap_calc_blocks(mp, rtb->rbmblocks); if (blocks > UINT_MAX) return -EOPNOTSUPP; if (blocks > rtb->resblks) { - error = xfs_trans_reserve_more(sc->tp, blocks, 0); + uint64_t delta = blocks - rtb->resblks; + + if (delta > UINT_MAX) + return -EOPNOTSUPP; + + error = xfs_trans_reserve_more(sc->tp, delta, 0); if (error) return error; - rtb->resblks += blocks; + rtb->resblks += delta; } /* Fix inode core and forks. */ -- cgit v1.2.3 From 8ed78104722b57df69478b0b1608a408c1036085 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:24:17 -0700 Subject: xfs: rtsummary scrub should treat rtbitmap corruption errors as an xref error LOLLM notes the discrepancy between the comment saying that this is an xref error and the code, which marks a (non-xref) corruption error. This appears to be a regression. Cc: stable@vger.kernel.org # v6.13 Fixes: e3088ae2dcae3c ("xfs: move RT bitmap and summary information to the rtgroup") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rtsummary.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtsummary.c b/fs/xfs/scrub/rtsummary.c index 78f72a046887..546b335ade13 100644 --- a/fs/xfs/scrub/rtsummary.c +++ b/fs/xfs/scrub/rtsummary.c @@ -358,7 +358,7 @@ xchk_rtsummary( * EFSCORRUPTED means the rtbitmap is corrupt, which is an xref * error since we're checking the summary file. */ - xchk_ip_set_corrupt(sc, rbmip); + xchk_ip_xref_set_corrupt(sc, rbmip); return 0; } if (error) -- cgit v1.2.3 From 49933254ba8d421ed706cbe6ed0fbc264e572cab Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:24:33 -0700 Subject: xfs: zero i_nlink before repair puts inode on unlinked list LOLLM observes that we don't reset i_nlink if we encounter a file with no parent that isn't on the unlinked list. This causes unnecessary assertion trips on debugging kernels and an inconsistent file, so let's fix that. Cc: stable@vger.kernel.org # v6.10 Fixes: 669dfe883c8e20 ("xfs: update the unlinked list when repairing link counts") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/nlinks_repair.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/xfs/scrub/nlinks_repair.c b/fs/xfs/scrub/nlinks_repair.c index fbc2ff809fc0..09e097e16689 100644 --- a/fs/xfs/scrub/nlinks_repair.c +++ b/fs/xfs/scrub/nlinks_repair.c @@ -232,9 +232,14 @@ xrep_nlinks_repair_inode( * unlinked list, put it on the unlinked list. */ if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) { + if (actual_nlink) + clear_nlink(VFS_I(ip)); error = xfs_iunlink(sc->tp, ip); - if (error) + if (error) { + if (actual_nlink) + set_nlink(VFS_I(ip), actual_nlink); goto out_trans; + } dirty = true; } -- cgit v1.2.3 From b28d23c51635b646784a2a62c71ba99458c07d5e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:24:48 -0700 Subject: xfs: mark nonzero sb_gquotino as corrupt on metadir filesystems On a metadir filesystem, the superblock gquotino field is always zero because we find the quota inodes through the metadata directory tree. A nonzero value is therefore a corruption (as pointed out by LOLLM) so mark the failure as such. Cc: stable@vger.kernel.org # v6.13 Fixes: 06b20ef09ba163 ("xfs: check pre-metadir fields correctly") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 9ed053b5f061..62ed5eaf08fb 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -266,7 +266,7 @@ xchk_superblock( xchk_block_set_corrupt(sc, bp); if (sb->sb_gquotino != cpu_to_be64(0)) - xchk_block_set_preen(sc, bp); + xchk_block_set_corrupt(sc, bp); } else { if (sb->sb_uquotino != cpu_to_be64(mp->m_sb.sb_uquotino)) xchk_block_set_preen(sc, bp); -- cgit v1.2.3 From 11055a46f398779b69aa36afb7c9f4124529a075 Mon Sep 17 00:00:00 2001 From: Christian Loehle Date: Wed, 22 Jul 2026 10:38:24 +0100 Subject: ACPI: CPPC: Check all controls for fast switching ACPI 6.2, Section 6.2.11.2 permits _CPC registers to use flexible address spaces. Linux advertises that capability through _OSC and parses the address space of each _CPC register independently. A directly accessible DESIRED_PERF combined with PCC-backed limits is therefore a valid configuration. cppc_allow_fast_switch() only checks DESIRED_PERF, although the fast-switch callback passes DESIRED_PERF, MIN_PERF and MAX_PERF to cppc_set_perf(). If a limit uses PCC, that function can sleep while called from scheduler context. Allow fast switching only when every supported control used by the callback has an address space already accepted for fast access. Check the complete policy domain, including initialized CPUs that are currently offline and may later become the policy's managing CPU. Fixes: 658fa7b1c47a ("ACPI: CPPC: Add cppc_get_perf() API to read performance controls") Cc: stable@vger.kernel.org Signed-off-by: Christian Loehle Link: https://patch.msgid.link/20260722093825.1030594-2-christian.loehle@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 22 +++++++++++++++++----- drivers/cpufreq/cppc_cpufreq.c | 2 +- include/acpi/cppc_acpi.h | 5 +++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 9f572f481241..1d3a94100491 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -475,17 +475,29 @@ bool acpi_cpc_valid(void) } EXPORT_SYMBOL_GPL(acpi_cpc_valid); -bool cppc_allow_fast_switch(void) +bool cppc_allow_fast_switch(const struct cpumask *cpus) { - struct cpc_register_resource *desired_reg; + struct cpc_register_resource *desired_reg, *min_reg, *max_reg; struct cpc_desc *cpc_ptr; int cpu; - for_each_online_cpu(cpu) { + for_each_cpu(cpu, cpus) { cpc_ptr = per_cpu(cpc_desc_ptr, cpu); + if (!cpc_ptr) + return false; desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF]; - if (!CPC_IN_SYSTEM_MEMORY(desired_reg) && - !CPC_IN_SYSTEM_IO(desired_reg)) + min_reg = &cpc_ptr->cpc_regs[MIN_PERF]; + max_reg = &cpc_ptr->cpc_regs[MAX_PERF]; + + if (!CPC_SUPPORTED(desired_reg) || + (!CPC_IN_SYSTEM_MEMORY(desired_reg) && + !CPC_IN_SYSTEM_IO(desired_reg)) || + (CPC_SUPPORTED(min_reg) && + !CPC_IN_SYSTEM_MEMORY(min_reg) && + !CPC_IN_SYSTEM_IO(min_reg)) || + (CPC_SUPPORTED(max_reg) && + !CPC_IN_SYSTEM_MEMORY(max_reg) && + !CPC_IN_SYSTEM_IO(max_reg))) return false; } diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index f6cea0c54dd9..b943bf78d3f5 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -693,7 +693,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) goto out; } - policy->fast_switch_possible = cppc_allow_fast_switch(); + policy->fast_switch_possible = cppc_allow_fast_switch(policy->cpus); policy->dvfs_possible_from_any_cpu = true; /* diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 8693890a7275..8c191b9ac18f 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -170,7 +170,7 @@ extern u64 cppc_get_dmi_max_khz(void); extern unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf); extern unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq); extern bool acpi_cpc_valid(void); -extern bool cppc_allow_fast_switch(void); +bool cppc_allow_fast_switch(const struct cpumask *cpus); extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data); extern int cppc_get_transition_latency(int cpu); extern bool cpc_ffh_supported(void); @@ -234,7 +234,8 @@ static inline bool acpi_cpc_valid(void) { return false; } -static inline bool cppc_allow_fast_switch(void) + +static inline bool cppc_allow_fast_switch(const struct cpumask *cpus) { return false; } -- cgit v1.2.3 From 9753c0ab89b7516aba4884dc3cc725ca33c2e3da Mon Sep 17 00:00:00 2001 From: Christian Loehle Date: Wed, 22 Jul 2026 10:38:25 +0100 Subject: cpufreq: cppc: Sanitize lockless policy limit snapshots cppc_cpufreq_update_perf_limits() reads policy->min and policy->max without holding the policy lock. The cpufreq core updates those fields with separate stores, so a reader can observe the old minimum together with the new maximum and construct MIN_PERF greater than MAX_PERF. Read both fields once and, if the lockless snapshot is inconsistent, reduce the minimum to the observed maximum. This matches the conservative correction used by cpufreq_driver_resolve_freq() and ensures that CPPC never receives an inverted limit pair. Fixes: ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks") Cc: stable@vger.kernel.org Signed-off-by: Christian Loehle Link: https://patch.msgid.link/20260722093825.1030594-3-christian.loehle@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cppc_cpufreq.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index b943bf78d3f5..6fe0e972952a 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_exit(void) } #endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */ -static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data, - struct cpufreq_policy *policy) +static void cppc_cpufreq_get_perf_limits(struct cppc_cpudata *cpu_data, + struct cpufreq_policy *policy, + u32 *min_perf, u32 *max_perf) { struct cppc_perf_caps *caps = &cpu_data->perf_caps; - u32 min_perf, max_perf; + unsigned int min_freq, max_freq; + u32 min, max; + + min_freq = READ_ONCE(policy->min); + max_freq = READ_ONCE(policy->max); + if (unlikely(min_freq > max_freq)) + min_freq = max_freq; + + min = cppc_khz_to_perf(caps, min_freq); + max = cppc_khz_to_perf(caps, max_freq); - min_perf = cppc_khz_to_perf(caps, policy->min); - max_perf = cppc_khz_to_perf(caps, policy->max); + *min_perf = clamp_t(u32, min, caps->lowest_perf, caps->highest_perf); + *max_perf = clamp_t(u32, max, caps->lowest_perf, caps->highest_perf); +} - cpu_data->perf_ctrls.min_perf = - clamp_t(u32, min_perf, caps->lowest_perf, caps->highest_perf); - cpu_data->perf_ctrls.max_perf = - clamp_t(u32, max_perf, caps->lowest_perf, caps->highest_perf); +static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data, + struct cpufreq_policy *policy) +{ + cppc_cpufreq_get_perf_limits(cpu_data, policy, + &cpu_data->perf_ctrls.min_perf, + &cpu_data->perf_ctrls.max_perf); } static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, -- cgit v1.2.3 From f0a3f042293a8c5a2152346b3637ea60866c503a Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Thu, 16 Jul 2026 19:51:58 +0800 Subject: cpufreq: schedutil: Publish util hooks only after all sg_cpu are initialized Commit 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop") merged the per-CPU initialization and the utilization-hook registration into a single loop in sugov_start(). For a shared cpufreq policy this re-introduces the race originally fixed by commit ab2f7cf141aa ("cpufreq: schedutil: Fix sugov_start() versus sugov_update_shared() race"). The scheduler's util path reaches the hook under RCU-sched and never takes policy->rwsem, so the rwsem held across sugov_start() cannot serialize the two. Once the first CPU's hook is published, sugov_update_shared() may run and, via sugov_next_freq_shared(), read/write each sibling sugov_cpu (iowait_boost, util, bw_min, ...) concurrently with the memset() still initializing them, with no lock common to both sides: the update side holds sg_policy->update_lock while the init side holds only policy->rwsem, which the scheduler's util path never takes. The walk only accesses scalar members, never a pointer like ->sg_policy, so it does not crash today; it merely uses stale (or zero on first start) values that skew the frequency selection and tracepoints. It is still a genuine data race, and a latent crash once any pointer member is dereferenced there. Restore the two-phase approach: initialize all per-CPU structures first, and only then publish the per-CPU utilization update hooks. Fixes: 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop") Cc: stable@vger.kernel.org Signed-off-by: Zhongqiu Han Reviewed-by: Christian Loehle Link: https://patch.msgid.link/20260716115159.848403-1-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki --- kernel/sched/cpufreq_schedutil.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index a4e689eefdfb..dff4ee04694c 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -870,8 +870,19 @@ static int sugov_start(struct cpufreq_policy *policy) memset(sg_cpu, 0, sizeof(*sg_cpu)); sg_cpu->cpu = cpu; sg_cpu->sg_policy = sg_policy; + } + + /* + * Publish the hooks only after all per-CPU data is initialized, so a + * shared policy's sugov_update_shared() never reads an uninitialized + * sibling sugov_cpu. + */ + for_each_cpu(cpu, policy->cpus) { + struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu); + cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu); } + return 0; } -- cgit v1.2.3 From f7df2da0d1c375c1c4c70e1e0b569592de0d6b73 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Sat, 18 Jul 2026 10:44:36 -0300 Subject: drm/v3d: Reach the GMP through the hub registers on V3D 7.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v3d_idle_axi() drains the GPU's memory interface for a safe powerdown by using the V3D_GMP_CFG register. It reached both registers with the macros V3D_CORE_READ and V3D_CORE_WRITE. On V3D 7.x the GMP is no longer a per-core block; it lives in the hub register region. Reaching it through the per-core register block addresses the wrong region. Select the hub accessors (V3D_{READ,WRITE}) for the GMP on V3D 7.x and keep the per-core path for earlier generations. Cc: stable@vger.kernel.org Fixes: 0ad5bc1ce463 ("drm/v3d: fix up register addresses for V3D 7.x") Link: https://patch.msgid.link/20260718-v3d-pm-axi-transactions-v1-1-4ecd7729ed70@igalia.com Reviewed-by: Iago Toral Quiroga Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_gem.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index c43d9af41374..bb0eec870794 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -39,6 +39,18 @@ v3d_init_core(struct v3d_dev *v3d, int core) static void v3d_idle_axi(struct v3d_dev *v3d, int core) { + if (v3d->ver >= V3D_GEN_71) { + V3D_WRITE(V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); + + if (wait_for((V3D_READ(V3D_GMP_STATUS(v3d->ver)) & + (V3D_GMP_STATUS_RD_COUNT_MASK | + V3D_GMP_STATUS_WR_COUNT_MASK | + V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) { + drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n"); + } + return; + } + V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) & -- cgit v1.2.3 From ab05caca123c6d0b41850b7c05b246e4dca4a770 Mon Sep 17 00:00:00 2001 From: Yitang Yang Date: Wed, 22 Jul 2026 20:45:51 +0800 Subject: io_uring/rw: fix missing ERESTARTSYS conversion in read paths Both read and write may receive internal restart error codes from the filesystem layer and should be converted to -EINTR. However, when multishot read support was added, the error code normalization was lost for both io_read() and io_read_mshot(). Extract the conversion into io_fixup_restart_res() and apply it in all three locations: io_rw_done(), io_read(), and io_read_mshot(). Fixes: a08d195b586a ("io_uring/rw: split io_read() into a helper") Cc: stable@vger.kernel.org Signed-off-by: Yitang Yang Link: https://patch.msgid.link/20260722124551.130563-1-yi1tang.yang@gmail.com Signed-off-by: Jens Axboe --- io_uring/rw.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/io_uring/rw.c b/io_uring/rw.c index 63b6519e498c..95038cfda615 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -615,6 +615,24 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res) smp_store_release(&req->iopoll_completed, 1); } +static inline ssize_t io_fixup_restart_res(ssize_t ret) +{ + switch (ret) { + case -ERESTARTSYS: + case -ERESTARTNOINTR: + case -ERESTARTNOHAND: + case -ERESTART_RESTARTBLOCK: + /* + * We can't just restart the syscall, since previously + * submitted sqes may already be in progress. Just fail + * this IO with EINTR. + */ + return -EINTR; + default: + return ret; + } +} + static inline void io_rw_done(struct io_kiocb *req, ssize_t ret) { struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); @@ -624,21 +642,8 @@ static inline void io_rw_done(struct io_kiocb *req, ssize_t ret) return; /* transform internal restart error codes */ - if (unlikely(ret < 0)) { - switch (ret) { - case -ERESTARTSYS: - case -ERESTARTNOINTR: - case -ERESTARTNOHAND: - case -ERESTART_RESTARTBLOCK: - /* - * We can't just restart the syscall, since previously - * submitted sqes may already be in progress. Just fail - * this IO with EINTR. - */ - ret = -EINTR; - break; - } - } + if (unlikely(ret < 0)) + ret = io_fixup_restart_res(ret); if (req->flags & REQ_F_IOPOLL) io_complete_rw_iopoll(&rw->kiocb, ret); @@ -1034,7 +1039,8 @@ int io_read(struct io_kiocb *req, unsigned int issue_flags) if (req->flags & REQ_F_BUFFERS_COMMIT) io_kbuf_recycle(req, sel.buf_list, issue_flags); - return ret; + + return io_fixup_restart_res(ret); } int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags) @@ -1068,8 +1074,10 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags) return IOU_RETRY; } else if (ret <= 0) { io_kbuf_recycle(req, sel.buf_list, issue_flags); - if (ret < 0) + if (ret < 0) { + ret = io_fixup_restart_res(ret); req_set_fail(req); + } } else if (!(req->flags & REQ_F_APOLL_MULTISHOT)) { cflags = io_put_kbuf(req, ret, sel.buf_list); } else { -- cgit v1.2.3 From 07e769ba3d82ac614725c81c512a8b54d33f2c9e Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Sat, 18 Jul 2026 10:44:37 -0300 Subject: drm/v3d: Idle AXI transactions before disabling the clock on suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, v3d_power_suspend() removes the GPU clock without first quiescing the GPU's memory interface (AXI). If the clock is cut while the core still has outstanding AXI transactions in flight, the hardware is frozen mid-transaction. That corrupted state survives the power cycle, and the first job submitted after the next resume will cause a GPU hang accompanied by an L2T "pte invalid" MMU fault. The hardware already provides a safe-powerdown sequence for this: request the GMP to stop and wait for outstanding reads/writes to drain (v3d_idle_axi()), plus the GCA safe shutdown on pre-4.1 HW (v3d_idle_gca()). The driver implements both, but the runtime PM support added later never invoked them when powering the GPU down. Perform the safe-powerdown sequence in v3d_power_suspend() before disabling the clock, while the core is still powered. Link: https://github.com/raspberrypi/linux/issues/7443 Link: https://github.com/raspberrypi/linux/issues/7488 Fixes: 458f2a712ab4 ("drm/v3d: Introduce Runtime Power Management") Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260718-v3d-pm-axi-transactions-v1-2-4ecd7729ed70@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_drv.h | 2 ++ drivers/gpu/drm/v3d/v3d_gem.c | 4 ++-- drivers/gpu/drm/v3d/v3d_power.c | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 4ebe175a8c6b..423bcfe331a0 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -571,6 +571,8 @@ extern bool super_pages; void v3d_init_hw_state(struct v3d_dev *v3d); int v3d_gem_init(struct drm_device *dev); void v3d_gem_destroy(struct drm_device *dev); +void v3d_idle_axi(struct v3d_dev *v3d, int core); +void v3d_idle_gca(struct v3d_dev *v3d); void v3d_reset_sms(struct v3d_dev *v3d); void v3d_reset(struct v3d_dev *v3d); void v3d_invalidate_caches(struct v3d_dev *v3d); diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index bb0eec870794..23a0db9575a5 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -36,7 +36,7 @@ v3d_init_core(struct v3d_dev *v3d, int core) V3D_CORE_WRITE(core, V3D_CTL_L2TFLEND, ~0); } -static void +void v3d_idle_axi(struct v3d_dev *v3d, int core) { if (v3d->ver >= V3D_GEN_71) { @@ -61,7 +61,7 @@ v3d_idle_axi(struct v3d_dev *v3d, int core) } } -static void +void v3d_idle_gca(struct v3d_dev *v3d) { if (v3d->ver >= V3D_GEN_41) diff --git a/drivers/gpu/drm/v3d/v3d_power.c b/drivers/gpu/drm/v3d/v3d_power.c index f7df6393d38f..c53146316079 100644 --- a/drivers/gpu/drm/v3d/v3d_power.c +++ b/drivers/gpu/drm/v3d/v3d_power.c @@ -54,8 +54,15 @@ int v3d_power_suspend(struct device *dev) v3d_clean_caches(v3d); + /* Wait until V3D has no active or pending AXI transactions. */ + v3d_idle_axi(v3d, 0); + v3d_idle_gca(v3d); + ret = v3d_suspend_sms(v3d); if (ret) { + /* Staying active: undo the GMP STOP_REQ from v3d_idle_axi(). */ + V3D_WRITE(V3D_GMP_CFG(v3d->ver), + V3D_READ(V3D_GMP_CFG(v3d->ver)) & ~V3D_GMP_CFG_STOP_REQ); v3d_irq_enable(v3d); return ret; } -- cgit v1.2.3 From 425224c2d700391729be7fe6929a88ef4e2d7a4e Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 6 Jul 2026 20:22:42 +0200 Subject: proc: Fix broken error paths for namespace links Don't return the return value of down_read_killable() (0) when a ptrace access check fails, return -EACCES as intended. Reported-by: Magnus Lindholm Closes: https://lore.kernel.org/r/20260706170735.2941493-1-linmag7@gmail.com Fixes: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Link: https://patch.msgid.link/20260706-procfs-ns-eacces-fix-v1-1-a69ab14c02e6@google.com Tested-by: Magnus Lindholm Signed-off-by: Christian Brauner (Amutable) --- fs/proc/namespaces.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 2f46f1396744..ea6ec61a0430 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -46,7 +46,7 @@ static const char *proc_ns_get_link(struct dentry *dentry, const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; struct task_struct *task; struct path ns_path; - int error = -EACCES; + int error; if (!dentry) return ERR_PTR(-ECHILD); @@ -59,6 +59,7 @@ static const char *proc_ns_get_link(struct dentry *dentry, if (error) goto out_put_task; + error = -EACCES; if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) goto out; @@ -90,6 +91,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl if (res) goto out_put_task; + res = -EACCES; if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { res = ns_get_name(name, sizeof(name), task, ns_ops); if (res >= 0) -- cgit v1.2.3 From 8eb052f48331474c2789d07b7f11165c323bd2f9 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sun, 24 May 2026 23:38:46 -0400 Subject: ARM: npcm: Fix OF node refcount leaks in SMP setup npcm7xx_smp_boot_secondary() and npcm7xx_smp_prepare_cpus() look up the GCR and SCU nodes with of_find_compatible_node(). The returned nodes are used for of_iomap(), but the node references are never released. of_iomap() does not consume the device node reference, and iounmap() only releases the MMIO mapping. Drop each node reference after the corresponding mapping attempt. Fixes: 7bffa14c9aed ("arm: npcm: add basic support for Nuvoton BMCs") Signed-off-by: Yuho Choi Reviewed-by: Avi Fishman Signed-off-by: Andrew Jeffery --- arch/arm/mach-npcm/platsmp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-npcm/platsmp.c b/arch/arm/mach-npcm/platsmp.c index 41891d3aa124..4c1fc9983746 100644 --- a/arch/arm/mach-npcm/platsmp.c +++ b/arch/arm/mach-npcm/platsmp.c @@ -32,6 +32,7 @@ static int npcm7xx_smp_boot_secondary(unsigned int cpu, goto out; } gcr_base = of_iomap(gcr_np, 0); + of_node_put(gcr_np); if (!gcr_base) { pr_err("could not iomap gcr"); ret = -ENOMEM; @@ -60,6 +61,7 @@ static void __init npcm7xx_smp_prepare_cpus(unsigned int max_cpus) return; } scu_base = of_iomap(scu_np, 0); + of_node_put(scu_np); if (!scu_base) { pr_err("could not iomap scu"); return; -- cgit v1.2.3 From fa989f2cc351aec3643350489553c9bdfb3adf9f Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Mon, 13 Jul 2026 10:22:13 +0200 Subject: drm/panel: ilitek-ili9881c: do not fail probe if iovcc is absent Commit 4c95b2b7d49e ("drm/panel: ilitek-ili9881c: support Waveshare 7.0" DSI panel") adds an additional iovcc regulator that other Ilitek ili9881c based panels apparently do not have or need. The commit goes out of its way to make usage of this new regulator optional, dutifully testing if the field in `struct ili9881c` is NULL before touching the new regulator. However, in the probe function, it unconditionally fails if devm_regulator_get_optional returns an error. devm_regulator_get_optional() returns -ENODEV if the regulator is missing, causing probe to fail for other panels that do not have an iovcc-supply set in the device tree. Fixes: 4c95b2b7d49e ("drm/panel: ilitek-ili9881c: support Waveshare 7.0" DSI panel") Signed-off-by: David Oberhollenzer Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260713082213.75759-1-david.oberhollenzer@sigma-star.at --- drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 0652cdb57d11..6c8d02712566 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -2562,9 +2562,12 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) "Couldn't get our power regulator\n"); ctx->iovcc = devm_regulator_get_optional(&dsi->dev, "iovcc"); - if (IS_ERR(ctx->iovcc)) - return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc), + if (IS_ERR(ctx->iovcc)) { + if (PTR_ERR(ctx->iovcc) != -ENODEV) + return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc), "Couldn't get our iovcc regulator\n"); + ctx->iovcc = NULL; + } ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset)) -- cgit v1.2.3 From fc03f930bd9fd7c09617e41b1743e50ba659707c Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 12 Jul 2026 01:26:32 +0100 Subject: drm/panel: ilitek-ili9882t: fix unmet dependency for DRM_PANEL_ILITEK_ILI9882T Currently, DRM_PANEL_ILITEK_ILI9882T selects DRM_DISPLAY_DSC_HELPER without ensuring DRM_DISPLAY_HELPER is also enabled, causing an unmet dependency and build failure. Other similar options select DRM_DISPLAY_HELPER, let's do the same here. This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig. Fixes: 68e28facbc8a ("drm/panel: ilitek-ili9882t: Select DRM_DISPLAY_DSC_HELPER") Signed-off-by: Julian Braha Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260712002632.2323484-1-julianbraha@gmail.com --- drivers/gpu/drm/panel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 7450b27622a2..613f09ccc7cb 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -332,6 +332,7 @@ config DRM_PANEL_ILITEK_ILI9882T depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER help Say Y if you want to enable support for panels based on the -- cgit v1.2.3 From 3667bc164849fee4f1b18b182bdfe643f758ca17 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 12 Jul 2026 01:15:14 +0100 Subject: drm/panel: s6e3ha8: fix unmet dependency on DRM_DISPLAY_HELPER Currently, DRM_PANEL_SAMSUNG_S6E3HA8 selects DRM_DISPLAY_DSC_HELPER without ensuring its dependency, DRM_DISPLAY_HELPER, is enabled, causing an unmet dependency. Let's select DRM_DISPLAY_HELPER as other similar options do. This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig. Fixes: fd3b2c5f40a1 ("drm/panel: s6e3ha8: select CONFIG_DRM_DISPLAY_DSC_HELPER") Signed-off-by: Julian Braha Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260712001514.2318597-1-julianbraha@gmail.com --- drivers/gpu/drm/panel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 613f09ccc7cb..fda7a3ee67e6 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -955,6 +955,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA8 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER help Say Y or M here if you want to enable support for the -- cgit v1.2.3 From aaa5be0258db1709e254b4150af5c2fa5518c548 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 12 Jul 2026 00:42:30 +0100 Subject: drm/panel: fix unmet dependency bug for DRM_PANEL_HIMAX_HX83121A Currently, DRM_PANEL_HIMAX_HX83121A selects DRM_DISPLAY_DSC_HELPER without also ensuring DRM_DISPLAY_HELPER is enabled, causing an unmet dependency: WARNING: unmet direct dependencies detected for DRM_DISPLAY_DSC_HELPER Depends on [n]: HAS_IOMEM [=y] && DRM [=m] && DRM_DISPLAY_HELPER [=n] Selected by [m]: - DRM_PANEL_HIMAX_HX83121A [=m] && HAS_IOMEM [=y] && DRM [=m] && DRM_PANEL [=y] && OF [=y] && DRM_MIPI_DSI [=y] && BACKLIGHT_CLASS_DEVICE [=m] - DRM_PANEL_ILITEK_ILI9882T [=m] && HAS_IOMEM [=y] && DRM [=m] && DRM_PANEL [=y] && OF [=y] && DRM_MIPI_DSI [=y] && BACKLIGHT_CLASS_DEVICE [=m] Many other DRM_PANEL_* options select DRM_DISPLAY_HELPER when selecting DRM_DISPLAY_DSC_HELPER, let's do the same here. This unmet dependency bug was found by kconfirm, a static analysis tool for Kconfig. Fixes: defab7b01e08 ("drm/panel: hx83121a: select DRM_DISPLAY_DSC_HELPER") Signed-off-by: Julian Braha Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260711234230.2236041-1-julianbraha@gmail.com --- drivers/gpu/drm/panel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index fda7a3ee67e6..3eac4d0f88bc 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -232,6 +232,7 @@ config DRM_PANEL_HIMAX_HX83121A depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DSC_HELPER select DRM_KMS_HELPER help -- cgit v1.2.3 From 7dc3680b7ffe01add3e9299fde8471d2dd53a8ae Mon Sep 17 00:00:00 2001 From: Gregor Herburger Date: Tue, 21 Jul 2026 17:38:19 +0200 Subject: drm/vc4: hvs/v3d: Fix null dereference in unbind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hvs and v3d drivers use dev_get_drvdata(master) in their unbind functions. Since the vc4-drm gets removed before its dependent drivers (vc4_hvs/vc4_v3d) the vc4_hvs_unbind/vc4_v3d_unbind functions try to get drvdata of its master and fails with a null dereference error. Use the data pointer passed to the unbind functions directly instead of dev_get_drvdata(master). This avoids using potentially freed memory. Fixes: d3f5168a0810 ("drm/vc4: Bind and initialize the V3D engine.") Fixes: c8b75bca92cb ("drm/vc4: Add KMS support for Raspberry Pi.") Signed-off-by: Gregor Herburger Link: https://patch.msgid.link/20260721-rpi-vc4-fix-v2-1-b813dcd01dc7@linutronix.de Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_hvs.c | 2 +- drivers/gpu/drm/vc4/vc4_v3d.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 184d51ea3fa5..e715147d091c 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -1752,7 +1752,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) static void vc4_hvs_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); + struct drm_device *drm = data; struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_hvs *hvs = vc4->hvs; struct drm_mm_node *node, *next; diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index d31b906cb8e7..f32410420d3e 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -494,7 +494,7 @@ err_put_runtime_pm: static void vc4_v3d_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); + struct drm_device *drm = data; struct vc4_dev *vc4 = to_vc4_dev(drm); vc4_irq_uninstall(drm); -- cgit v1.2.3 From a1e0eb8f55cfe09bb31a202a388babc411292656 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 12 Jul 2026 14:24:21 +0200 Subject: ovl: check access to copy_file_range source with src mounter creds Commit 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices") allowed filesystems that implement the copy_file_range() f_op to decide if they want to access cross-sb copy from/to the same fs type. The same commit added checks to verify same sb copy for filesystems that implement ->copy_file_range() and do not support cross-sb copy at the time, namely, to ceph, fuse and nfs. The two remaining fs which implement ->copy_file_range(), cifs and overlayfs started to support cross-sb copy from this time. While overlayfs does support cross-sb copy when the two underlying files are on the same base fs, the copy operation on the two real files from two different overalyfs filesystems is performed with the mounter creds of the destination overlayfs and the read permission access hook for the source file was called with the wrong creds. This could cause either deny of access to copy which would otherwise be allowed (e.g. with splice) or allow read access to file which would otherwise be denied. Fix the latter case by explicitly verifying read access to source file with the source overlayfs mounter creds. The former case remains a quirk of cross-sb overlayfs copy, but userspace could fall back to regular copy so no harm done. Fixes: 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices") Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260712122421.203113-1-amir73il@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/overlayfs/file.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 27cc07738f33..f3d97eb146e8 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -528,6 +528,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, loff_t len, unsigned int flags, enum ovl_copyop op) { + struct inode *inode_in = file_inode(file_in); struct inode *inode_out = file_inode(file_out); struct file *realfile_in, *realfile_out; loff_t ret; @@ -551,7 +552,20 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, if (IS_ERR(realfile_in)) goto out_unlock; - with_ovl_creds(file_inode(file_out)->i_sb) { + /* + * For cross-sb copy, vfs_copy_file_range() will verify read access with + * the mounter creds of the dest fs mounter, so we need to explicitly + * verify read access with the source mounter creds. + */ + if (unlikely(inode_in->i_sb != inode_out->i_sb)) { + with_ovl_creds(inode_in->i_sb) { + ret = rw_verify_area(READ, realfile_in, &pos_in, len); + if (unlikely(ret)) + goto out_unlock; + } + } + + with_ovl_creds(inode_out->i_sb) { switch (op) { case OVL_COPY: ret = vfs_copy_file_range(realfile_in, pos_in, -- cgit v1.2.3 From 1d0cff74d8c8d798a64c1e7fe442659aecb64130 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Thu, 16 Jul 2026 13:28:20 +0800 Subject: pidfs: handle FS_IOC32_GETVERSION in compat ioctl FS_IOC32_GETVERSION has a distinct compat command encoding. Passing it through compat_ptr_ioctl() leaves pidfd_ioctl() unable to recognize the otherwise architecture-independent inode generation query. Translate the compat command to FS_IOC_GETVERSION before dispatching it through the native pidfd ioctl implementation. Signed-off-by: Li Chen Link: https://patch.msgid.link/20260716052822.1034228-1-me@linux.beauty Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/pidfs.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index 695215aa2a58..d7fe9abdd6f1 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include #include @@ -659,6 +660,17 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return open_namespace(ns_common); } +#ifdef CONFIG_COMPAT +static long pidfd_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + if (cmd == FS_IOC32_GETVERSION) + cmd = FS_IOC_GETVERSION; + + return pidfd_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + static int pidfs_file_release(struct inode *inode, struct file *file) { struct pid *pid = inode->i_private; @@ -686,7 +698,9 @@ static const struct file_operations pidfs_file_operations = { .show_fdinfo = pidfd_show_fdinfo, #endif .unlocked_ioctl = pidfd_ioctl, - .compat_ioctl = compat_ptr_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = pidfd_compat_ioctl, +#endif }; struct pid *pidfd_pid(const struct file *file) -- cgit v1.2.3 From 3656a79f94c471827a08f2cacce5f94ad5e52c24 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 11 Jul 2026 11:19:33 -0400 Subject: amt: re-read skb header pointers after every pull Several AMT receive and transmit paths cache a pointer into the skb head (ip_hdr(), ipv6_hdr(), eth_hdr() or the AMT message header) and then call a helper that can reallocate that head before the cached pointer is used again. pskb_may_pull(), ip_mc_may_pull(), ipv6_mc_may_pull(), iptunnel_pull_header(), ip_mc_check_igmp() and ipv6_mc_check_mld() can all free the old head and move the data, so a pointer taken before the call dangles afterwards and the later access is a use-after-free of the freed head. The affected sites are: amt_rcv() caches ip_hdr() before amt_parse_type() pulls, then reads iph->saddr. amt_dev_xmit() caches ip_hdr()/ipv6_hdr() before ip_mc_check_igmp()/ ipv6_mc_check_mld() and pskb_may_pull(), then reads the group address. amt_multicast_data_handler() caches eth_hdr() before pskb_may_pull(), then writes the L2 header. amt_membership_query_handler() caches the AMT header, the outer and inner eth_hdr() and ip_hdr() before iptunnel_pull_header() and several pulls, then reads and writes them. amt_igmpv3_report_handler() and amt_mldv2_report_handler() cache ip_hdr()/ipv6_hdr() and the current group record and read the record count from the report header inside the record loop, across the *_mc_may_pull() calls. amt_update_handler() caches ip_hdr() and the AMT membership-update header before pskb_may_pull(), iptunnel_pull_header(), ip_mc_check_igmp() and the report handler, then reads iph->daddr and amtmu->nonce / amtmu->response_mac. Fix each site by either snapshotting the scalar that is used after the pull before the first pull runs, or re-deriving the header pointer from the skb after the last pull that can move the head. Values that are stable across the pull (source and group address, the response MAC and nonce, the record count, the outer source MAC) are snapshotted; pointers that are written through or read repeatedly are re-derived. Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface") Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Reviewed-by: Taehee Yoo Link: https://patch.msgid.link/20260711151934.2955226-2-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/amt.c | 79 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 951dd10e192b..35e77af76bd9 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -1211,7 +1211,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) data = true; } v6 = false; - group.ip4 = iph->daddr; + group.ip4 = ip_hdr(skb)->daddr; #if IS_ENABLED(CONFIG_IPV6) } else if (iph->version == 6) { ip6h = ipv6_hdr(skb); @@ -1235,7 +1235,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) data = true; } v6 = true; - group.ip6 = ip6h->daddr; + group.ip6 = ipv6_hdr(skb)->daddr; #endif } else { dev->stats.tx_errors++; @@ -1278,12 +1278,12 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev) hlist_for_each_entry_rcu(gnode, &tunnel->groups[hash], node) { if (!v6) { - if (gnode->group_addr.ip4 == iph->daddr) + if (gnode->group_addr.ip4 == group.ip4) goto found; #if IS_ENABLED(CONFIG_IPV6) } else { if (ipv6_addr_equal(&gnode->group_addr.ip6, - &ip6h->daddr)) + &group.ip6)) goto found; #endif } @@ -2000,14 +2000,18 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb, struct igmpv3_report *ihrv3 = igmpv3_report_hdr(skb); int len = skb_transport_offset(skb) + sizeof(*ihrv3); void *zero_grec = (void *)&igmpv3_zero_grec; - struct iphdr *iph = ip_hdr(skb); struct amt_group_node *gnode; union amt_addr group, host; struct igmpv3_grec *grec; + __be32 saddr; u16 nsrcs; + u16 ngrec; int i; - for (i = 0; i < ntohs(ihrv3->ngrec); i++) { + saddr = ip_hdr(skb)->saddr; + ngrec = ntohs(ihrv3->ngrec); + + for (i = 0; i < ngrec; i++) { len += sizeof(*grec); if (!ip_mc_may_pull(skb, len)) break; @@ -2019,10 +2023,13 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb, if (!ip_mc_may_pull(skb, len)) break; + grec = (void *)(skb->data + len - sizeof(*grec) - + nsrcs * sizeof(__be32)); + memset(&group, 0, sizeof(union amt_addr)); group.ip4 = grec->grec_mca; memset(&host, 0, sizeof(union amt_addr)); - host.ip4 = iph->saddr; + host.ip4 = saddr; gnode = amt_lookup_group(tunnel, &group, &host, false); if (!gnode) { gnode = amt_add_group(amt, tunnel, &group, &host, @@ -2162,14 +2169,18 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb, struct mld2_report *mld2r = (struct mld2_report *)icmp6_hdr(skb); int len = skb_transport_offset(skb) + sizeof(*mld2r); void *zero_grec = (void *)&mldv2_zero_grec; - struct ipv6hdr *ip6h = ipv6_hdr(skb); struct amt_group_node *gnode; union amt_addr group, host; struct mld2_grec *grec; + struct in6_addr saddr; u16 nsrcs; + u16 ngrec; int i; - for (i = 0; i < ntohs(mld2r->mld2r_ngrec); i++) { + saddr = ipv6_hdr(skb)->saddr; + ngrec = ntohs(mld2r->mld2r_ngrec); + + for (i = 0; i < ngrec; i++) { len += sizeof(*grec); if (!ipv6_mc_may_pull(skb, len)) break; @@ -2181,10 +2192,13 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb, if (!ipv6_mc_may_pull(skb, len)) break; + grec = (void *)(skb->data + len - sizeof(*grec) - + nsrcs * sizeof(struct in6_addr)); + memset(&group, 0, sizeof(union amt_addr)); group.ip6 = grec->grec_mca; memset(&host, 0, sizeof(union amt_addr)); - host.ip6 = ip6h->saddr; + host.ip6 = saddr; gnode = amt_lookup_group(tunnel, &group, &host, true); if (!gnode) { gnode = amt_add_group(amt, tunnel, &group, &host, @@ -2305,7 +2319,6 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) skb_push(skb, sizeof(*eth)); skb_reset_mac_header(skb); skb_pull(skb, sizeof(*eth)); - eth = eth_hdr(skb); if (!pskb_may_pull(skb, sizeof(*iph))) return true; @@ -2315,6 +2328,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) if (!ipv4_is_multicast(iph->daddr)) return true; skb->protocol = htons(ETH_P_IP); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IP); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) @@ -2328,6 +2342,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) if (!ipv6_addr_is_multicast(&ip6h->daddr)) return true; skb->protocol = htons(ETH_P_IPV6); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IPV6); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif @@ -2351,10 +2366,12 @@ static bool amt_membership_query_handler(struct amt_dev *amt, struct sk_buff *skb) { struct amt_header_membership_query *amtmq; - struct igmpv3_query *ihv3; struct ethhdr *eth, *oeth; + struct igmpv3_query *ihv3; + u8 h_source[ETH_ALEN]; struct iphdr *iph; int hdr_size, len; + u64 response_mac; hdr_size = sizeof(*amtmq) + sizeof(struct udphdr); if (!pskb_may_pull(skb, hdr_size)) @@ -2367,6 +2384,8 @@ static bool amt_membership_query_handler(struct amt_dev *amt, if (amtmq->nonce != amt->nonce) return true; + response_mac = amtmq->response_mac; + hdr_size -= sizeof(*eth); if (iptunnel_pull_header(skb, hdr_size, htons(ETH_P_TEB), false)) return true; @@ -2376,6 +2395,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_pull(skb, sizeof(*eth)); skb_reset_network_header(skb); eth = eth_hdr(skb); + ether_addr_copy(h_source, oeth->h_source); if (!pskb_may_pull(skb, sizeof(*iph))) return true; @@ -2388,6 +2408,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, sizeof(*ihv3))) return true; + iph = ip_hdr(skb); if (!ipv4_is_multicast(iph->daddr)) return true; @@ -2395,10 +2416,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_reset_transport_header(skb); skb_push(skb, sizeof(*iph) + AMT_IPHDR_OPTS); WRITE_ONCE(amt->ready4, true); - amt->mac = amtmq->response_mac; + amt->mac = response_mac; amt->req_cnt = 0; amt->qi = ihv3->qqic; skb->protocol = htons(ETH_P_IP); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IP); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) @@ -2421,10 +2443,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_reset_transport_header(skb); skb_push(skb, sizeof(*ip6h) + AMT_IP6HDR_OPTS); WRITE_ONCE(amt->ready6, true); - amt->mac = amtmq->response_mac; + amt->mac = response_mac; amt->req_cnt = 0; amt->qi = mld2q->mld2q_qqic; skb->protocol = htons(ETH_P_IPV6); + eth = eth_hdr(skb); eth->h_proto = htons(ETH_P_IPV6); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif @@ -2432,7 +2455,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt, return true; } - ether_addr_copy(eth->h_source, oeth->h_source); + ether_addr_copy(eth->h_source, h_source); skb->pkt_type = PACKET_MULTICAST; skb->ip_summed = CHECKSUM_NONE; len = skb->len; @@ -2455,8 +2478,11 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) struct ethhdr *eth; struct iphdr *iph; int len, hdr_size; + u64 response_mac; + __be32 saddr; + __be32 nonce; - iph = ip_hdr(skb); + saddr = ip_hdr(skb)->saddr; hdr_size = sizeof(*amtmu) + sizeof(struct udphdr); if (!pskb_may_pull(skb, hdr_size)) @@ -2466,15 +2492,18 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb) if (amtmu->reserved || amtmu->version) return true; + nonce = amtmu->nonce; + response_mac = amtmu->response_mac; + if (iptunnel_pull_header(skb, hdr_size, skb->protocol, false)) return true; skb_reset_network_header(skb); list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list) { - if (tunnel->ip4 == iph->saddr) { - if ((amtmu->nonce == tunnel->nonce && - amtmu->response_mac == tunnel->mac)) { + if (tunnel->ip4 == saddr) { + if ((nonce == tunnel->nonce && + response_mac == tunnel->mac)) { mod_delayed_work(amt_wq, &tunnel->gc_wq, msecs_to_jiffies(amt_gmi(amt)) * 3); @@ -2508,6 +2537,7 @@ report: eth = eth_hdr(skb); skb->protocol = htons(ETH_P_IP); eth->h_proto = htons(ETH_P_IP); + iph = ip_hdr(skb); ip_eth_mc_map(iph->daddr, eth->h_dest); #if IS_ENABLED(CONFIG_IPV6) } else if (iph->version == 6) { @@ -2527,6 +2557,7 @@ report: eth = eth_hdr(skb); skb->protocol = htons(ETH_P_IPV6); eth->h_proto = htons(ETH_P_IPV6); + ip6h = ipv6_hdr(skb); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); #endif } else { @@ -2772,7 +2803,7 @@ drop: static int amt_rcv(struct sock *sk, struct sk_buff *skb) { struct amt_dev *amt; - struct iphdr *iph; + __be32 saddr; int type; bool err; @@ -2785,7 +2816,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) } skb->dev = amt->dev; - iph = ip_hdr(skb); + saddr = ip_hdr(skb)->saddr; type = amt_parse_type(skb); if (type == -1) { err = true; @@ -2795,7 +2826,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) if (amt->mode == AMT_MODE_GATEWAY) { switch (type) { case AMT_MSG_ADVERTISEMENT: - if (iph->saddr != amt->discovery_ip) { + if (saddr != amt->discovery_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; @@ -2807,7 +2838,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) } goto out; case AMT_MSG_MULTICAST_DATA: - if (iph->saddr != amt->remote_ip) { + if (saddr != amt->remote_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; @@ -2818,7 +2849,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb) else goto out; case AMT_MSG_MEMBERSHIP_QUERY: - if (iph->saddr != amt->remote_ip) { + if (saddr != amt->remote_ip) { netdev_dbg(amt->dev, "Invalid Relay IP\n"); err = true; goto drop; -- cgit v1.2.3 From 53969d704fa5b7c1751e277fac96bfc22b435eac Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 11 Jul 2026 11:19:34 -0400 Subject: amt: make the head writable before rewriting the L2 header amt_multicast_data_handler(), amt_membership_query_handler() and amt_update_handler() rewrite the ethernet header of the decapsulated skb in place (eth->h_proto, eth->h_dest and, for the query, also eth->h_source) before handing it up the stack. The skb head may be shared, for example when a packet tap has cloned it on the underlay interface, so writing through it corrupts the other reader's copy. Call skb_cow_head() before the rewrite so the head is private. It is placed before the pointers into the head are (re-)derived, so a reallocation caused by the copy is picked up by those derivations. Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface") Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Reviewed-by: Taehee Yoo Link: https://patch.msgid.link/20260711151934.2955226-3-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/amt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index 35e77af76bd9..b733309b866f 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -2320,6 +2320,9 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb) skb_reset_mac_header(skb); skb_pull(skb, sizeof(*eth)); + if (skb_cow_head(skb, 0)) + return true; + if (!pskb_may_pull(skb, sizeof(*iph))) return true; iph = ip_hdr(skb); @@ -2396,6 +2399,8 @@ static bool amt_membership_query_handler(struct amt_dev *amt, skb_reset_network_header(skb); eth = eth_hdr(skb); ether_addr_copy(h_source, oeth->h_source); + if (skb_cow_head(skb, 0)) + return true; if (!pskb_may_pull(skb, sizeof(*iph))) return true; @@ -2521,6 +2526,9 @@ report: if (!pskb_may_pull(skb, sizeof(*iph))) return true; + if (skb_cow_head(skb, 0)) + return true; + iph = ip_hdr(skb); if (iph->version == 4) { if (ip_mc_check_igmp(skb)) { -- cgit v1.2.3 From e148e567a9252643baa125cb65d7ae9c2c6cf68a Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 17 Jul 2026 11:06:45 +0900 Subject: ksmbd: preserve VFS inherited POSIX ACL mask The VFS initializes a child's POSIX ACL from the parent's default ACL and the requested creation mode. Do not mutate the parent ACL or overwrite the child's VFS-computed access and default ACLs afterwards. This preserves restrictive ACL_MASK entries and prevents SMB object creation from widening effective permissions. Reported-by: Charles Vosburgh Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index d0a0ad15d803..d324585c0566 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1886,10 +1886,6 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap, const struct path *path, struct inode *parent_inode) { struct posix_acl *acls; - struct posix_acl_entry *pace; - struct dentry *dentry = path->dentry; - struct inode *inode = d_inode(dentry); - int rc, i; if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) return -EOPNOTSUPP; @@ -1897,29 +1893,9 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap, acls = get_inode_acl(parent_inode, ACL_TYPE_DEFAULT); if (IS_ERR_OR_NULL(acls)) return -ENOENT; - pace = acls->a_entries; - - for (i = 0; i < acls->a_count; i++, pace++) { - if (pace->e_tag == ACL_MASK) { - pace->e_perm = 0x07; - break; - } - } - - rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - if (S_ISDIR(inode->i_mode)) { - rc = set_posix_acl(idmap, dentry, ACL_TYPE_DEFAULT, - acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); - } posix_acl_release(acls); - return rc; + return 0; } void ksmbd_vfs_update_compressed_fattr(struct dentry *dentry, __le32 *fattr) -- cgit v1.2.3 From 2bebf2470af1a72f87754a5c7b21e86af32b9c8f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 17 Jul 2026 11:32:00 +0900 Subject: ksmbd: enforce signing required by the session SMB2_FLAGS_SIGNED is controlled by the incoming request and only indicates that a signature accompanies that request. Do not use it to decide whether a signing-required session must authenticate the request. Reject an unsigned plaintext request before dispatch when the session requires signing. Continue to validate signatures on signed requests, including when signing is optional. Encrypted requests have already been authenticated during decryption. An OPLOCK_BREAK acknowledgment is a session request and is subject to the same signing rule, so do not exclude it from signed-request detection. Reported-by: Charles Vosburgh Tested-by: ChenXiaoSong Reviewed-by: ChenXiaoSong Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/server.c | 10 +++++++++- fs/smb/server/smb2pdu.c | 3 +-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index f5baba934840..960c4c897c11 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -112,6 +112,7 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, { struct smb_version_cmds *cmds; u16 command; + bool signed_req; int ret; if (check_conn_state(work)) @@ -138,7 +139,14 @@ andx_again: return SERVER_HANDLER_ABORT; } - if (work->sess && conn->ops->is_sign_req(work, command)) { + signed_req = conn->ops->is_sign_req && conn->ops->is_sign_req(work, command); + if (work->sess && work->sess->sign && !work->encrypted && + !signed_req) { + conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); + return SERVER_HANDLER_ABORT; + } + + if (work->sess && signed_req) { ret = conn->ops->check_sign_req(work); if (!ret) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index bec692bca1ca..d54b714cc36c 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -9596,8 +9596,7 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) struct smb2_hdr *rcv_hdr2 = smb_get_msg(work->request_buf); if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && - command != SMB2_NEGOTIATE_HE && - command != SMB2_OPLOCK_BREAK_HE) + command != SMB2_NEGOTIATE_HE) return true; return false; -- cgit v1.2.3 From 58d97fcd0bf1aee694e244cc28635b9df95b543b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 3 Jul 2026 10:54:19 +0900 Subject: ksmbd: bound DACL dedup walk to copied ACEs set_ntacl_dacl() can stop copying ACEs before consuming the full input DACL when size accounting overflows. When that happens, num_aces reflects only the ACEs that were actually copied into the output DACL, but set_posix_acl_entries_dacl() still receives nt_num_aces and uses it to walk the existing ACE array during dedup. That makes the dedup walk scan past the copied ACE array and inspect buffer tail that does not contain valid ACEs. Split the two meanings currently carried by the NT ACE count. Pass the number of copied NT ACEs to bound the dedup walk, and preserve the original "input DACL had NT ACEs" state separately for the Everyone/default ACL fallback. This keeps the dedup walk aligned with the ACEs that are actually present in the rebuilt DACL. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 67b39b4d218c..d28289188e44 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -608,7 +608,8 @@ static void parse_dacl(struct mnt_idmap *idmap, static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, struct smb_ace *pndace, struct smb_fattr *fattr, u16 *num_aces, - u16 *size, u32 nt_aces_num) + u16 *size, u16 existing_nt_aces, + bool had_nt_aces) { struct posix_acl_entry *pace; struct smb_sid *sid; @@ -640,14 +641,14 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, gid = posix_acl_gid_translate(idmap, pace); id_to_sid(gid, SIDUNIX_GROUP, sid); - } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { + } else if (pace->e_tag == ACL_OTHER && !had_nt_aces) { smb_copy_sid(sid, &sid_everyone); } else { kfree(sid); continue; } ntace = pndace; - for (j = 0; j < nt_aces_num; j++) { + for (j = 0; j < existing_nt_aces; j++) { if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == sid->sub_auth[sid->num_subauth - 1]) goto pass_same_sid; @@ -689,7 +690,7 @@ pass_same_sid: kfree(sid); } - if (nt_aces_num) + if (had_nt_aces) return; posix_default_acl: @@ -742,6 +743,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, { struct smb_ace *ntace, *pndace; u16 nt_num_aces = le16_to_cpu(nt_dacl->num_aces), num_aces = 0; + u16 copied_nt_aces; unsigned short size = 0; int i; @@ -773,8 +775,10 @@ next_ace: } } + copied_nt_aces = num_aces; set_posix_acl_entries_dacl(idmap, pndace, fattr, - &num_aces, &size, nt_num_aces); + &num_aces, &size, copied_nt_aces, + nt_num_aces != 0); pndacl->num_aces = cpu_to_le16(num_aces); pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); } @@ -792,7 +796,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap, if (fattr->cf_acls) { set_posix_acl_entries_dacl(idmap, pndace, fattr, - &num_aces, &size, num_aces); + &num_aces, &size, num_aces, false); goto out; } -- cgit v1.2.3 From bbf0a8e931204ecdab494a88d43b0a24a04285c5 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Fri, 3 Jul 2026 11:22:09 +0900 Subject: ksmbd: restore DACL size on check_add_overflow() to avoid malformed ACL check_add_overflow() unconditionally writes the truncated sum into *d even on overflow, per its contract in include/linux/overflow.h. The four check_add_overflow() guards in set_posix_acl_entries_dacl() and set_ntacl_dacl() break out of the ACE-building loops on overflow, but the truncated *size is then consumed downstream at the end of set_ntacl_dacl(): pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); This produces an on-wire NT ACL whose pndacl->size under-reports the bytes actually written by the preceding fill_ace_for_sid()/memcpy() calls, yielding a malformed ACL that can trigger out-of-bounds reads when re-parsed by clients or ksmbd itself. Restore *size to its pre-addition value on each overflow branch (via `*size -= ace_sz` / `size -= nt_ace_size`) so that after the break, *size once again holds the cumulative size of the successfully-written ACEs. The committed ACL is then truncated-but-self-consistent rather than malformed. The ksmbd DACL builders are the only check_add_overflow() sites found where an overflow path breaks out of a loop and the destination value is consumed afterward. The other nearby break-style cases either return -EINVAL on overflow (transport_ipc.c) or break without consuming the overflowed destination value afterward (buildid.c). Fixes: 299f962c0b02 ("ksmbd: use check_add_overflow() to prevent u16 DACL size overflow") Assisted-by: atomcode:glm-5.2 Assisted-by: Codex:gpt-5.5 Cc: stable@vger.kernel.org Signed-off-by: Wentao Guan Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index d28289188e44..f285b4f24a5b 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -663,6 +663,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, pace->e_perm, 0777); if (check_add_overflow(*size, ace_sz, size)) { + *size -= ace_sz; kfree(sid); break; } @@ -677,6 +678,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x03, pace->e_perm, 0777); if (check_add_overflow(*size, ace_sz, size)) { + *size -= ace_sz; kfree(sid); break; } @@ -722,6 +724,7 @@ posix_default_acl: ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, pace->e_perm, 0777); if (check_add_overflow(*size, ace_sz, size)) { + *size -= ace_sz; kfree(sid); break; } @@ -765,8 +768,10 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, goto next_ace; memcpy((char *)pndace + size, ntace, nt_ace_size); - if (check_add_overflow(size, nt_ace_size, &size)) + if (check_add_overflow(size, nt_ace_size, &size)) { + size -= nt_ace_size; break; + } num_aces++; next_ace: -- cgit v1.2.3 From 5152c6d49e3fd4e9f2e857c57527aead752f1f87 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 4 Jul 2026 11:07:51 +0900 Subject: ksmbd: validate ACE size against SID sub-authorities set_ntacl_dacl() validates sid.num_subauth before copying an ACE, but does not verify that the declared ACE size contains all sub-authorities described by that field. An undersized ACE can therefore be copied and later make the POSIX ACL deduplication walk inspect data beyond the copied ACE boundary. The existing initial bound check is also too small. It only ensures that the ACE size field is accessible before set_ntacl_dacl() reads sid.num_subauth farther into the input buffer. Require enough input for the fixed SID header before accessing num_subauth, reject ACEs smaller than that header, and skip ACEs whose declared size cannot contain the complete SID. This makes the validation consistent with the other ACE walk paths. Reported-by: LocalHost Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index f285b4f24a5b..c13f07a09ab8 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -756,15 +756,22 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap, for (i = 0; i < nt_num_aces; i++) { unsigned short nt_ace_size; - if (offsetof(struct smb_ace, access_req) > aces_size) + if (aces_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; nt_ace_size = le16_to_cpu(ntace->size); - if (nt_ace_size > aces_size) + if (nt_ace_size > aces_size || + nt_ace_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; if (ntace->sid.num_subauth == 0 || - ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) + ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + nt_ace_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE + + sizeof(__le32) * + ntace->sid.num_subauth) goto next_ace; memcpy((char *)pndace + size, ntace, nt_ace_size); -- cgit v1.2.3 From c74801ee524f477c174a1899782b6c3b6918d407 Mon Sep 17 00:00:00 2001 From: James Montgomery Date: Fri, 3 Jul 2026 15:26:41 -0400 Subject: ksmbd: defer destroy_previous_session() until after NTLM authentication In ntlm_authenticate(), destroy_previous_session() is called using a user pointer resolved from the client-supplied NTLM blob username field before the NTLMv2 response is validated. An authenticated attacker can set the NTLM blob username to match a victim account and set PreviousSessionId to the victim's session ID; destroy_previous_session() destroys the victim's session while ksmbd_decode_ntlmssp_auth_blob() subsequently rejects the request with -EPERM. Move destroy_previous_session() and the prev_id assignment to after ksmbd_decode_ntlmssp_auth_blob() returns success and use sess->user rather than the pre-authentication lookup result. This matches the ordering already used by krb5_authenticate(), where destroy_previous_session() is called only after ksmbd_krb5_authenticate() returns success. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-cifs/20260702155449.3639773-1-james_montgomery@disroot.org/ Signed-off-by: James Montgomery Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index d54b714cc36c..c1ba5e01aa7f 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1717,11 +1717,6 @@ static int ntlm_authenticate(struct ksmbd_work *work, return -EPERM; } - /* Check for previous session */ - prev_id = le64_to_cpu(req->PreviousSessionId); - if (prev_id && prev_id != sess->id) - destroy_previous_session(conn, user, prev_id); - if (sess->state == SMB2_SESSION_VALID) { /* * Reuse session if anonymous try to connect @@ -1761,6 +1756,10 @@ static int ntlm_authenticate(struct ksmbd_work *work, } } + prev_id = le64_to_cpu(req->PreviousSessionId); + if (prev_id && prev_id != sess->id) + destroy_previous_session(conn, sess->user, prev_id); + /* * If session state is SMB2_SESSION_VALID, We can assume * that it is reauthentication. And the user/password -- cgit v1.2.3 From cfc0b8e5080aec87700774e8568765eaa4b7b92b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 4 Jul 2026 11:30:27 +0900 Subject: ksmbd: validate minimum PDU size for transform requests The receive path applies the minimum SMB2 PDU size check only when ProtocolId is SMB2_PROTO_NUMBER. A packet carrying SMB2_TRANSFORM_PROTO_NUM bypasses the check even when the negotiated dialect does not provide transform handling. On an SMB 2.1 connection, a short transform packet therefore reaches init_smb2_rsp_hdr(), which interprets the request as a full SMB2 header and reads beyond the request allocation. The copied fields can then be returned to the unauthenticated client. Compression transforms are converted to ordinary SMB2 messages before protocol validation. After that conversion, validate ordinary SMB2 requests against SMB2_MIN_SUPPORTED_PDU_SIZE and require encryption transform requests to contain both a transform header and an SMB2 header. This rejects truncated requests before work allocation. Fixes: 368ba06881c3 ("ksmbd: check the validation of pdu_size in ksmbd_conn_handler_loop") Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-31063 Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/connection.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 9e8fdb39e5a2..dee8e4aced99 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -441,6 +441,8 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn) /* "+2" for BCC field (ByteCount, 2 bytes) */ #define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2) #define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu)) +#define SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE \ + (sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr)) /** * ksmbd_conn_handler_loop() - session thread to listen on new smb requests @@ -455,6 +457,7 @@ int ksmbd_conn_handler_loop(void *p) struct ksmbd_conn *conn = (struct ksmbd_conn *)p; struct ksmbd_transport *t = conn->transport; unsigned int pdu_size, max_allowed_pdu_size, max_req; + __le32 proto; char hdr_buf[4] = {0,}; int size; @@ -546,11 +549,14 @@ recheck: if (!ksmbd_smb_request(conn)) break; - if (((struct smb2_hdr *)smb_get_msg(conn->request_buf))->ProtocolId == - SMB2_PROTO_NUMBER) { - if (pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE) - break; - } + proto = *(__le32 *)smb_get_msg(conn->request_buf); + if (proto == SMB2_PROTO_NUMBER && + pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE) + break; + + if (proto == SMB2_TRANSFORM_PROTO_NUM && + pdu_size < SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE) + break; if (!default_conn_ops.process_fn) { pr_err("No connection request callback\n"); -- cgit v1.2.3 From 5e1b924808568e89c5cb132ecebe1824bd91af0c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 4 Jul 2026 12:23:14 +0900 Subject: ksmbd: reject undersized decompressed SMB2 requests ksmbd_decompress_request() bounds the decompressed size only against the maximum request size. A compression transform can therefore produce a buffer smaller than an SMB2 PDU and install it as conn->request_buf. The receive path subsequently calls ksmbd_smb_request(), which reads the protocol ID before the normal SMB2 minimum-size check. If the decompressed output is too short, that read can access beyond the request allocation. Require the decompressed output to contain at least a complete minimum SMB2 PDU before allocating and installing the replacement request buffer. Fixes: a08de24c2b85 ("ksmbd: negotiate and decode SMB2 compression") Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/compress.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/compress.c b/fs/smb/server/compress.c index f8cf515b9c30..95e48fa6b448 100644 --- a/fs/smb/server/compress.c +++ b/fs/smb/server/compress.c @@ -56,7 +56,8 @@ int ksmbd_decompress_request(struct ksmbd_conn *conn) } max_allowed_pdu_size = SMB3_MAX_MSGSIZE + conn->vals->max_write_size; - if (out_size > max_allowed_pdu_size || + if (out_size < sizeof(struct smb2_pdu) || + out_size > max_allowed_pdu_size || out_size > MAX_STREAM_PROT_LEN) return -EINVAL; -- cgit v1.2.3 From 14fa65d10f5696b063a7d8d26e8291ea84a2c6ed Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Sun, 12 Jul 2026 14:27:29 +0000 Subject: net: hip04: fix RX buffer leak on build_skb failure When build_skb() fails in hip04_rx_poll(), the driver jumps to the refill path without releasing the current RX buffer and its DMA mapping. Installing a replacement buffer then overwrites the slot references and leaks both resources. Keep the current slot intact and return budget so NAPI retries the same buffer. Also free a newly allocated RX fragment when dma_map_single() fails. This issue was found by an in-house static analysis tool. Fixes: 701a0fd52318 ("hip04_eth: fix missing error handle for build_skb failed") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260712142729.2057636-1-fanwu01@zju.edu.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hip04_eth.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index 18376bcc718a..fc2c47dcfaab 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -594,7 +594,11 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget) skb = build_skb(buf, priv->rx_buf_size); if (unlikely(!skb)) { net_dbg_ratelimited("build_skb failed\n"); - goto refill; + /* Retain the slot; return budget so NAPI retries this + * buffer. Refill would overwrite rx_buf[]/rx_phys[] + * and leak them. + */ + return budget; } dma_unmap_single(priv->dev, priv->rx_phys[priv->rx_head], @@ -622,14 +626,15 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget) rx++; } -refill: buf = netdev_alloc_frag(priv->rx_buf_size); if (!buf) goto done; phys = dma_map_single(priv->dev, buf, RX_BUF_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(priv->dev, phys)) + if (dma_mapping_error(priv->dev, phys)) { + skb_free_frag(buf); goto done; + } priv->rx_buf[priv->rx_head] = buf; priv->rx_phys[priv->rx_head] = phys; hip04_set_recv_desc(priv, phys); -- cgit v1.2.3 From 6cb22477929489a412df8d153e550e77a012e701 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:27 +0530 Subject: phy: zynqmp: fix L0_TM_DISABLE_SCRAMBLE_ENCODER mask The L0_TX_DIG_61 register bit 2 is a reserved read-only field. The previous mask value 0x0f incorrectly included bit 2, causing unintended writes to a reserved bit on every scrambler bypass operation. Correct the mask to (BIT(3) | GENMASK(1, 0)) to cover only the valid scramble bypass control bits. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-2-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 2138f5399821..58fa2f837689 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -53,7 +53,7 @@ #define L0_TM_DIG_6 0x106c #define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f #define L0_TX_DIG_61 0x00f4 -#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f +#define L0_TM_DISABLE_SCRAMBLE_ENCODER (BIT(3) | GENMASK(1, 0)) /* PLL Test Mode register parameters */ #define L0_TM_PLL_DIG_37 0x2094 -- cgit v1.2.3 From 21e0749f931702765b9d52d05740092bc87fcd8d Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:28 +0530 Subject: phy: zynqmp: use read-modify-write for SERDES scrambler bypass xpsgtr_bypass_scrambler_8b10b() used xpsgtr_write_phy() which performs a full register write, silently clearing any bits beyond the intended bypass control fields. Switch to xpsgtr_clr_set_phy() with clr=mask, set=mask to set only the bypass bits while preserving the remaining bits in each register. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-3-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 58fa2f837689..0e99b2cc2a2d 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -505,8 +505,12 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) /* Bypass (de)scrambler and 8b/10b decoder and encoder. */ static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) { - xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER); - xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, + L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, + L0_TM_DISABLE_SCRAMBLE_ENCODER); } /* DP-specific initialization. */ -- cgit v1.2.3 From 7eb61caf45607e1e1270f51f8f93f0ded53146da Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:29 +0530 Subject: phy: zynqmp: keep SERDES scrambler and 8b/10b enabled for USB USB Gen1 requires scrambling and 8b/10b encoding to be performed in the physical layer. Do not bypass PHY-side scrambler or encoder/decoder for USB operation, as mandated by the USB 3.x specification. Scrambler and 8b/10b bypass remain restricted to SATA and SGMII modes, where encoding is handled in the controller. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-4-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 0e99b2cc2a2d..240626b55475 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -502,15 +502,30 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) } } -/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ -static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) +/** + * xpsgtr_bypass_scrambler_8b10b - Configure scrambler/encoder behavior + * @gtr_phy: pointer to lane context + * @bypass: true to enable scrambler/encoder bypass (SATA/SGMII), + * false to disable scrambler/encoder bypass (USB3) + * + * Uses RMW to preserve reserved and unrelated register fields. + */ +static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy, + bool bypass) { - xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, - L0_TM_DIS_DESCRAMBLE_DECODER, - L0_TM_DIS_DESCRAMBLE_DECODER); - xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, - L0_TM_DISABLE_SCRAMBLE_ENCODER, - L0_TM_DISABLE_SCRAMBLE_ENCODER); + if (bypass) { + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, + L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, + L0_TM_DISABLE_SCRAMBLE_ENCODER); + } else { + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, 0); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, 0); + } } /* DP-specific initialization. */ @@ -531,7 +546,7 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy) { struct xpsgtr_dev *gtr_dev = gtr_phy->dev; - xpsgtr_bypass_scrambler_8b10b(gtr_phy); + xpsgtr_bypass_scrambler_8b10b(gtr_phy, true); writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET); } @@ -547,7 +562,7 @@ static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy) xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val); xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val); - xpsgtr_bypass_scrambler_8b10b(gtr_phy); + xpsgtr_bypass_scrambler_8b10b(gtr_phy, true); } /* Configure TX de-emphasis and margining for DP. */ @@ -708,6 +723,10 @@ static int xpsgtr_phy_init(struct phy *phy) case ICM_PROTOCOL_SGMII: xpsgtr_phy_init_sgmii(gtr_phy); break; + + case ICM_PROTOCOL_USB: + xpsgtr_bypass_scrambler_8b10b(gtr_phy, false); + break; } goto out; -- cgit v1.2.3 From af78c0020480aba3132fb9fa4db0fd1fb949feaa Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Wed, 22 Jul 2026 14:42:15 +0100 Subject: io_uring/zcrx: drop "notif" from stats struct names Keep zcrx statistics generic and don't stick "notif" to its uapi definitions. Stats dosn't need to be bound to notification details, it makes it cleaner and more readable. Signed-off-by: Pavel Begunkov Link: https://patch.msgid.link/6a39676b6f71b67d3f89c6ebab7a3739873834a3.1784726895.git.asml.silence@gmail.com Signed-off-by: Jens Axboe --- include/uapi/linux/io_uring/query.h | 6 +++--- include/uapi/linux/io_uring/zcrx.h | 4 ++-- io_uring/query.c | 4 ++-- io_uring/zcrx.c | 4 ++-- io_uring/zcrx.h | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/uapi/linux/io_uring/query.h b/include/uapi/linux/io_uring/query.h index 1a68eca7c6b4..708bcdd585dd 100644 --- a/include/uapi/linux/io_uring/query.h +++ b/include/uapi/linux/io_uring/query.h @@ -66,10 +66,10 @@ struct io_uring_query_zcrx { struct io_uring_query_zcrx_notif { /* Bitmask of supported ZCRX_NOTIF_* flags */ __u32 notif_flags; - /* Size of io_uring_zcrx_notif_stats */ - __u32 notif_stats_size; + /* Size of zcrx_stats */ + __u32 stats_size; /* Required alignment for the stats struct within the region (ie stats_offset) */ - __u32 notif_stats_off_alignment; + __u32 stats_off_alignment; __u32 __resv1; __u64 __resv2[4]; }; diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h index 15c05c45ce36..99e56ec26ead 100644 --- a/include/uapi/linux/io_uring/zcrx.h +++ b/include/uapi/linux/io_uring/zcrx.h @@ -76,11 +76,11 @@ enum zcrx_notification_type { }; enum zcrx_notification_desc_flags { - /* If set, stats_offset holds a valid offset to a notif_stats struct */ + /* If set, stats_offset holds a valid offset to a zcrx_stats struct */ ZCRX_NOTIF_DESC_FLAG_STATS = 1 << 0, }; -struct zcrx_notif_stats { +struct zcrx_stats { __u64 copy_count; /* cumulative copy-fallback CQEs */ __u64 copy_bytes; /* cumulative bytes copied */ }; diff --git a/io_uring/query.c b/io_uring/query.c index d529d94aa8f4..2e48fddd8d3a 100644 --- a/io_uring/query.c +++ b/io_uring/query.c @@ -50,8 +50,8 @@ static ssize_t io_query_zcrx_notif(union io_query_data *data) struct io_uring_query_zcrx_notif *e = &data->zcrx_notif; e->notif_flags = ZCRX_NOTIF_TYPE_MASK; - e->notif_stats_size = sizeof(struct zcrx_notif_stats); - e->notif_stats_off_alignment = __alignof__(struct zcrx_notif_stats); + e->stats_size = sizeof(struct zcrx_stats); + e->stats_off_alignment = __alignof__(struct zcrx_stats); e->__resv1 = 0; memset(&e->__resv2, 0, sizeof(e->__resv2)); return sizeof(*e); diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 49163f9c39df..53dcb5110719 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -863,12 +863,12 @@ static int zcrx_validate_notif_stats(struct io_zcrx_ifq *ifq, used = reg->offsets.rqes + sizeof(struct io_uring_zcrx_rqe) * reg->rq_entries; - if (!IS_ALIGNED(stats_off, __alignof__(struct zcrx_notif_stats))) + if (!IS_ALIGNED(stats_off, __alignof__(struct zcrx_stats))) return -EINVAL; if (stats_off < used) return -ERANGE; if (check_add_overflow(stats_off, - sizeof(struct zcrx_notif_stats), + sizeof(struct zcrx_stats), &end)) return -ERANGE; if (end > io_region_size(&ifq->rq_region)) diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h index fa00900e479e..a8d301b6191f 100644 --- a/io_uring/zcrx.h +++ b/io_uring/zcrx.h @@ -80,7 +80,7 @@ struct io_zcrx_ifq { u32 allowed_notif_mask; u32 fired_notifs; u64 notif_data; - struct zcrx_notif_stats *notif_stats; + struct zcrx_stats *notif_stats; }; #if defined(CONFIG_IO_URING_ZCRX) -- cgit v1.2.3 From 201e30810223a40275f17c5e6ee410fa857b6b44 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Wed, 22 Jul 2026 14:42:16 +0100 Subject: io_uring/zcrx: rename ZCRX_NOTIF_NO_BUFFERS ZCRX_NOTIF_NO_BUFFERS tells when page pool fails to allocate memory from zcrx. "No buffers" could be more confusing, rename it to ZCRX_NOTIF_ALLOC_FAIL. Signed-off-by: Pavel Begunkov Link: https://patch.msgid.link/29bd4fc069bc89691868beba0627ffbe570c2722.1784726895.git.asml.silence@gmail.com Signed-off-by: Jens Axboe --- include/uapi/linux/io_uring/zcrx.h | 2 +- io_uring/zcrx.c | 2 +- io_uring/zcrx.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h index 99e56ec26ead..abb898d59a4b 100644 --- a/include/uapi/linux/io_uring/zcrx.h +++ b/include/uapi/linux/io_uring/zcrx.h @@ -69,7 +69,7 @@ enum zcrx_features { }; enum zcrx_notification_type { - ZCRX_NOTIF_NO_BUFFERS, + ZCRX_NOTIF_ALLOC_FAIL, ZCRX_NOTIF_COPY, __ZCRX_NOTIF_TYPE_LAST, diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 53dcb5110719..872d5794d3de 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -1244,7 +1244,7 @@ static netmem_ref io_pp_zc_alloc_netmems(struct page_pool *pp, gfp_t gfp) allocated = io_zcrx_refill_slow(pp, ifq, netmems, to_alloc); if (!allocated) { - zcrx_send_notif(ifq, ZCRX_NOTIF_NO_BUFFERS); + zcrx_send_notif(ifq, ZCRX_NOTIF_ALLOC_FAIL); return 0; } out_return: diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h index a8d301b6191f..d16206eb9e30 100644 --- a/io_uring/zcrx.h +++ b/io_uring/zcrx.h @@ -11,7 +11,7 @@ #define ZCRX_SUPPORTED_REG_FLAGS (ZCRX_REG_IMPORT | ZCRX_REG_NODEV) #define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE |\ ZCRX_FEATURE_NOTIFICATION) -#define ZCRX_NOTIF_TYPE_MASK ((1U << ZCRX_NOTIF_NO_BUFFERS) | (1U << ZCRX_NOTIF_COPY)) +#define ZCRX_NOTIF_TYPE_MASK ((1U << ZCRX_NOTIF_ALLOC_FAIL) | (1U << ZCRX_NOTIF_COPY)) struct io_zcrx_mem { unsigned long size; -- cgit v1.2.3 From e366c15e1610ef11d0717ecd875ae63050282676 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Wed, 22 Jul 2026 14:42:17 +0100 Subject: io_uring/zcrx: rename notif to event "Notification" is too long and the abbreviated version is used in several places, which is inconsistent and more ambiguous for users. Rename it to event, which is easier to keep consistent. To keep the change small, only change uapi/ + do necessary fix ups, and the rest of internals can be adjusted in the next release. Signed-off-by: Pavel Begunkov Link: https://patch.msgid.link/f95ca6717da3c8d3649a1a7f0d883a563f545052.1784726895.git.asml.silence@gmail.com Signed-off-by: Jens Axboe --- include/uapi/linux/io_uring/query.h | 8 ++++---- include/uapi/linux/io_uring/zcrx.h | 28 ++++++++++++++-------------- io_uring/query.c | 8 ++++---- io_uring/zcrx.c | 30 +++++++++++++++--------------- io_uring/zcrx.h | 5 +++-- 5 files changed, 40 insertions(+), 39 deletions(-) diff --git a/include/uapi/linux/io_uring/query.h b/include/uapi/linux/io_uring/query.h index 708bcdd585dd..e6493e4a7a4d 100644 --- a/include/uapi/linux/io_uring/query.h +++ b/include/uapi/linux/io_uring/query.h @@ -23,7 +23,7 @@ enum { IO_URING_QUERY_OPCODES = 0, IO_URING_QUERY_ZCRX = 1, IO_URING_QUERY_SCQ = 2, - IO_URING_QUERY_ZCRX_NOTIF = 3, + IO_URING_QUERY_ZCRX_EVENT = 3, __IO_URING_QUERY_MAX, }; @@ -63,9 +63,9 @@ struct io_uring_query_zcrx { __u64 __resv2; }; -struct io_uring_query_zcrx_notif { - /* Bitmask of supported ZCRX_NOTIF_* flags */ - __u32 notif_flags; +struct io_uring_query_zcrx_event { + /* Bitmask of supported ZCRX_EVENT_* flags */ + __u32 event_flags; /* Size of zcrx_stats */ __u32 stats_size; /* Required alignment for the stats struct within the region (ie stats_offset) */ diff --git a/include/uapi/linux/io_uring/zcrx.h b/include/uapi/linux/io_uring/zcrx.h index abb898d59a4b..e01bc0e34b24 100644 --- a/include/uapi/linux/io_uring/zcrx.h +++ b/include/uapi/linux/io_uring/zcrx.h @@ -65,19 +65,19 @@ enum zcrx_features { * value in struct io_uring_zcrx_ifq_reg::rx_buf_len. */ ZCRX_FEATURE_RX_PAGE_SIZE = 1 << 0, - ZCRX_FEATURE_NOTIFICATION = 1 << 1, + ZCRX_FEATURE_EVENT = 1 << 1, }; -enum zcrx_notification_type { - ZCRX_NOTIF_ALLOC_FAIL, - ZCRX_NOTIF_COPY, +enum zcrx_event_type { + ZCRX_EVENT_ALLOC_FAIL, + ZCRX_EVENT_COPY, - __ZCRX_NOTIF_TYPE_LAST, + __ZCRX_EVENT_TYPE_LAST, }; -enum zcrx_notification_desc_flags { +enum zcrx_event_desc_flags { /* If set, stats_offset holds a valid offset to a zcrx_stats struct */ - ZCRX_NOTIF_DESC_FLAG_STATS = 1 << 0, + ZCRX_EVENT_DESC_FLAG_STATS = 1 << 0, }; struct zcrx_stats { @@ -85,10 +85,10 @@ struct zcrx_stats { __u64 copy_bytes; /* cumulative bytes copied */ }; -struct zcrx_notification_desc { +struct zcrx_event_desc { __u64 user_data; __u32 type_mask; - __u32 flags; /* see enum zcrx_notification_desc_flags */ + __u32 flags; /* see enum zcrx_event_desc_flags */ __u64 stats_offset; /* offset from the beginning of refill ring region for stats */ __u64 __resv2[9]; }; @@ -108,14 +108,14 @@ struct io_uring_zcrx_ifq_reg { struct io_uring_zcrx_offsets offsets; __u32 zcrx_id; __u32 rx_buf_len; - __u64 notif_desc; /* see struct zcrx_notification_desc */ + __u64 event_desc; /* see struct zcrx_event_desc */ __u64 __resv[2]; }; enum zcrx_ctrl_op { ZCRX_CTRL_FLUSH_RQ, ZCRX_CTRL_EXPORT, - ZCRX_CTRL_ARM_NOTIFICATION, + ZCRX_CTRL_ARM_EVENT, __ZCRX_CTRL_LAST, }; @@ -129,8 +129,8 @@ struct zcrx_ctrl_export { __u32 __resv1[11]; }; -struct zcrx_ctrl_arm_notif { - __u32 notif_type; +struct zcrx_ctrl_arm_event { + __u32 event_type; /* see enum zcrx_event_type */ __u32 __resv[11]; }; @@ -142,7 +142,7 @@ struct zcrx_ctrl { union { struct zcrx_ctrl_export zc_export; struct zcrx_ctrl_flush_rq zc_flush; - struct zcrx_ctrl_arm_notif zc_arm_notif; + struct zcrx_ctrl_arm_event zc_arm_event; }; }; diff --git a/io_uring/query.c b/io_uring/query.c index 2e48fddd8d3a..88a325736992 100644 --- a/io_uring/query.c +++ b/io_uring/query.c @@ -9,7 +9,7 @@ union io_query_data { struct io_uring_query_opcode opcodes; struct io_uring_query_zcrx zcrx; - struct io_uring_query_zcrx_notif zcrx_notif; + struct io_uring_query_zcrx_event zcrx_notif; struct io_uring_query_scq scq; }; @@ -47,9 +47,9 @@ static ssize_t io_query_zcrx(union io_query_data *data) static ssize_t io_query_zcrx_notif(union io_query_data *data) { - struct io_uring_query_zcrx_notif *e = &data->zcrx_notif; + struct io_uring_query_zcrx_event *e = &data->zcrx_notif; - e->notif_flags = ZCRX_NOTIF_TYPE_MASK; + e->event_flags = ZCRX_EVENT_TYPE_MASK; e->stats_size = sizeof(struct zcrx_stats); e->stats_off_alignment = __alignof__(struct zcrx_stats); e->__resv1 = 0; @@ -96,7 +96,7 @@ static int io_handle_query_entry(union io_query_data *data, void __user *uhdr, case IO_URING_QUERY_ZCRX: ret = io_query_zcrx(data); break; - case IO_URING_QUERY_ZCRX_NOTIF: + case IO_URING_QUERY_ZCRX_EVENT: ret = io_query_zcrx_notif(data); break; case IO_URING_QUERY_SCQ: diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 872d5794d3de..7d9fcf34539a 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -766,7 +766,7 @@ static int import_zcrx(struct io_ring_ctx *ctx, return -EINVAL; if (reg->if_rxq || reg->rq_entries || reg->area_ptr || reg->region_ptr) return -EINVAL; - if (reg->notif_desc) + if (reg->event_desc) return -EINVAL; if (reg->flags & ~ZCRX_REG_IMPORT) return -EINVAL; @@ -855,7 +855,7 @@ netdev_put_unlock: static int zcrx_validate_notif_stats(struct io_zcrx_ifq *ifq, const struct io_uring_zcrx_ifq_reg *reg, - const struct zcrx_notification_desc *notif) + const struct zcrx_event_desc *notif) { size_t stats_off = notif->stats_offset; size_t used, end; @@ -883,7 +883,7 @@ static int zcrx_validate_notif_stats(struct io_zcrx_ifq *ifq, int io_register_zcrx(struct io_ring_ctx *ctx, struct io_uring_zcrx_ifq_reg __user *arg) { - struct zcrx_notification_desc notif; + struct zcrx_event_desc notif; struct io_uring_zcrx_area_reg area; struct io_uring_zcrx_ifq_reg reg; struct io_uring_region_desc rd; @@ -928,14 +928,14 @@ int io_register_zcrx(struct io_ring_ctx *ctx, return -EFAULT; memset(¬if, 0, sizeof(notif)); - if (reg.notif_desc && copy_from_user(¬if, u64_to_user_ptr(reg.notif_desc), + if (reg.event_desc && copy_from_user(¬if, u64_to_user_ptr(reg.event_desc), sizeof(notif))) return -EFAULT; - if (notif.type_mask & ~ZCRX_NOTIF_TYPE_MASK) + if (notif.type_mask & ~ZCRX_EVENT_TYPE_MASK) return -EINVAL; - if (notif.flags & ~ZCRX_NOTIF_DESC_FLAG_STATS) + if (notif.flags & ~ZCRX_EVENT_DESC_FLAG_STATS) return -EINVAL; - if (!(notif.flags & ZCRX_NOTIF_DESC_FLAG_STATS)) { + if (!(notif.flags & ZCRX_EVENT_DESC_FLAG_STATS)) { if (notif.stats_offset) return -EINVAL; } @@ -970,7 +970,7 @@ int io_register_zcrx(struct io_ring_ctx *ctx, if (ret) goto err; - if (notif.flags & ZCRX_NOTIF_DESC_FLAG_STATS) { + if (notif.flags & ZCRX_EVENT_DESC_FLAG_STATS) { ret = zcrx_validate_notif_stats(ifq, ®, ¬if); if (ret) goto err; @@ -1244,7 +1244,7 @@ static netmem_ref io_pp_zc_alloc_netmems(struct page_pool *pp, gfp_t gfp) allocated = io_zcrx_refill_slow(pp, ifq, netmems, to_alloc); if (!allocated) { - zcrx_send_notif(ifq, ZCRX_NOTIF_ALLOC_FAIL); + zcrx_send_notif(ifq, ZCRX_EVENT_ALLOC_FAIL); return 0; } out_return: @@ -1398,16 +1398,16 @@ static int zcrx_flush_rq(struct io_ring_ctx *ctx, struct io_zcrx_ifq *zcrx, static int zcrx_arm_notif(struct io_ring_ctx *ctx, struct io_zcrx_ifq *zcrx, struct zcrx_ctrl *ctrl) { - const struct zcrx_ctrl_arm_notif *an = &ctrl->zc_arm_notif; + const struct zcrx_ctrl_arm_event *an = &ctrl->zc_arm_event; unsigned type_mask; - if (an->notif_type >= __ZCRX_NOTIF_TYPE_LAST) + if (an->event_type >= __ZCRX_EVENT_TYPE_LAST) return -EINVAL; if (!mem_is_zero(&an->__resv, sizeof(an->__resv))) return -EINVAL; guard(spinlock_bh)(&zcrx->ctx_lock); - type_mask = 1U << an->notif_type; + type_mask = 1U << an->event_type; if (type_mask & ~zcrx->fired_notifs) return -EINVAL; zcrx->fired_notifs &= ~type_mask; @@ -1420,7 +1420,7 @@ int io_zcrx_ctrl(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args) struct io_zcrx_ifq *zcrx; BUILD_BUG_ON(sizeof(ctrl.zc_export) != sizeof(ctrl.zc_flush)); - BUILD_BUG_ON(sizeof(ctrl.zc_export) != sizeof(ctrl.zc_arm_notif)); + BUILD_BUG_ON(sizeof(ctrl.zc_export) != sizeof(ctrl.zc_arm_event)); if (nr_args) return -EINVAL; @@ -1438,7 +1438,7 @@ int io_zcrx_ctrl(struct io_ring_ctx *ctx, void __user *arg, unsigned nr_args) return zcrx_flush_rq(ctx, zcrx, &ctrl); case ZCRX_CTRL_EXPORT: return zcrx_export(ctx, zcrx, &ctrl, arg); - case ZCRX_CTRL_ARM_NOTIFICATION: + case ZCRX_CTRL_ARM_EVENT: return zcrx_arm_notif(ctx, zcrx, &ctrl); } @@ -1584,7 +1584,7 @@ static int io_zcrx_copy_frag(struct io_kiocb *req, struct io_zcrx_ifq *ifq, zcrx_stat_add(&ifq->notif_stats->copy_count, 1); zcrx_stat_add(&ifq->notif_stats->copy_bytes, ret); } - zcrx_send_notif(ifq, ZCRX_NOTIF_COPY); + zcrx_send_notif(ifq, ZCRX_EVENT_COPY); } return ret; diff --git a/io_uring/zcrx.h b/io_uring/zcrx.h index d16206eb9e30..c1005f23caff 100644 --- a/io_uring/zcrx.h +++ b/io_uring/zcrx.h @@ -10,8 +10,9 @@ #define ZCRX_SUPPORTED_REG_FLAGS (ZCRX_REG_IMPORT | ZCRX_REG_NODEV) #define ZCRX_FEATURES (ZCRX_FEATURE_RX_PAGE_SIZE |\ - ZCRX_FEATURE_NOTIFICATION) -#define ZCRX_NOTIF_TYPE_MASK ((1U << ZCRX_NOTIF_ALLOC_FAIL) | (1U << ZCRX_NOTIF_COPY)) + ZCRX_FEATURE_EVENT) +#define ZCRX_EVENT_TYPE_MASK ((1U << ZCRX_EVENT_ALLOC_FAIL) |\ + (1U << ZCRX_EVENT_COPY)) struct io_zcrx_mem { unsigned long size; -- cgit v1.2.3 From 9545fef46d363cdcd63da63259b6d48ebb586024 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Jul 2026 13:55:55 -0700 Subject: MAINTAINERS: add nci tests to nfc NCI is part of NFC, so include its selftests under the NFC entry. Reviewed-by: David Heidelberg Link: https://patch.msgid.link/20260721205555.1020513-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index a674e36529f7..6fd196545966 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19108,6 +19108,7 @@ F: drivers/nfc/ F: include/net/nfc/ F: include/uapi/linux/nfc.h F: net/nfc/ +F: tools/testing/selftests/nci/ NFC VIRTUAL NCI DEVICE DRIVER M: Bongsu Jeon -- cgit v1.2.3 From 43171c97e4714bf601b468401b37732244639c21 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 21 Jul 2026 17:09:21 +0300 Subject: net: bridge: vlan: fix vlan range dumps starting with pvid There is a bug in all range dumps that rely on br_vlan_can_enter_range() when the PVID is a range starting VLAN, all following VLANs that match its flags can enter the range, but when the range is filled in only the PVID VLAN is dumped and the rest of the range is discarded because br_vlan_fill_vids() checks for the PVID flag. Since the PVID VLAN can be only one, we need to break ranges around it, the best way to do that consistently for all is to alter br_vlan_can_enter_range() to take into account the PVID and return false to break the range when it's matched. Before the fix: $ ip l add br0 type bridge vlan_filtering 1 $ ip l add dumdum type dummy $ ip l set dumdum master br0 $ ip l set br0 up $ ip l set dumdum up $ bridge vlan add dev dumdum vid 1 pvid untagged master $ bridge vlan add dev dumdum vid 2 untagged master $ bridge vlan show dev dumdum # use legacy dump to show all vlans port vlan-id dumdum 1 PVID Egress Untagged 2 Egress Untagged $ bridge -d vlan show dev dumdum # use the new dump (RTM_GETVLAN) port vlan-id dumdum 1 PVID Egress Untagged state forwarding mcast_router 1 VLAN 2 is missing, and if there are more matching VLANs afterwards they'd be missing too. After the fix: [ same setup steps ] $ bridge vlan show dev dumdum port vlan-id dumdum 1 PVID Egress Untagged 2 Egress Untagged $ bridge -d vlan show dev dumdum # use the new dump (RTM_GETVLAN) port vlan-id dumdum 1 PVID Egress Untagged state forwarding mcast_router 1 2 Egress Untagged state forwarding mcast_router 1 Fixes: 0ab558795184 ("net: bridge: vlan: add rtm range support") Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260721140922.682265-2-razor@blackwall.org Signed-off-by: Jakub Kicinski --- net/bridge/br_netlink_tunnel.c | 3 ++- net/bridge/br_private.h | 6 ++++-- net/bridge/br_vlan.c | 10 ++++++---- net/bridge/br_vlan_options.c | 3 +-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c index 71a12da30004..a713668ea34f 100644 --- a/net/bridge/br_netlink_tunnel.c +++ b/net/bridge/br_netlink_tunnel.c @@ -271,7 +271,8 @@ static void __vlan_tunnel_handle_range(const struct net_bridge_port *p, if (!*v_start) goto out_init; - if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) { + if (v && curr_change && + br_vlan_can_enter_range(v, *v_end, br_get_pvid(vg))) { *v_end = v; return; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index d55ea9516e3e..d3880f31edc4 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1627,7 +1627,8 @@ void br_vlan_notify(const struct net_bridge *br, u16 vid, u16 vid_range, int cmd); bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end); + const struct net_bridge_vlan *range_end, + u16 pvid); void br_vlan_fill_forward_path_pvid(struct net_bridge *br, struct net_device_path_ctx *ctx, @@ -1874,7 +1875,8 @@ static inline void br_vlan_notify(const struct net_bridge *br, } static inline bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end) + const struct net_bridge_vlan *range_end, + u16 pvid) { return true; } diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 5560afcaaca3..31c1b2cf75d9 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -1982,9 +1982,11 @@ out_kfree: /* check if v_curr can enter a range ending in range_end */ bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr, - const struct net_bridge_vlan *range_end) + const struct net_bridge_vlan *range_end, + u16 pvid) { - return v_curr->vid - range_end->vid == 1 && + return v_curr->vid != pvid && range_end->vid != pvid && + v_curr->vid - range_end->vid == 1 && range_end->flags == v_curr->flags && br_vlan_opts_eq_range(v_curr, range_end); } @@ -2066,8 +2068,8 @@ static int br_vlan_dump_dev(const struct net_device *dev, idx += range_end->vid - range_start->vid + 1; range_start = v; - } else if (dump_stats || v->vid == pvid || - !br_vlan_can_enter_range(v, range_end)) { + } else if (dump_stats || + !br_vlan_can_enter_range(v, range_end, pvid)) { u16 vlan_flags = br_vlan_flags(range_start, pvid); if (!br_vlan_fill_vids(skb, range_start->vid, diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c index fcc200c3e3da..cb0f556ff40d 100644 --- a/net/bridge/br_vlan_options.c +++ b/net/bridge/br_vlan_options.c @@ -350,8 +350,7 @@ int br_vlan_process_options(const struct net_bridge *br, continue; } - if (v->vid == pvid || - !br_vlan_can_enter_range(v, curr_end)) { + if (!br_vlan_can_enter_range(v, curr_end, pvid)) { br_vlan_notify(br, p, curr_start->vid, curr_end->vid, RTM_NEWVLAN); curr_start = v; -- cgit v1.2.3 From 679eb1e32d2cd1707de4984ca1b6e68f3d8da1ac Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 21 Jul 2026 17:09:22 +0300 Subject: selftests: net: bridge: test ranges with PVID VLAN Add a test with PVID VLAN that matches the flags of the VLAN following it and check if the range is properly dumped. PVID VLAN should be on its own and all VLANs should be present in the dump. Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260721140922.682265-3-razor@blackwall.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/bridge_vlan_dump.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/testing/selftests/net/bridge_vlan_dump.sh b/tools/testing/selftests/net/bridge_vlan_dump.sh index ad66731d2a6f..90e18e2104e3 100755 --- a/tools/testing/selftests/net/bridge_vlan_dump.sh +++ b/tools/testing/selftests/net/bridge_vlan_dump.sh @@ -13,6 +13,7 @@ ALL_TESTS=" vlan_range_mcast_max_groups vlan_range_mcast_n_groups vlan_range_mcast_enabled + vlan_range_pvid " setup_prepare() @@ -191,6 +192,28 @@ vlan_range_mcast_enabled() log_test "VLAN range grouping with mcast_enabled" } +vlan_range_pvid() +{ + RET=0 + + ip -n "$NS" link set dev br0 type bridge vlan_default_pvid 1 + check_err $? "Failed to configure default PVID" + defer ip -n "$NS" link set dev br0 type bridge vlan_default_pvid 0 + + bridge -n "$NS" vlan add vid 2 dev dummy0 untagged + check_err $? "Failed to add VLAN 2" + defer bridge -n "$NS" vlan del vid 2 dev dummy0 + + bridge -n "$NS" -d vlan show dev dummy0 | + grep -Eq '(^|[[:space:]])2([[:space:]]|$)' + check_err $? "VLAN following PVID is missing from detailed dump" + + bridge -n "$NS" -d vlan show dev dummy0 | grep -q "1-2" + check_fail $? "PVID was incorrectly included in a VLAN range" + + log_test "PVID is isolated from VLAN dump ranges" +} + # Verify the newest tested option is supported if ! bridge vlan help 2>&1 | grep -q "neigh_suppress"; then echo "SKIP: iproute2 too old, missing per-VLAN neighbor suppression support" -- cgit v1.2.3 From dcf15eaf5641812f1cfc5e96537380132a7da89d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 21 Jul 2026 10:12:40 +0000 Subject: net: hsr: fix memory leak on slave unregistration by removing synced VLANs When an HSR master device is brought UP, it auto-adds VLAN 0 via vlan_vid0_add(), which propagates VID 0 to its slave devices (slave A and B). If a slave device is later unregistered while HSR is active (e.g., during netns cleanup or interface destruction), hsr_del_port() is called to detach the slave port from the HSR master. However, hsr_del_port() currently does not delete the VLAN IDs that were synced to the slave device by HSR. As a result, the slave device retains a refcount on VID 0 (and any other synced VLANs). When the slave device is destroyed, its vlan_info / vlan_vid_info structure remains allocated, leading to a memory leak. Fix this by calling vlan_vids_del_by_dev(port->dev, master->dev) in hsr_del_port() before unlinking slave A or slave B ports, matching the propagation logic in hsr_ndo_vlan_rx_add_vid() / hsr_ndo_vlan_rx_kill_vid() and the cleanup behavior in bonding and team drivers. Fixes: 1a8a63a5305e ("net: hsr: Add VLAN CTAG filter support") Reported-by: syzbot+456957213f32970c0762@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a4cb6ca.57639fcc.86d58.000b.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Fernando Fernandez Mancera Reviewed-by: Felix Maurer Link: https://patch.msgid.link/20260721101240.995597-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/hsr/hsr_slave.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index d9af9e65f72f..01c73b4b50dd 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -242,6 +242,8 @@ void hsr_del_port(struct hsr_port *port) netdev_rx_handler_unregister(port->dev); if (!port->hsr->fwd_offloaded) dev_set_promiscuity(port->dev, -1); + if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) + vlan_vids_del_by_dev(port->dev, master->dev); netdev_upper_dev_unlink(port->dev, master->dev); if (hsr->prot_version == PRP_V1 && port->type == HSR_PT_SLAVE_B) { -- cgit v1.2.3 From db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 12 Jul 2026 16:42:01 -0700 Subject: netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in sip_help_tcp() sip_help_tcp() stores the size change of each NAT-rewritten SIP message in s16 diff and accumulates it in s16 tdiff, but a single message can grow by more than S16_MAX while the packet stays under the 65535 enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long Contact list expands the message by tens of kilobytes. diff then wraps, and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, so the next iteration's ct_sip_get_header() reads past the linearized skb tail. Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the 65535 byte packet limit, and the seqadj core is already s32 (nf_ct_seqadj_set() takes s32), so no previously accepted input is rejected. BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) nf_confirm (net/netfilter/nf_conntrack_proto.c:183) nf_hook_slow (net/netfilter/core.c:619) ip6_output (net/ipv6/ip6_output.c:246) ip6_forward (net/ipv6/ip6_output.c:690) ipv6_rcv (net/ipv6/ip6_input.c:351) __netif_receive_skb_one_core (net/core/dev.c:6212) process_backlog (net/core/dev.c:6676) __napi_poll (net/core/dev.c:7735) net_rx_action (net/core/dev.c:7955) handle_softirqs (kernel/softirq.c:622) run_ksoftirqd (kernel/softirq.c:1076) ... Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support") Reported-by: Weiming Shi Link: https://patch.msgid.link/netfilter-devel/20260712234201.3213635-1-xmei5@asu.edu Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_sip.h | 2 +- net/netfilter/nf_conntrack_sip.c | 2 +- net/netfilter/nf_nat_sip.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index dbc614dfe0d5..aafa0c04f917 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { unsigned int *datalen); void (*seq_adjust)(struct sk_buff *skb, - unsigned int protoff, s16 off); + unsigned int protoff, s32 off); unsigned int (*expect)(struct sk_buff *skb, unsigned int protoff, diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index f3f90a866338..e4a70d1d77b0 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1663,7 +1663,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, unsigned int matchoff, matchlen; unsigned int msglen, origlen; const char *dptr, *end; - s16 diff, tdiff = 0; + s32 diff, tdiff = 0; int ret = NF_ACCEPT; unsigned long clen; bool term; diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index aea02f6aff09..a93eaf0f7d30 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -321,7 +321,7 @@ next: } static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, - s16 off) + s32 off) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); -- cgit v1.2.3 From 9591fcc95dddfd24298724f8eb5239e907980779 Mon Sep 17 00:00:00 2001 From: Kuba Piecuch Date: Wed, 22 Jul 2026 14:33:07 +0000 Subject: sched_ext: Mark waker CPU busy when selected in WAKE_SYNC case SCX's built-in idle CPU tracking is imperfect and can be out-of-sync with the actual idle state of CPUs, especially immediately after enabling SCX due to scx_idle_enable() marking all online CPUs idle. scx_select_cpu_dfl() skips marking the selected CPU as busy if the selected CPU is the waker CPU in the SCX_WAKE_SYNC case. If the waker CPU was marked idle by SCX, it will still be marked idle after CPU selection and potentially even after switching to the wakee. In the allowed_cpus selftest, this can manifest as the test failing with the following message in dmesg: allowed_cpus.bpf.c:21: CPU 0 should be marked as busy This patch explicitly marks the waker CPU as busy. With this patch, the test failure no longer reproduces. There are still some pretty unlikely races that could make the test fail (e.g. pick_task_idle() marking the selected CPU idle between selection and validation), but these can't be fixed easily. Signed-off-by: Kuba Piecuch Signed-off-by: Tejun Heo --- kernel/sched/ext/idle.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c index 8e8c6201b7df..6f93cc32b650 100644 --- a/kernel/sched/ext/idle.c +++ b/kernel/sched/ext/idle.c @@ -554,8 +554,10 @@ s32 scx_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags, cpu_rq(cpu)->scx.local_dsq.nr == 0 && (!(flags & SCX_PICK_IDLE_IN_NODE) || (waker_node == node)) && !cpumask_empty(idle_cpumask(waker_node)->cpu)) { - if (cpumask_test_cpu(cpu, allowed)) + if (cpumask_test_cpu(cpu, allowed)) { + scx_idle_test_and_clear_cpu(cpu); goto out_unlock; + } } } -- cgit v1.2.3 From 1d6123f87eebb5148844cd43045c6e598799720b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 13 Jul 2026 14:53:22 +0200 Subject: selftests: netfilter: nft_flowtable.sh: fix offload counter verification for tunnel tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IPIP and IP6IP6 tunnel tests call check_counters() to verify flowtable offloading occurred, but the flow-add rule only matches meta oif "veth1". When traffic is routed through a tunnel device, oif is the tunnel interface (tun0, tun6, etc.), not veth1, so the flow-add rule never fires, no flowtable entry is created, and counters stay at zero — producing a silent false pass. Fix by adding tunnel-specific flow-add rules for each tunnel interface. These match TCP dport 12345 traffic before the bare accept rule, set ct mark, add the flow to the flowtable, and increment routed_orig. The existing routed_repl rule on veth0 already handles the reply direction since decapsulated reply packets exit through the physical interface. Also add check_counters() for the IP6IP6 non-VLAN and IP6IP6-over-VLAN tests which previously used a bare PASS message. Fixes: fe8313316eaf ("selftests: netfilter: nft_flowtable.sh: Add IPIP flowtable selftest") Fixes: 5e5180352193 ("selftests: netfilter: nft_flowtable.sh: Add IP6IP6 flowtable selftest") Signed-off-by: Lorenzo Bianconi Signed-off-by: Pablo Neira Ayuso --- tools/testing/selftests/net/netfilter/nft_flowtable.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh index fb1c59d45567..449c518bd947 100755 --- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh +++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh @@ -617,7 +617,11 @@ 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 tun0 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 tun6 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' @@ -629,7 +633,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel"; then fi if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then - echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel" + check_counters "flow offload for ns1/ns2 IP6IP6 tunnel" else echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel" 1>&2 ip netns exec "$nsr1" nft list ruleset @@ -642,6 +646,8 @@ ip -net "$nsr1" link set veth1.10 up ip -net "$nsr1" addr add 192.168.20.1/24 dev veth1.10 ip -net "$nsr1" addr add fee1:4::1/64 dev veth1.10 nodad ip netns exec "$nsr1" sysctl net.ipv4.conf.veth1/10.forwarding=1 > /dev/null +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif veth1.10 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 veth1.10 accept' ip -net "$nsr1" link add name tun0.10 type ipip local 192.168.20.1 remote 192.168.20.2 @@ -649,6 +655,8 @@ ip -net "$nsr1" link set tun0.10 up ip -net "$nsr1" addr add 192.168.200.1/24 dev tun0.10 ip -net "$nsr1" route change default via 192.168.200.2 ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun0.10 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 tun0.10 accept' ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none @@ -656,6 +664,8 @@ ip -net "$nsr1" link set tun6.10 up ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad ip -6 -net "$nsr1" route delete default ip -6 -net "$nsr1" route add default via fee1:5::2 +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun6.10 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.10 accept' ip -net "$nsr2" link add link veth0 name veth0.10 type vlan id 10 @@ -683,7 +693,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel over vlan"; then fi if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then - echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel over vlan" + check_counters "flow offload for ns1/ns2 IP6IP6 tunnel over vlan" else echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel over vlan" 1>&2 ip netns exec "$nsr1" nft list ruleset -- cgit v1.2.3 From 4aa63842fc92de1bce59d4709a0d32e718890bb2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 13 Jul 2026 00:26:04 +0200 Subject: netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair() Add a new function to insert a pair of expectations, this is required by the SIP and H323 NAT helpers. The spinlock is held to check if there is a slot for both expectations, in such case, insert them. This removes the need for nf_ct_unexpect_related() inside the loop to find a pair of consecutive ports, otherwise inserting expectations whose dead flag is already set on can happen. Bump master_help->expecting for the expectation class after checking if the expectation fits in the master expectation list, which is needed for this new _pair() function variant to run the eviction routine including the preallocated slot for the first expectation in the pair. Fixes: b8b09dc2bf35 ("netfilter: nf_conntrack_expect: use conntrack GC to reap expectations") Reported-by: Jaeyeong Lee Link: https://patch.msgid.link/178377968720.33756.12204817361601593230@proton.me/ Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_expect.h | 3 +++ net/ipv4/netfilter/nf_nat_h323.c | 22 ++++++------------ net/netfilter/nf_conntrack_expect.c | 35 ++++++++++++++++++++++++++++- net/netfilter/nf_nat_sip.c | 20 +++++------------ 4 files changed, 50 insertions(+), 30 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index c024345c9bd8..26d6babd92fc 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -161,6 +161,9 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect, return nf_ct_expect_related_report(expect, 0, 0, flags); } +int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[], + unsigned int flag); + struct nf_conn_help; void nf_ct_expectation_gc(struct nf_conn_help *master_help); diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index 183e8a3ff2ba..6bcd6734769b 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -182,6 +182,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp) { + struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp }; struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); int i; @@ -227,22 +228,13 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, int ret; rtp_exp->tuple.dst.u.udp.port = htons(nated_port); - ret = nf_ct_expect_related(rtp_exp, 0); + rtcp_exp->tuple.dst.u.udp.port = htons(nated_port + 1); + ret = nf_ct_expect_related_pair(rtp_pair, 0); if (ret == 0) { - rtcp_exp->tuple.dst.u.udp.port = - htons(nated_port + 1); - ret = nf_ct_expect_related(rtcp_exp, 0); - if (ret == 0) - break; - else if (ret == -EBUSY) { - nf_ct_unexpect_related(rtp_exp); - continue; - } else if (ret < 0) { - nf_ct_unexpect_related(rtp_exp); - nated_port = 0; - break; - } - } else if (ret != -EBUSY) { + break; + } else if (ret == -EBUSY) { + continue; + } else if (ret < 0) { nated_port = 0; break; } diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 7ae68d60586a..8a3b9e33e94f 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -427,7 +427,6 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, exp->timeout += helper->expect_policy[exp->class].timeout * HZ; hlist_add_head_rcu(&exp->lnode, &master_help->expectations); - master_help->expecting[exp->class]++; hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]); cnet = nf_ct_pernet(net); @@ -534,6 +533,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, if (ret < 0) goto out; + master_help->expecting[expect->class]++; nf_ct_expect_insert(expect, master_help); nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); @@ -546,6 +546,39 @@ out: } EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); +int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[], + unsigned int flags) +{ + struct nf_conn_help *master_help; + int i, ret; + + spin_lock_bh(&nf_conntrack_expect_lock); + master_help = nfct_help(expect[0]->master); + if (!master_help || master_help != nfct_help(expect[1]->master)) { + ret = -EINVAL; + goto out; + } + + for (i = 0; i < 2; i++) { + ret = __nf_ct_expect_check(expect[i], master_help, flags); + if (ret < 0) { + if (i == 1) + master_help->expecting[expect[0]->class]--; + goto out; + } + master_help->expecting[expect[i]->class]++; + } + + for (i = 0; i < 2; i++) { + nf_ct_expect_insert(expect[i], master_help); + nf_ct_expect_event_report(IPEXP_NEW, expect[i], 0, 0); + } +out: + spin_unlock_bh(&nf_conntrack_expect_lock); + return ret; +} +EXPORT_SYMBOL_GPL(nf_ct_expect_related_pair); + void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data), void *data) { diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index a93eaf0f7d30..133bd713fe0c 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -592,6 +592,7 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, unsigned int medialen, union nf_inet_addr *rtp_addr) { + struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp }; enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); @@ -622,24 +623,15 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, int ret; rtp_exp->tuple.dst.u.udp.port = htons(port); - ret = nf_ct_expect_related(rtp_exp, - NF_CT_EXP_F_SKIP_MASTER); - if (ret == -EBUSY) - continue; - else if (ret < 0) { - port = 0; - break; - } rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); - ret = nf_ct_expect_related(rtcp_exp, - NF_CT_EXP_F_SKIP_MASTER); + + ret = nf_ct_expect_related_pair(rtp_pair, + NF_CT_EXP_F_SKIP_MASTER); if (ret == 0) break; - else if (ret == -EBUSY) { - nf_ct_unexpect_related(rtp_exp); + else if (ret == -EBUSY) continue; - } else if (ret < 0) { - nf_ct_unexpect_related(rtp_exp); + else if (ret < 0) { port = 0; break; } -- cgit v1.2.3 From 6842427bf2990271c771081f11fd8fab17f86c82 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 15 Jul 2026 12:43:18 -0500 Subject: cpufreq/amd-pstate: Loosen requirement on lowest nonlinear frequency != min freq This requirement was introduced by commit 8f8b42c1fcc93 ("cpufreq: amd-pstate: optimize the initial frequency values verification") specifically to aid in debugging BIOS issues with invalid _CPC tables on some older systems. This requirement is too tight for new systems though as some systems actually have lowest nonlinear frequency identical to minimum frequency. Allow that combo to work. Signed-off-by: Mario Limonciello Reviewed-by: K Prateek Nayak Tested-by: K Prateek Nayak Link: https://lore.kernel.org/r/20260715174318.18235-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello --- drivers/cpufreq/amd-pstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index a74a4cf99d22..3d72337a3336 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1031,7 +1031,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata) return -EINVAL; } - if (lowest_nonlinear_freq <= min_freq || lowest_nonlinear_freq > nominal_freq) { + if (lowest_nonlinear_freq < min_freq || lowest_nonlinear_freq > nominal_freq) { pr_err("lowest_nonlinear_freq(%d) value is out of range [min_freq(%d), nominal_freq(%d)]\n", lowest_nonlinear_freq, min_freq, nominal_freq); return -EINVAL; -- cgit v1.2.3 From 08fc1e7b31f8832e356ede8afae1ad1ff0d5a1fc Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Wed, 22 Jul 2026 02:13:43 +0800 Subject: cpufreq/amd-pstate: Prevent the driver from loading on unsupported hardware X86_FEATURE_HW_PSTATE indicates if the processor supports frequency scaling or not. Without it, the driver is unusable and thus will not load. This check also prevents the driver from loading in guests and thus not confuse users with misleading prints. Reviewed-by: Michael Kelley Tested-by: Michael Kelley Acked-by: Mario Limonciello (AMD) Reviewed-by: K Prateek Nayak Tested-by: K Prateek Nayak Acked-by: Borislav Petkov (AMD) Signed-off-by: Rong Zhang Link: https://lore.kernel.org/r/20260722-amd-pstate-vm-v4-1-d6607d9e9d9a@rong.moe Signed-off-by: Mario Limonciello --- drivers/cpufreq/amd-pstate.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 3d72337a3336..3a6b4b224a66 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -2167,6 +2167,7 @@ static struct cpufreq_driver amd_pstate_epp_driver = { }; /* + * Processors without frequency scaling support can't do CPPC. * CPPC function is not supported for family ID 17H with model_ID ranging from 0x10 to 0x2F. * show the debug message that helps to check if the CPU has CPPC support for loading issue. */ @@ -2175,6 +2176,11 @@ static bool amd_cppc_supported(void) struct cpuinfo_x86 *c = &cpu_data(0); bool warn = false; + if (!cpu_feature_enabled(X86_FEATURE_HW_PSTATE)) { + pr_debug_once("frequency scaling is not supported by the processor\n"); + return false; + } + if ((boot_cpu_data.x86 == 0x17) && (boot_cpu_data.x86_model < 0x30)) { pr_debug_once("CPPC feature is not supported by the processor\n"); return false; -- cgit v1.2.3 From c83e79c0842ed29860648bcce5022ef0ba5001c6 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 20 Jul 2026 14:12:59 +0800 Subject: Input: byd - synchronize timer deletion before freeing private data byd_disconnect() uses timer_delete() before freeing the driver's private data. This does not wait for a running byd_clear_touch() callback, which dereferences the private data and its psmouse pointer. A callback racing with disconnect can therefore access the private data after it has been freed. The timer can also still be re-armed by byd_process_byte() while the disconnect is in progress. Use timer_shutdown_sync() before freeing the private data: it waits for a running callback and turns any later re-arm attempt into a no-op. Fixes: 2d5f5611dd0d ("Input: byd - enable absolute mode") Cc: stable@vger.kernel.org Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260720061259.1601281-1-lilinmao@kylinos.cn Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/byd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/byd.c b/drivers/input/mouse/byd.c index f5770a3af2f1..5fc3c629590a 100644 --- a/drivers/input/mouse/byd.c +++ b/drivers/input/mouse/byd.c @@ -423,7 +423,7 @@ static void byd_disconnect(struct psmouse *psmouse) struct byd_data *priv = psmouse->private; if (priv) { - timer_delete(&priv->timer); + timer_shutdown_sync(&priv->timer); kfree(psmouse->private); psmouse->private = NULL; } -- cgit v1.2.3 From c58ea9adf7342508c6ac0b7ad79ef10d589f9c6e Mon Sep 17 00:00:00 2001 From: Chuyi Zhou Date: Wed, 15 Jul 2026 17:45:38 -0700 Subject: smp: Avoid invalid per-CPU CSD lookup with CSD lock debug Commit b0473dcd4b1d ("smp: Improve smp_call_function_single() CSD-lock diagnostics") made smp_call_function_single() use the destination CPU's csd_data when CSD lock debugging is enabled. That lets the debug code associate a stuck CSD lock with the target CPU, but it also means the CPU argument is used in per_cpu_ptr() before generic_exec_single() has a chance to validate it. This becomes unsafe when smp_call_function_any() cannot find an online CPU in the supplied mask. In that case the selected CPU can be nr_cpu_ids, and the !wait path calls get_single_csd_data(cpu) before generic_exec_single() returns -ENXIO. With csdlock_debug_enabled set, that indexes the per-CPU offset array with an invalid CPU number. Use the destination CPU's csd_data only when the CPU number is within nr_cpu_ids. For invalid CPU numbers, fall back to the local CPU's csd_data and let generic_exec_single() perform the existing validation and return -ENXIO. Fixes: b0473dcd4b1d ("smp: Improve smp_call_function_single() CSD-lock diagnostics") Signed-off-by: Chuyi Zhou Signed-off-by: Paul E. McKenney Signed-off-by: Thomas Gleixner Reviewed-by: Paul E. McKenney Acked-by: Muchun Song Link: https://patch.msgid.link/20260716004539.13983-1-paulmck@kernel.org --- kernel/smp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/smp.c b/kernel/smp.c index a0bb56bd8dda..dc6582bb35d0 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -380,7 +380,8 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(call_single_data_t, csd_data); #ifdef CONFIG_CSD_LOCK_WAIT_DEBUG static call_single_data_t *get_single_csd_data(int cpu) { - if (static_branch_unlikely(&csdlock_debug_enabled)) + if (static_branch_unlikely(&csdlock_debug_enabled) && + (unsigned int)cpu < nr_cpu_ids) return per_cpu_ptr(&csd_data, cpu); return this_cpu_ptr(&csd_data); } -- cgit v1.2.3 From 35551efb155e3b83445a6c3f66cb498d5efc182c Mon Sep 17 00:00:00 2001 From: Chuyi Zhou Date: Wed, 15 Jul 2026 17:45:39 -0700 Subject: smp: Make CSD lock acquisition atomic for debug mode Commit b0473dcd4b1d ("smp: Improve smp_call_function_single() CSD-lock diagnostics") changed smp_call_function_single() so that, when CSD lock debugging is enabled, async !wait calls use the destination CPU csd_data. That improves diagnostics, but it also removes the single-writer property that made the old csd_lock() safe: multiple CPUs can now prepare the same destination CPU CSD concurrently. csd_lock() currently waits for CSD_FLAG_LOCK to clear and then sets the bit with a non-atomic read-modify-write. Two senders can both see an unlocked CSD, set the bit, overwrite the callback fields, and enqueue the same llist node. Re-adding a node that is already the queue head can make node->next point to itself, leaving the target CPU stuck walking call_single_queue. Later synchronous work, such as a TLB shootdown, can then remain queued and trigger soft-lockup warnings or panics. Keep the single csd_lock() implementation, but when CSD lock debugging is enabled, acquire CSD_FLAG_LOCK with try_cmpxchg_acquire(). This makes the destination CPU CSD a real atomic lock in the only configuration where it can be shared by multiple remote senders, while preserving the existing non-debug fast path. Fixes: b0473dcd4b1d ("smp: Improve smp_call_function_single() CSD-lock diagnostics") Signed-off-by: Chuyi Zhou Signed-off-by: Paul E. McKenney Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260716004539.13983-2-paulmck@kernel.org --- kernel/smp.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index dc6582bb35d0..52dffc86555c 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -137,10 +137,10 @@ csd_do_func(smp_call_func_t func, void *info, call_single_data_t *csd) trace_csd_function_exit(func, csd); } -#ifdef CONFIG_CSD_LOCK_WAIT_DEBUG - static DEFINE_STATIC_KEY_MAYBE(CONFIG_CSD_LOCK_WAIT_DEBUG_DEFAULT, csdlock_debug_enabled); +#ifdef CONFIG_CSD_LOCK_WAIT_DEBUG + /* * Parse the csdlock_debug= kernel boot parameter. * @@ -342,6 +342,10 @@ static __always_inline void csd_lock_wait(call_single_data_t *csd) smp_cond_load_acquire(&csd->node.u_flags, !(VAL & CSD_FLAG_LOCK)); } #else +static __always_inline void __csd_lock_wait(call_single_data_t *csd) +{ +} + static void csd_lock_record(call_single_data_t *csd) { } @@ -354,8 +358,23 @@ static __always_inline void csd_lock_wait(call_single_data_t *csd) static __always_inline void csd_lock(call_single_data_t *csd) { - csd_lock_wait(csd); - csd->node.u_flags |= CSD_FLAG_LOCK; + if (IS_ENABLED(CONFIG_CSD_LOCK_WAIT_DEBUG) && + static_branch_unlikely(&csdlock_debug_enabled)) { + + for (;;) { + unsigned int flags; + + __csd_lock_wait(csd); + flags = READ_ONCE(csd->node.u_flags); + + if (!(flags & CSD_FLAG_LOCK) && + try_cmpxchg_acquire(&csd->node.u_flags, &flags, flags | CSD_FLAG_LOCK)) + break; + } + } else { + csd_lock_wait(csd); + csd->node.u_flags |= CSD_FLAG_LOCK; + } /* * prevent CPU from reordering the above assignment -- cgit v1.2.3 From da2c6bcc5e30b1496ac587785dcacf6e849eb6ef Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 17 Jul 2026 15:20:29 +0200 Subject: net: dpaa: fix mode setting Before converting to the phylink interface, the init function would have set a non-reserved I/F mode in the maccfg2 register. After converting to phylink, 0 is written as mode, which is a reserved value (although it's the hardware default). Without a valid mode, a SGMII link is never established between the MAC and the PHY and thus .link_up() is never called which could set the correct mode according to the actual speed. Fix it by setting the maximum speed of the phy_interface_t in use in .mac_config() - just like the driver did before the phylink conversion. Fixes: 5d93cfcf7360 ("net: dpaa: Convert to phylink") Suggested-by: Sean Anderson Signed-off-by: Michael Walle Reviewed-by: Sean Anderson Reviewed-by: Sean Anderson Link: https://patch.msgid.link/20260717132401.2653252-1-mwalle@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/fman/fman_dtsec.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index fe35703c509e..b8d70c0ecb6c 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -900,22 +900,28 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode, { struct mac_device *mac_dev = fman_config_to_mac(config); struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs; - u32 tmp; + u32 ecntrl, maccfg2; + + maccfg2 = ioread32be(®s->maccfg2); + maccfg2 &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE); switch (state->interface) { case PHY_INTERFACE_MODE_RMII: - tmp = DTSEC_ECNTRL_RMM; + ecntrl = DTSEC_ECNTRL_RMM; + maccfg2 |= MACCFG2_NIBBLE_MODE; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: - tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM; + ecntrl = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM; + maccfg2 |= MACCFG2_BYTE_MODE; break; case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: - tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM; + ecntrl = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM; + maccfg2 |= MACCFG2_BYTE_MODE; break; default: dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n", @@ -923,7 +929,8 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode, return; } - iowrite32be(tmp, ®s->ecntrl); + iowrite32be(ecntrl, ®s->ecntrl); + iowrite32be(maccfg2, ®s->maccfg2); } static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy, -- cgit v1.2.3 From 59a57128ae5231f9aa9d544fa9d3e38986f0efaa Mon Sep 17 00:00:00 2001 From: Luis Lang Date: Mon, 20 Jul 2026 13:15:29 +0200 Subject: net: stmmac: dwmac4: mask interrupts when stopping DMA in suspend Since commit 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts"), suspending causes an interrupt storm from the RPS interrupt. Fix this by adding a deinit_chan() op to stmmac_dma_ops, which masks all default dma channel interrupts. This is called from stmmac_stop_all_dma(), so interrupts don't trigger while suspending. Fixes: 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts") Suggested-by: Andrew Lunn Suggested-by: Maxime Chevallier Signed-off-by: Luis Lang Reviewed-by: Andrew Lunn Tested-by: Maxime Chevallier Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260720111534.163416-1-luis.la@mail.de Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 24 +++++++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/hwif.h | 4 ++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++ 3 files changed, 32 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 829a23bdad01..23ffe1adcd0d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -106,6 +106,17 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); } +static void dwmac4_dma_deinit_channel(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; + u32 value; + + value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + value &= ~DMA_CHAN_INTR_DEFAULT_MASK; + writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); +} + static void dwmac410_dma_init_channel(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, u32 chan) @@ -125,6 +136,17 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); } +static void dwmac410_dma_deinit_channel(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; + u32 value; + + value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); + value &= ~DMA_CHAN_INTR_DEFAULT_MASK_4_10; + writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); +} + static void dwmac4_dma_init(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg) { @@ -548,6 +570,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = { .reset = dwmac4_dma_reset, .init = dwmac4_dma_init, .init_chan = dwmac4_dma_init_channel, + .deinit_chan = dwmac4_dma_deinit_channel, .init_rx_chan = dwmac4_dma_init_rx_chan, .init_tx_chan = dwmac4_dma_init_tx_chan, .axi = dwmac4_dma_axi, @@ -577,6 +600,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = { .reset = dwmac4_dma_reset, .init = dwmac4_dma_init, .init_chan = dwmac410_dma_init_channel, + .deinit_chan = dwmac410_dma_deinit_channel, .init_rx_chan = dwmac4_dma_init_rx_chan, .init_tx_chan = dwmac4_dma_init_tx_chan, .axi = dwmac4_dma_axi, diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index e6317b94fff7..04dafec021b4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -170,6 +170,8 @@ struct stmmac_dma_ops { void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg); void (*init_chan)(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, u32 chan); + void (*deinit_chan)(struct stmmac_priv *priv, void __iomem *ioaddr, + u32 chan); void (*init_rx_chan)(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, dma_addr_t phy, u32 chan); @@ -235,6 +237,8 @@ struct stmmac_dma_ops { stmmac_do_void_callback(__priv, dma, init, __args) #define stmmac_init_chan(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, init_chan, __priv, __args) +#define stmmac_deinit_chan(__priv, __args...) \ + stmmac_do_void_callback(__priv, dma, deinit_chan, __priv, __args) #define stmmac_init_rx_chan(__priv, __args...) \ stmmac_do_void_callback(__priv, dma, init_rx_chan, __priv, __args) #define stmmac_init_tx_chan(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 2a0d7eff88d3..af29a50ddb89 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2560,6 +2560,7 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv) { u8 rx_channels_count = priv->plat->rx_queues_to_use; u8 tx_channels_count = priv->plat->tx_queues_to_use; + u8 dma_csr_ch = max(rx_channels_count, tx_channels_count); u8 chan; for (chan = 0; chan < rx_channels_count; chan++) @@ -2567,6 +2568,9 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv) for (chan = 0; chan < tx_channels_count; chan++) stmmac_stop_tx_dma(priv, chan); + + for (chan = 0; chan < dma_csr_ch; chan++) + stmmac_deinit_chan(priv, priv->ioaddr, chan); } /** -- cgit v1.2.3 From dcd9b465965422b9654f6026e8a2fa8984f74c3c Mon Sep 17 00:00:00 2001 From: James Raphael Tiovalen Date: Tue, 21 Jul 2026 00:04:24 +0800 Subject: vxlan: mdb: Fix source list corruption on a failed replace When replacing the source list of an MDB remote entry, all existing sources are first marked for deletion and vxlan_mdb_remote_srcs_add() is then called to add the new source list. Sources present in the new list have their deletion mark cleared, and any sources left marked afterwards are removed. If vxlan_mdb_remote_srcs_add() fails partway through, its error path deletes all entries on the remote's source list. That rollback is only correct for its other caller, vxlan_mdb_remote_add(), where the remote was just allocated and the list contains solely entries added during the call. On the replace path the list also holds pre-existing sources, so a failed replace tears them down together with their (S, G) forwarding entries instead of leaving the entry unchanged. This is reachable from an existing (*, G) remote. An EXCLUDE filter that loses sources starts forwarding traffic that should be blocked, while an INCLUDE filter that loses sources drops traffic that should be forwarded. Mark entries created during the current pass with a new VXLAN_SGRP_F_NEW flag. On failure, delete only those entries and clear the deletion mark on the pre-existing ones, so a failed replace leaves the source list untouched. Retain the flag until the whole operation succeeds and then clear it. Also stop vxlan_mdb_remote_src_add() from deleting a pre-existing entry it only looked up when adding that entry's forwarding entry fails. Fixes: a3a48de5eade ("vxlan: mdb: Add MDB control path support") Cc: stable@vger.kernel.org Signed-off-by: James Raphael Tiovalen Reviewed-by: Ido Schimmel Reviewed-by: Antoine Tenart Reviewed-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260720160428.249356-1-jamestiotio@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_mdb.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 055a4969f593..af7a0d7f95a5 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -42,6 +42,7 @@ struct vxlan_mdb_remote { }; #define VXLAN_SGRP_F_DELETE BIT(0) +#define VXLAN_SGRP_F_NEW BIT(1) struct vxlan_mdb_src_entry { struct hlist_node node; @@ -844,6 +845,7 @@ vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg, ent = vxlan_mdb_remote_src_entry_add(remote, &src->addr); if (!ent) return -ENOMEM; + ent->flags |= VXLAN_SGRP_F_NEW; } else if (!(cfg->nlflags & NLM_F_REPLACE)) { NL_SET_ERR_MSG_MOD(extack, "Source entry already exists"); return -EEXIST; @@ -853,15 +855,16 @@ vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg, if (err) goto err_src_del; - /* Clear flags in case source entry was marked for deletion as part of - * replace flow. + /* Clear the deletion mark so the entry survives the replace sweep. + * The new mark is retained until the whole operation succeeds. */ - ent->flags = 0; + ent->flags &= ~VXLAN_SGRP_F_DELETE; return 0; err_src_del: - vxlan_mdb_remote_src_entry_del(ent); + if (ent->flags & VXLAN_SGRP_F_NEW) + vxlan_mdb_remote_src_entry_del(ent); return err; } @@ -889,11 +892,19 @@ static int vxlan_mdb_remote_srcs_add(const struct vxlan_mdb_config *cfg, goto err_src_del; } + hlist_for_each_entry(ent, &remote->src_list, node) + ent->flags &= ~VXLAN_SGRP_F_NEW; + return 0; err_src_del: - hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) - vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, ent); + hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) { + if (ent->flags & VXLAN_SGRP_F_NEW) + vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, + ent); + else + ent->flags &= ~VXLAN_SGRP_F_DELETE; + } return err; } @@ -1069,7 +1080,7 @@ vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg, err = vxlan_mdb_remote_srcs_add(cfg, remote, extack); if (err) - goto err_clear_delete; + return err; hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) { if (ent->flags & VXLAN_SGRP_F_DELETE) @@ -1078,11 +1089,6 @@ vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg, } return 0; - -err_clear_delete: - hlist_for_each_entry(ent, &remote->src_list, node) - ent->flags &= ~VXLAN_SGRP_F_DELETE; - return err; } static int vxlan_mdb_remote_replace(const struct vxlan_mdb_config *cfg, -- cgit v1.2.3 From 8e04823c120b376ef7dab14b60ebf6823aa16c14 Mon Sep 17 00:00:00 2001 From: Qing Luo Date: Tue, 21 Jul 2026 09:55:32 +0800 Subject: sctp: auth: verify auth requirement when auth_chunk is NULL sctp_auth_chunk_verify() returns true unconditionally when chunk->auth_chunk is NULL, silently skipping authentication. This is incorrect when: 1. skb_clone() failed in the BH receive path, leaving auth_chunk NULL. In sctp_endpoint_bh_rcv() asoc is NULL for new connections, so the early sctp_auth_recv_cid() check cannot catch this. 2. No AUTH chunk precedes COOKIE-ECHO, so skb_clone() is never called and auth_chunk remains NULL. Fix by checking sctp_auth_recv_cid() when auth_chunk is NULL: if authentication is required, return false to drop the chunk; otherwise continue normally. Fixes: bbd0d59809f9 ("[SCTP]: Implement the receive and verification of AUTH chunk") Signed-off-by: Qing Luo Acked-by: Xin Long Link: https://patch.msgid.link/20260721015532.120157-2-l1138897701@163.com Signed-off-by: Jakub Kicinski --- net/sctp/sm_statefuns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 3893b44448b3..708fa07d5fff 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -643,7 +643,7 @@ static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk, struct sctp_chunk auth; if (!chunk->auth_chunk) - return true; + return !sctp_auth_recv_cid(chunk->chunk_hdr->type, asoc); /* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo * is supposed to be authenticated and we have to do delayed -- cgit v1.2.3 From 9b5ce5c496efd20c1c662cedba88465d39ec1f93 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 25 Jun 2026 08:32:40 +0800 Subject: drm/gma500: return errors from Oaktrail HDMI I2C reads xfer_read() waits for the HDMI I2C transaction to reach I2C_TRANSACTION_DONE, but it ignores both timeout and signal returns from wait_for_completion_interruptible_timeout(). If the interrupt never advances the transaction state, the loop can wait forever. Return -ETIMEDOUT when the completion wait expires, propagate interrupted waits, and make the I2C master_xfer callback return the first transfer error instead of reporting a successful message count. Signed-off-by: Pengpeng Hou Signed-off-by: Patrik Jakobsson Link: https://patch.msgid.link/20260625003240.6923-1-pengpeng@iscas.ac.cn --- drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c index 2a7916ca5907..a780643f25aa 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c @@ -98,6 +98,7 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; u32 temp; + int ret; i2c_dev->status = I2C_STAT_INIT; i2c_dev->msg = pmsg; @@ -109,9 +110,14 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) HDMI_WRITE(HDMI_HI2CHCR, temp); HDMI_READ(HDMI_HI2CHCR); - while (i2c_dev->status != I2C_TRANSACTION_DONE) - wait_for_completion_interruptible_timeout(&i2c_dev->complete, + while (i2c_dev->status != I2C_TRANSACTION_DONE) { + ret = wait_for_completion_interruptible_timeout(&i2c_dev->complete, 10 * HZ); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + } return 0; } @@ -130,7 +136,7 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, { struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - int i; + int i, ret = 0; mutex_lock(&i2c_dev->i2c_lock); @@ -142,9 +148,11 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, for (i = 0; i < num; i++) { if (pmsg->len && pmsg->buf) { if (pmsg->flags & I2C_M_RD) - xfer_read(adap, pmsg); + ret = xfer_read(adap, pmsg); else - xfer_write(adap, pmsg); + ret = xfer_write(adap, pmsg); + if (ret) + break; } pmsg++; /* next message */ } @@ -154,6 +162,9 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, mutex_unlock(&i2c_dev->i2c_lock); + if (ret) + return ret; + return i; } -- cgit v1.2.3 From 34a71f5361fc3adb5b7138da78750b0d535a8252 Mon Sep 17 00:00:00 2001 From: Harshaka Narayana Date: Mon, 13 Jul 2026 07:09:15 -0700 Subject: vmxnet3: fix BUG_ON in vmxnet3_get_hdr_len() for Geneve packets vmxnet3_get_hdr_len() assumes gdesc->rcd.v4/v6/tcp always describe the outer header, but for a Geneve-encapsulated packet the device can set them based on the inner header instead, signalled by the VMXNET3_RCD_HDR_INNER_SHIFT bit in the completion descriptor. Since the function never skips the outer encapsulation, this mismatch triggers: - BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP), because the outer protocol is UDP (Geneve), not TCP. - BUG_ON(hdr.eth->h_proto != ...), when the tunnel's outer and inner IP versions differ (e.g. outer IPv6/inner IPv4 or vice versa). Check VMXNET3_RCD_HDR_INNER_SHIFT up front and bail out, since the function cannot locate the inner header it would need to parse. Also convert the remaining BUG_ON()s in this function to return 0 defensively. Fixes: 45dac1d6ea04 ("vmxnet3: Changes for vmxnet3 adapter version 2 (fwd)") Signed-off-by: Harshaka Narayana Reviewed-by: Ronak Doshi Reviewed-by: Sankararaman Jayaraman Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260713140915.3381715-1-harshaka.narayana@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/vmxnet3/vmxnet3_drv.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 40522afc0532..f8df83f9965d 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -1530,7 +1530,11 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, struct ipv6hdr *ipv6; struct tcphdr *tcp; } hdr; - BUG_ON(gdesc->rcd.tcp == 0); + + /* v4/v6/tcp then describe the inner header, which we can't locate. */ + if ((le32_to_cpu(gdesc->dword[0]) & (1UL << VMXNET3_RCD_HDR_INNER_SHIFT)) || + gdesc->rcd.tcp == 0) + return 0; maplen = skb_headlen(skb); if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen)) @@ -1544,15 +1548,21 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb, hdr.eth = eth_hdr(skb); if (gdesc->rcd.v4) { - BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP) && - hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP)); + if (hdr.eth->h_proto != htons(ETH_P_IP) && + hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP)) + return 0; + hdr.ptr += hlen; - BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP); + if (hdr.ipv4->protocol != IPPROTO_TCP) + return 0; + hlen = hdr.ipv4->ihl << 2; hdr.ptr += hdr.ipv4->ihl << 2; } else if (gdesc->rcd.v6) { - BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6) && - hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6)); + if (hdr.eth->h_proto != htons(ETH_P_IPV6) && + hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6)) + return 0; + hdr.ptr += hlen; /* Use an estimated value, since we also need to handle * TSO case. -- cgit v1.2.3 From 94b83ff0c0a69e42f403b59918529fbca2a89daf Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Mon, 13 Jul 2026 08:28:23 +0100 Subject: tracing/remotes: Fix page_va[] access before counter update in trace_remote_alloc_buffer() page_va[] is annotated __counted_by(nr_page_va), so nr_page_va must cover an index before that element is accessed. The allocation loop writes page_va[id] while nr_page_va is still id and increments it only afterwards, so every write is one element past the declared count. The store is out of bounds with respect to the annotation: a build with CONFIG_UBSAN_BOUNDS on a toolchain that honours __counted_by (clang >= 20.1, gcc >= 15.1) flags it as an array-index overflow. Increment nr_page_va before writing the element it now covers. A failed allocation then leaves the slot counted but NULL; the error path frees it with free_page(0), which is a no-op. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260713072823.2668323-1-fuad.tabba@linux.dev Fixes: 96e43537af546 ("tracing: Introduce trace remotes") Signed-off-by: Fuad Tabba Reviewed-by: Vincent Donnefort Tested-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/trace_remote.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index 0f6ef5c36d84..ef42d9c38b37 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -1004,11 +1004,10 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, desc->nr_cpus++; for (id = 0; id < nr_pages; id++) { + rb_desc->nr_page_va++; rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL); if (!rb_desc->page_va[id]) goto err; - - rb_desc->nr_page_va++; } rb_desc = __next_ring_buffer_desc(rb_desc); } -- cgit v1.2.3 From 92d3817649df2b0b6a008a686c8275c88d7ef594 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 14 Jul 2026 07:49:03 -0400 Subject: ila: reload IPv6 header after pskb_may_pull in checksum adjust ila_csum_adjust_transport() caches ip6h = ipv6_hdr(skb) before calling pskb_may_pull(). On a non-linear skb whose transport header sits in a page fragment, pskb_may_pull() can call __pskb_pull_tail() / pskb_expand_head() and free the old skb head, leaving ip6h dangling; the following get_csum_diff(ip6h, p) then reads freed memory. ila_update_ipv6_locator() uses ip6h (and the iaddr derived from it) again after the csum-adjust call and additionally writes the new locator through that pointer. Impact: a remote IPv6 packet routed through a configured ILA csum-adjust-transport route or receive-side mapping triggers a slab-use-after-free in ila_update_ipv6_locator() (KASAN). The route or mapping requires CAP_NET_ADMIN to configure, but trigger packets are unauthenticated once it exists. Reload ip6h after each pskb_may_pull() in ila_csum_adjust_transport() before the csum-diff read. In ila_update_ipv6_locator() only the ILA_CSUM_ADJUST_TRANSPORT case pulls the skb, so reload ip6h and iaddr in that case alone before the destination-address write; the neutral-map modes never pull and keep their cached pointers. Fixes: 33f11d16142b ("ila: Create net/ipv6/ila directory") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Reviewed-by: Antoine Tenart Link: https://patch.msgid.link/20260714114903.3763420-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ila/ila_common.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c index e71571455c8a..b78179bfc4c7 100644 --- a/net/ipv6/ila/ila_common.c +++ b/net/ipv6/ila/ila_common.c @@ -85,6 +85,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, struct tcphdr *th = (struct tcphdr *) (skb_network_header(skb) + nhoff); + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&th->check, skb, diff, true, true); @@ -96,6 +97,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, (skb_network_header(skb) + nhoff); if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&uh->check, skb, diff, true, true); @@ -110,6 +112,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, struct icmp6hdr *ih = (struct icmp6hdr *) (skb_network_header(skb) + nhoff); + ip6h = ipv6_hdr(skb); diff = get_csum_diff(ip6h, p); inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb, diff, true, true); @@ -127,6 +130,15 @@ void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, switch (p->csum_mode) { case ILA_CSUM_ADJUST_TRANSPORT: ila_csum_adjust_transport(skb, p); + /* + * ila_csum_adjust_transport() calls pskb_may_pull(), which can + * reallocate the skb head and leave ip6h (and the iaddr derived + * from it) dangling; reload both before the write below. The + * other csum modes do not pull, so their cached pointers stay + * valid. + */ + ip6h = ipv6_hdr(skb); + iaddr = ila_a2i(&ip6h->daddr); break; case ILA_CSUM_NEUTRAL_MAP: if (sir2ila) { -- cgit v1.2.3 From d3c87e71539cb183ea6173a99dc0e6f84fa0bc4f Mon Sep 17 00:00:00 2001 From: Surendra Singh Chouhan Date: Wed, 22 Jul 2026 21:54:44 +0530 Subject: spi: spacemit: prepare both DMA descriptors before submitting k1_spi_dma_one() currently submits the TX DMA descriptor to the DMA engine before preparing the RX DMA descriptor. If preparing the RX descriptor subsequently fails, the function jumps to the fallback error path without canceling or aborting the already submitted TX DMA descriptor. Fix this by preparing both the TX and RX descriptors before submitting either of them to the DMA engine. Fixes: efcd8b9d1111 ("spi: spacemit: introduce SpacemiT K1 SPI controller driver") Reviewed-by: Alex Elder Signed-off-by: Surendra Singh Chouhan Link: https://patch.msgid.link/20260722162444.11415-1-kr494167@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-spacemit-k1.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c index 0faf7ffed676..fd5893a92e59 100644 --- a/drivers/spi/spi-spacemit-k1.c +++ b/drivers/spi/spi-spacemit-k1.c @@ -278,25 +278,25 @@ static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi, struct spi_transfer *transfer) { struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); - struct dma_async_tx_descriptor *desc; + struct dma_async_tx_descriptor *txdesc, *rxdesc; u32 val; - /* Prepare the TX descriptor and submit it */ - desc = k1_spi_dma_prep(drv_data, transfer, true); - if (!desc) + /* Prepare the TX descriptor */ + txdesc = k1_spi_dma_prep(drv_data, transfer, true); + if (!txdesc) goto fallback; - dmaengine_submit(desc); - /* Prepare the RX descriptor and submit it */ - desc = k1_spi_dma_prep(drv_data, transfer, false); - if (!desc) + /* Prepare the RX descriptor */ + rxdesc = k1_spi_dma_prep(drv_data, transfer, false); + if (!rxdesc) goto fallback; /* When RX is complete we also know TX has completed */ - desc->callback = k1_spi_dma_callback; - desc->callback_param = drv_data; + rxdesc->callback = k1_spi_dma_callback; + rxdesc->callback_param = drv_data; - dmaengine_submit(desc); + dmaengine_submit(txdesc); + dmaengine_submit(rxdesc); val = readl(drv_data->base + SSP_TOP_CTRL); val |= TOP_TRAIL; /* Trailing bytes handled by DMA */ -- cgit v1.2.3 From d6f0248f04a96249660591e47fcf37ba98ac7ea3 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Tue, 30 Jun 2026 14:57:54 -0700 Subject: mshv: fix hv_input_get_system_property struct Keep it in sync with the correct definition. The old code worked by chance. Fixes: e68bda71a2384 ("hyperv: Add new Hyper-V headers in include/hyperv") Cc: stable@kernel.org Signed-off-by: Wei Liu --- include/hyperv/hvhdk_mini.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index b4cb2fa26e9b..035ba20870f7 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -184,8 +184,9 @@ enum hv_dynamic_processor_feature_property { struct hv_input_get_system_property { u32 property_id; /* enum hv_system_property */ + u32 reserved; union { - u32 as_uint32; + u64 as_uint64; #if IS_ENABLED(CONFIG_X86) /* enum hv_dynamic_processor_feature_property */ u32 hv_processor_feature; -- cgit v1.2.3 From f43ee0c0730d6191629b5ee1ceae27b1ebfdc047 Mon Sep 17 00:00:00 2001 From: Aldo Ariel Panzardo Date: Wed, 15 Jul 2026 08:41:14 -0300 Subject: net/sched: serialize qdisc_rtab_list against concurrent get/put qdisc_get_rtab() and qdisc_put_rtab() mutate the process-global singly linked list qdisc_rtab_list and a plain non-atomic 'int refcnt' with no lock. This was only safe because every caller historically held the RTNL mutex, which serialized all rate-table lookups, inserts and frees. That invariant no longer holds. cls_flower sets TCF_PROTO_OPS_DOIT_UNLOCKED, so tc_new_tfilter() keeps rtnl_held == false for it and sets TCA_ACT_FLAGS_NO_RTNL. That flag propagates through tcf_exts_validate_ex() -> tcf_action_init() -> tcf_action_init_1() -> tcf_police_init(), which calls qdisc_get_rtab()/qdisc_put_rtab() with the RTNL mutex NOT held. Two RTM_NEWTFILTER requests on different CPUs, each adding a flower filter with a police action carrying the same rate, then race on qdisc_rtab_list and on the non-atomic refcnt, leading to a use-after-free / double-free of the kmalloc-2k struct qdisc_rate_table. qdisc_rtab_list is a single global (not per-netns), so the corrupted object is shared system-wide. BUG: KASAN: slab-use-after-free in qdisc_put_rtab+0x12f/0x160 qdisc_put_rtab+0x12f/0x160 tcf_police_init+0xda9/0x1590 tcf_action_init_1+0x460/0x6b0 tcf_action_init+0x439/0xa40 tcf_exts_validate_ex+0x42d/0x550 fl_change+0xddd/0x7da0 tc_new_tfilter+0xaa7/0x2420 rtnetlink_rcv_msg+0x95e/0xe90 which belongs to the cache kmalloc-2k of size 2048 Protect qdisc_rtab_list and the refcount with a dedicated spinlock. The (sleeping, GFP_KERNEL) allocation in qdisc_get_rtab() is performed before taking the lock; if a concurrent inserter added an identical table in the meantime the freshly allocated one is freed under the lock, so no duplicate is leaked. qdisc_put_rtab() now decrements the refcount and unlinks under the same lock. Fixes: 470502de5bdb ("net: sched: unlock rules update API") Suggested-by: Eric Dumazet Signed-off-by: Aldo Ariel Panzardo Cc: stable@vger.kernel.org Acked-by: Jamal Hadi Salim Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260715114114.446841-1-qwe.aldo@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/sch_api.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 8a3236456db4..668bcd60d183 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -415,12 +415,13 @@ static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab) } static struct qdisc_rate_table *qdisc_rtab_list; +static DEFINE_SPINLOCK(qdisc_rtab_lock); struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab, struct netlink_ext_ack *extack) { - struct qdisc_rate_table *rtab; + struct qdisc_rate_table *rtab, *new_rtab; if (tab == NULL || r->rate == 0 || r->cell_log == 0 || r->cell_log >= 32 || @@ -429,15 +430,20 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, return NULL; } + new_rtab = kmalloc_obj(*new_rtab); + + spin_lock(&qdisc_rtab_lock); for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) { if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) && !memcmp(&rtab->data, nla_data(tab), TC_RTAB_SIZE)) { rtab->refcnt++; + spin_unlock(&qdisc_rtab_lock); + kfree(new_rtab); return rtab; } } - rtab = kmalloc_obj(*rtab); + rtab = new_rtab; if (rtab) { rtab->rate = *r; rtab->refcnt = 1; @@ -449,6 +455,7 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, } else { NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table"); } + spin_unlock(&qdisc_rtab_lock); return rtab; } EXPORT_SYMBOL(qdisc_get_rtab); @@ -457,18 +464,25 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab) { struct qdisc_rate_table *rtab, **rtabp; - if (!tab || --tab->refcnt) + if (!tab) return; + spin_lock(&qdisc_rtab_lock); + if (--tab->refcnt) { + spin_unlock(&qdisc_rtab_lock); + return; + } + for (rtabp = &qdisc_rtab_list; (rtab = *rtabp) != NULL; rtabp = &rtab->next) { if (rtab == tab) { *rtabp = rtab->next; - kfree(rtab); - return; + break; } } + spin_unlock(&qdisc_rtab_lock); + kfree(tab); } EXPORT_SYMBOL(qdisc_put_rtab); -- cgit v1.2.3 From 233a4d3a39fc1585f5e271b2adab43c6af025ae0 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 7 Jul 2026 23:05:28 +0800 Subject: drm/mediatek: Check CRTC state before freeing mtk_crtc_reset() destroys the current CRTC state only when crtc->state is non-NULL, but it always converts crtc->state to struct mtk_crtc_state and passes the result to kfree(). When reset is called without an existing state, container_of(NULL, ...) does not produce NULL. Keep the mtk state free in the same crtc->state guard as the helper state destruction. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") Signed-off-by: Ruoyu Wang Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c index 8e552cdc3b53..97e3ff412e6e 100644 --- a/drivers/gpu/drm/mediatek/mtk_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_crtc.c @@ -154,10 +154,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc) { struct mtk_crtc_state *state; - if (crtc->state) + if (crtc->state) { __drm_atomic_helper_crtc_destroy_state(crtc->state); - - kfree(to_mtk_crtc_state(crtc->state)); + kfree(to_mtk_crtc_state(crtc->state)); + } crtc->state = NULL; state = kzalloc_obj(*state); -- cgit v1.2.3 From d19d8542808c3be5f4af849da6509e877b74d811 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Jul 2026 19:29:57 +0800 Subject: drm/mediatek: mtk_hdmi: Fix DDC adapter double put in v2 mtk_hdmi_common_probe() gets the DDC adapter with of_find_i2c_adapter_by_node() and registers a devm action to release the adapter device reference with put_device(). The HDMI v2 remove callback also calls i2c_put_adapter() on the same DDC adapter. This is not paired with of_find_i2c_adapter_by_node(): it drops the adapter device reference before the devm action drops it again, and it also puts a module reference that was never taken. Remove the extra i2c_put_adapter() call and drop the now-empty HDMI v2 remove callback. The common devm action releases the adapter device reference. Fixes: 8d0f79886273 ("drm/mediatek: Introduce HDMI/DDC v2 for MT8195/MT8188") Cc: stable@vger.kernel.org Reviewed-by: Johan Hovold Signed-off-by: Guangshuo Li Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260713112957.884640-1-lgs201920130244@gmail.com/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_hdmi_v2.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c index 7bbf463056c9..ffe456238a2b 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c @@ -1499,13 +1499,6 @@ static int mtk_hdmi_v2_probe(struct platform_device *pdev) return 0; } -static void mtk_hdmi_v2_remove(struct platform_device *pdev) -{ - struct mtk_hdmi *hdmi = platform_get_drvdata(pdev); - - i2c_put_adapter(hdmi->ddc_adpt); -} - static const struct of_device_id mtk_drm_hdmi_v2_of_ids[] = { { .compatible = "mediatek,mt8188-hdmi-tx", .data = &mtk_hdmi_conf_mt8188 }, { .compatible = "mediatek,mt8195-hdmi-tx", .data = &mtk_hdmi_conf_mt8195 }, @@ -1515,7 +1508,6 @@ MODULE_DEVICE_TABLE(of, mtk_drm_hdmi_v2_of_ids); static struct platform_driver mtk_hdmi_v2_driver = { .probe = mtk_hdmi_v2_probe, - .remove = mtk_hdmi_v2_remove, .driver = { .name = "mediatek-drm-hdmi-v2", .of_match_table = mtk_drm_hdmi_v2_of_ids, -- cgit v1.2.3 From ab96bc97cdcf77c6f38634c73d00074c4f92c324 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Sun, 7 Jun 2026 19:06:16 -0700 Subject: Drivers: hv: vmbus: Set DMA coherent mask for VMBus devices In current code, the coherent_dma_mask for VMBus devices is not set, so it has the default value of 0, which essentially means "invalid". Because drivers for VMBus devices do not use dma_alloc_*() functions, the usual use of the coherent mask does not occur, and no errors result. However, a valid coherent_dma_mask may be needed even though the drivers don't use dma_alloc_*() functions. In a CoCo VM, the VMBus storvsc and netvsc drivers must bounce buffer DMA operations through the swiotlb because the Hyper-V host can't DMA into encrypted guest memory. If the kernel is built with CONFIG_SWIOTLB_DYNAMIC and the initial swiotlb size is small, swiotlb code may need to grow the swiotlb in response to a DMA mapping request. That growth first allocates a transient pool while the swiotlb is expanded in the background. The transient pool memory is allocated from the DMA atomic pools, and the allocation code checks for a valid coherent_dma_mask. With current code, this check fails, then the DMA mapping request from the storvsc or netvsc driver fails, and finally an I/O error occurs. Fix this problem by setting coherent_dma_mask for VMBus devices at the same time that dma_mask is set. Being a synthetic bus, VMBus does not have any restrictions on coherent DMA, so the coherent mask is set to the full 64 bits for all VMBus devices, just like with dma_mask. Signed-off-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 23206640c613..a6b9a33db657 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2169,6 +2169,7 @@ int vmbus_device_register(struct hv_device *child_device_obj) child_device_obj->device.dma_parms = &child_device_obj->dma_parms; child_device_obj->device.dma_mask = &child_device_obj->dma_mask; dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64)); + dma_set_coherent_mask(&child_device_obj->device, DMA_BIT_MASK(64)); /* * Register with the LDM. This will kick off the driver/device -- cgit v1.2.3 From b496f042e0da26dd673806870c204adfe697f169 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Thu, 25 Jun 2026 20:13:14 +0200 Subject: mshv_vtl: clear hypercall output before copyout mshv_vtl_hvcall_call() copies output_size bytes to userspace. The output page is freshly allocated. Userspace chooses the copyout length. If the hypercall writes less, the tail can contain stale page data. Clear the copied range before issuing the hypercall. Also check both bounce page allocations before either page is used. Signed-off-by: Yousef Alhouseen Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/mshv_vtl_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index 0d3d4161974f..dbf03b6676cd 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -1148,12 +1148,22 @@ static int mshv_vtl_hvcall_call(struct mshv_vtl_hvcall_fd *fd, */ in = (void *)__get_free_page(GFP_KERNEL); out = (void *)__get_free_page(GFP_KERNEL); + if (!in || !out) { + ret = -ENOMEM; + goto free_pages; + } if (copy_from_user(in, (void __user *)hvcall.input_ptr, hvcall.input_size)) { ret = -EFAULT; goto free_pages; } + /* + * The caller supplies output_size, so clear the range copied back to + * userspace in case the hypercall writes fewer bytes than requested. + */ + memset(out, 0, hvcall.output_size); + hvcall.status = hv_do_hypercall(hvcall.control, in, out); if (copy_to_user((void __user *)hvcall.output_ptr, out, hvcall.output_size)) { -- cgit v1.2.3 From ec08dd5b9ffa52980908805bdc8a55a8f1bc6667 Mon Sep 17 00:00:00 2001 From: Yi Xie Date: Wed, 8 Jul 2026 09:28:52 +0800 Subject: mshv_vtl: fix fd leak in mshv_ioctl_create_vtl() put_unused_fd() if anon_inode_getfile() fails. Fixes: 7bfe3b8ea6e30 ("Drivers: hv: Introduce mshv_vtl driver") Signed-off-by: Yi Xie Reviewed-by: Hamza Mahfooz Signed-off-by: Wei Liu --- drivers/hv/mshv_vtl_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index dbf03b6676cd..5ba1efb3b4e7 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -129,6 +129,7 @@ mshv_ioctl_create_vtl(void __user *user_arg, struct device *module_dev) file = anon_inode_getfile("mshv_vtl", &mshv_vtl_fops, vtl, O_RDWR); if (IS_ERR(file)) { + put_unused_fd(fd); kfree(vtl); return PTR_ERR(file); } -- cgit v1.2.3 From 8c7ab779c8850f4dab8473463cca9a7d52fdaecc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 Jul 2026 17:32:15 +0200 Subject: Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation lockdep_hardirq_threaded() is supposed to be used within IRQ core code and not within drivers. It is not obvious from within the driver, that this is the only interrupt service routing and that it is not shared handler. Replace lockdep_hardirq_threaded() with a lockdep annotation limiting threaded context on PREEMPT_RT to __vmbus_isr(). Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT") Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index a6b9a33db657..7d095c0add25 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1384,8 +1384,19 @@ void vmbus_isr(void) if (IS_ENABLED(CONFIG_PREEMPT_RT)) { vmbus_irqd_wake(); } else { - lockdep_hardirq_threaded(); + static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG); + + /* + * vmbus_isr is never force-threaded and always invoked at hard + * IRQ level. __vmbus_isr() below can acquire a spinlock_t + * which becomes a sleeping lock and must not be acquired in + * this context. Therefore on PREEMPT_RT this will be threaded + * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets + * lockdep know that acquiring a spinlock_t is not an issue. + */ + lock_map_acquire_try(&vmbus_map); __vmbus_isr(); + lock_map_release(&vmbus_map); } } EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl"); -- cgit v1.2.3 From 66688e655dcb1e9d06fa2c0651bd9fd588d2a2c3 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 Jul 2026 17:32:16 +0200 Subject: Drivers: hv: vmbus: Remove vmbus_irq_initialized vmbus_irq_initialized is only true if the registration of the per-CPU threads succeeded. If it failed, the whole registration aborts and the vmbus_exit() path is never called. Remove vmbus_irq_initialized. Reviewed-by: Michael Kelley Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 7d095c0add25..6824bd7cb3c4 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1369,8 +1369,6 @@ static void run_vmbus_irqd(unsigned int cpu) __vmbus_isr(); } -static bool vmbus_irq_initialized; - static struct smp_hotplug_thread vmbus_irq_threads = { .store = &vmbus_irqd, .setup = vmbus_irqd_setup, @@ -1497,11 +1495,10 @@ static int vmbus_bus_init(void) * the VMbus interrupt handler. */ - if (IS_ENABLED(CONFIG_PREEMPT_RT) && !vmbus_irq_initialized) { + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { ret = smpboot_register_percpu_thread(&vmbus_irq_threads); if (ret) goto err_kthread; - vmbus_irq_initialized = true; } if (vmbus_irq == -1) { @@ -1545,10 +1542,8 @@ err_connect: else free_percpu_irq(vmbus_irq, &vmbus_evt); err_setup: - if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) { + if (IS_ENABLED(CONFIG_PREEMPT_RT)) smpboot_unregister_percpu_thread(&vmbus_irq_threads); - vmbus_irq_initialized = false; - } err_kthread: bus_unregister(&hv_bus); return ret; @@ -3046,10 +3041,9 @@ static void __exit vmbus_exit(void) hv_remove_vmbus_handler(); else free_percpu_irq(vmbus_irq, &vmbus_evt); - if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) { + if (IS_ENABLED(CONFIG_PREEMPT_RT)) smpboot_unregister_percpu_thread(&vmbus_irq_threads); - vmbus_irq_initialized = false; - } + for_each_online_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); -- cgit v1.2.3 From 649dd135491945afa544351e3e6d4a727de87020 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:49 +0000 Subject: mshv: Fix duplicate GSI detection for GSI 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The duplicate routing entry check in mshv_update_routing_table() uses guest_irq_num != 0 to detect whether a GSI slot is already occupied. This fails for GSI 0 because its guest_irq_num is 0 both when the slot is unused (zero-initialized) and when legitimately assigned. As a result, duplicate entries for GSI 0 are silently accepted, with the second entry overwriting the first — corrupting the routing table without any error reported to userspace. While GSI 0 (legacy timer) is unlikely to appear in MSI-based routing in practice, the check is semantically wrong — it conflates "uninitialized" with "GSI number 0." Use girq_entry_valid instead, which is explicitly set to true when an entry is populated and remains zero for unused slots regardless of the GSI number. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c index b3142c84dcbc..65a4ffc82d56 100644 --- a/drivers/hv/mshv_irq.c +++ b/drivers/hv/mshv_irq.c @@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition, /* * Allow only one to one mapping between GSI and MSI routing. */ - if (girq->guest_irq_num != 0) { + if (girq->girq_entry_valid) { r = -EINVAL; goto out; } -- cgit v1.2.3 From a9708e550d11a53b22d932cdbfaa10c26346a2d0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:59 +0000 Subject: mshv: Fix sleeping under spinlock in mshv_portid_alloc idr_alloc() is called with GFP_KERNEL inside idr_lock(), which holds a spinlock. GFP_KERNEL allows the allocator to sleep, triggering a sleeping-while-atomic bug. Fix by using idr_preload(GFP_KERNEL) before taking the lock to pre-allocate memory in a sleepable context, then idr_alloc() with GFP_NOWAIT inside the spinlock-protected section. Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_portid_table.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c index c349af1f0aaa..6f59b3e37624 100644 --- a/drivers/hv/mshv_portid_table.c +++ b/drivers/hv/mshv_portid_table.c @@ -40,12 +40,14 @@ mshv_port_table_fini(void) int mshv_portid_alloc(struct port_table_info *info) { - int ret = 0; + int ret; + idr_preload(GFP_KERNEL); idr_lock(&port_table_idr); ret = idr_alloc(&port_table_idr, info, PORTID_MIN, - PORTID_MAX, GFP_KERNEL); + PORTID_MAX, GFP_NOWAIT); idr_unlock(&port_table_idr); + idr_preload_end(); return ret; } -- cgit v1.2.3 From 5d8ed6b64220ad629aade5f174e3f690c37435f9 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Tue, 21 Jul 2026 17:04:38 +0530 Subject: drm/xe/i2c: Allow per domain unique id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI bus, device and function can be same for devices existing across different domains. Allow per domain unique identifier while registering platform device to prevent name conflict. Fixes: f0e53aadd702 ("drm/xe: Support for I2C attached MCUs") Signed-off-by: Raag Jadav Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260721113438.651100-1-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit a79f6abc8b516b5bd906e2eca8121e3549ee163f) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_i2c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 706783863d07..f05f23221c1b 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -95,18 +95,21 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c) struct platform_device *pdev; struct fwnode_handle *fwnode; int ret; + u32 id; fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL); if (IS_ERR(fwnode)) return PTR_ERR(fwnode); + id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci); + /* * Not using platform_device_register_full() here because we don't have * a handle to the platform_device before it returns. xe_i2c_notifier() * uses that handle, but it may be called before * platform_device_register_full() is done. */ - pdev = platform_device_alloc(adapter_name, pci_dev_id(pci)); + pdev = platform_device_alloc(adapter_name, id); if (!pdev) { ret = -ENOMEM; goto err_fwnode_remove; -- cgit v1.2.3 From d2c6800ad1802bed72a6de1416536737f114f1d6 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Tue, 21 Jul 2026 20:55:14 +0000 Subject: drm/xe/vm: Fix SVM leak on resv obj alloc failure in xe_vm_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") made xe_svm_init() unconditional in xe_vm_create() and extended it to also initialize a "simple" gpusvm state for non-fault-mode VMs. The matching xe_svm_fini() call in xe_vm_close_and_put() was updated to run unconditionally, but the error unwind path in xe_vm_create() was not. On the drm_gpuvm_resv_object_alloc() failure path, xe_svm_init() has already succeeded but xe_svm_fini() is only called when XE_VM_FLAG_FAULT_MODE is set. For non-fault-mode VMs this leaves vm->svm.gpusvm partially initialized and leaks the resources allocated by drm_gpusvm_init(). For fault-mode VMs, xe_svm_init() additionally acquires the pagemap owner via drm_pagemap_acquire_owner() and the pagemaps via xe_svm_get_pagemaps(). Those resources are released by xe_svm_close(), not xe_svm_fini(). On the same error path, xe_svm_close() is not called either, so fault-mode VMs leak the pagemap owner and pagemaps. Fix both leaks: - Call xe_svm_fini() unconditionally on the err_svm_fini path, matching the unconditional xe_svm_init() call. Move the vm->size = 0 assignment out of the conditional so the xe_vm_is_closed() assert in xe_svm_fini() (and xe_svm_close()) holds for both modes. - Call xe_svm_close() for fault-mode VMs before xe_svm_fini(), matching the ordering used in xe_vm_close_and_put(). Fixes: 9e9787414882 ("drm/xe/userptr: replace xe_hmm with gpusvm") Cc: Matthew Auld Assisted-by: Claude:claude-opus-4.7 Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260721205516.4058959-2-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit ca2a3587d577ba764e0fe628fb676244fc33ddd4) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_vm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 32ded13491ca..67819deb45e3 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1809,10 +1809,10 @@ err_close: return ERR_PTR(err); err_svm_fini: - if (flags & XE_VM_FLAG_FAULT_MODE) { - vm->size = 0; /* close the vm */ - xe_svm_fini(vm); - } + vm->size = 0; /* close the vm */ + if (flags & XE_VM_FLAG_FAULT_MODE) + xe_svm_close(vm); + xe_svm_fini(vm); err_no_resv: mutex_destroy(&vm->snap_mutex); for_each_tile(tile, xe, id) -- cgit v1.2.3 From 4a9ec5ec9555ad62dc5b81a37ac946025c2ea002 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 22 Jul 2026 17:09:43 -0700 Subject: x86/boot/compressed: Disable jump tables After a recent upstream LLVM change to start generating jump and lookup tables in switch statements in more instances [1], linking the compressed x86 boot image when CONFIG_KERNEL_ZSTD is enabled fails with: ld.lld: error: Unexpected run-time relocations (.rela) detected! Dumping the relocations in misc.o, which is the only file influenced by CONFIG_KERNEL_ZSTD in the decompressor, shows dynamic relocations to some string constants, which correspond to the string literals in the switch statement in handle_zstd_error(): Relocation section '.rela.data.rel.ro' at offset 0x277b0 contains 31 entries: Offset Info Type Symbol's Value Symbol's Name + Addend 0000000000000000 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 73a 0000000000000008 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e 0000000000000010 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e 0000000000000018 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e ... This optimization is problematic for the decompressor environment, as it is built as -fPIE without any explicit absolute references (as described at the top of misc.c) while not applying any dynamic relocations, hence the linker assertion. To opt out of this optimization, which is of little value in this special early boot code, and to mirror the other x86 startup code in arch/x86/boot/startup, disable jump tables in the decompressor. Signed-off-by: Nathan Chancellor Signed-off-by: Ingo Molnar Acked-by: Ard Biesheuvel Cc: Bill Wendling Cc: Justin Stitt Cc: Nick Desaulniers Cc: "H. Peter Anvin" Cc: Peter Zijlstra Cc: stable@vger.kernel.org Link: https://github.com/llvm/llvm-project/commit/fa02a6ed66b1700c996b49c96c6bc0eb014c9518 [1] Link: https://patch.msgid.link/20260722-x86-boot-compressed-disable-jt-clang-v2-1-7373d38482fb@kernel.org Closes: https://github.com/ClangBuiltLinux/linux/issues/2165 --- arch/x86/boot/compressed/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 07e0e64b9a98..06934f9691d6 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -27,6 +27,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \ KBUILD_CFLAGS := -m$(BITS) -O2 $(CLANG_FLAGS) KBUILD_CFLAGS += $(CC_FLAGS_DIALECT) KBUILD_CFLAGS += -fno-strict-aliasing -fPIE +KBUILD_CFLAGS += -fno-jump-tables KBUILD_CFLAGS += -Wundef KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING cflags-$(CONFIG_X86_32) := -march=i386 -- cgit v1.2.3 From 503d67fbaec6fdeaba391cb497675071db9d16ea Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Tue, 21 Jul 2026 14:41:40 +0800 Subject: fs/super: fix emergency thaw double-unlock of s_umount do_thaw_all() iterates over all superblocks via __iterate_supers() with SUPER_ITER_EXCL, which acquires s_umount exclusively before calling the callback and releases it afterwards. However, the callback do_thaw_all_callback() calls thaw_super_locked() which unconditionally releases s_umount on every code path. This results in a second unlock attempt in __iterate_supers() that corrupts the rwsem state, triggering a DEBUG_RWSEMS warning: [ 182.601148] sysrq: Emergency Thaw of all frozen filesystems [ 182.601865] ------------[ cut here ]------------ [ 182.602375] DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) && !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE)): count = 0x0, magic = 0xffff99b1011e5870, owner = 0x0, curr 0xffff99b101b06c80, list not empty [ 182.603817] WARNING: kernel/locking/rwsem.c:1412 at up_write+0xa3/0x170, CPU#2: kworker/2:1/53 [ 182.604578] Modules linked in: [ 182.604864] CPU: 2 UID: 0 PID: 53 Comm: kworker/2:1 Not tainted 7.2.0-rc4-00001-gbd3bd93ea98a-dirty #4 PREEMPT(lazy) [ 182.605711] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1kylin1 04/01/2014 [ 182.606417] Workqueue: events do_thaw_all [ 182.606750] RIP: 0010:up_write+0xaf/0x170 [ 182.607076] Code: 19 3a 92 48 0f 44 c2 48 8b 55 08 48 8b 55 00 4c 8b 45 08 48 8b 55 00 48 8d 3d ad 91 e0 01 48 8b 4d 20 50 48 c7 c6 f0 8c 26 92 <67> 48 0f b9 3a e8 d7 93 4e 00 58 eb 81 48 83 7f 18 00 48 c7 c2 8d [ 182.608563] RSP: 0018:ffffb670001d7e08 EFLAGS: 00010246 [ 182.609007] RAX: ffffffff92349e8d RBX: 0000000000000000 RCX: ffff99b1011e5870 [ 182.609595] RDX: 0000000000000000 RSI: ffffffff92268cf0 RDI: ffffffff92914d10 [ 182.610283] RBP: ffff99b1011e5870 R08: 0000000000000000 R09: ffff99b101b06c80 [ 182.610847] R10: ffff99b10139a808 R11: fefefefefefefeff R12: 0000000000000000 [ 182.611414] R13: ffffffff90cf74d0 R14: 0000000000000000 R15: ffff99b1011e5800 [ 182.612009] FS: 0000000000000000(0000) GS:ffff99b1eaaee000(0000) knlGS:0000000000000000 [ 182.612670] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 182.613146] CR2: 00000000005c631c CR3: 00000000013ee000 CR4: 00000000000006f0 [ 182.613722] Call Trace: [ 182.613946] [ 182.614130] __iterate_supers+0x128/0x150 [ 182.614463] do_thaw_all+0x1b/0x30 [ 182.614759] process_scheduled_works+0xbb/0x3f0 [ 182.615150] ? __pfx_worker_thread+0x10/0x10 [ 182.615499] worker_thread+0x129/0x270 [ 182.615816] ? __pfx_worker_thread+0x10/0x10 [ 182.616201] kthread+0xe2/0x120 [ 182.616469] ? __pfx_kthread+0x10/0x10 [ 182.616792] ret_from_fork+0x15b/0x240 [ 182.617115] ? __pfx_kthread+0x10/0x10 [ 182.617426] ret_from_fork_asm+0x1a/0x30 [ 182.617761] [ 182.617968] ---[ end trace 0000000000000000 ]--- [ 182.618412] Emergency Thaw complete Fix this by switching to SUPER_ITER_UNLOCKED and acquiring s_umount in the callback via super_lock_excl() before calling thaw_super_locked(). This matches the locking pattern expected by thaw_super_locked() and eliminates the double unlock. While at it, remove the dead 'return;' at the end of do_thaw_all_callback(). Fixes: 2992476528ae ("super: use a common iterator (Part 1)") Cc: stable@vger.kernel.org Signed-off-by: Chen Changcheng Link: https://patch.msgid.link/20260721064140.152305-1-chenchangcheng@kylinos.cn Signed-off-by: Christian Brauner (Amutable) --- fs/super.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/super.c b/fs/super.c index a8fd61136aaf..70dcb07e7fa5 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1084,16 +1084,19 @@ void emergency_remount(void) static void do_thaw_all_callback(struct super_block *sb, void *unused) { + if (!super_lock_excl(sb)) + return; + if (IS_ENABLED(CONFIG_BLOCK)) while (sb->s_bdev && !bdev_thaw(sb->s_bdev)) pr_warn("Emergency Thaw on %pg\n", sb->s_bdev); + thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL); - return; } static void do_thaw_all(struct work_struct *work) { - __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_EXCL); + __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_UNLOCKED); kfree(work); printk(KERN_WARNING "Emergency Thaw complete\n"); } -- cgit v1.2.3 From fb0bf289f5d529336ef490c8273e88a8a8b29f69 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Fri, 17 Jul 2026 10:46:14 +0000 Subject: usb: typec: ucsi: Correct teardown ordering in ucsi_init() error path The commit 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration") consolidated port teardown into the ucsi_unregister_port() helper. However, it introduced an ordering problem in the ucsi_init() error path. Fix this by ensuring ucsi_unregister_port() is called before we unregister their corresponding lockdep keys. Cc: stable@vger.kernel.org Fixes: 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration") Reported-by: "Borah, Chaitanya Kumar" Closes: https://lore.kernel.org/all/22064276-6c56-411a-9f20-6917ceeb865f@intel.com/ Signed-off-by: Andrei Kuchynski Tested-by: Chaitanya Kumar Borah Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260717104614.325250-1-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 1ae4224d2dfc..49f1c53721bb 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -2142,11 +2142,11 @@ static int ucsi_init(struct ucsi *ucsi) return 0; err_unregister: + for (con = connector; con->port; con++) + ucsi_unregister_port(con); for (i = 0; i < ucsi->cap.num_connectors; i++) lockdep_unregister_key(&connector[i].lock_key); - for (con = connector; con->port; con++) - ucsi_unregister_port(con); kfree(connector); err_reset: memset(&ucsi->cap, 0, sizeof(ucsi->cap)); -- cgit v1.2.3 From cbfe2b24a1ea9de35032dbdd100fdc700f5be92d Mon Sep 17 00:00:00 2001 From: Carlos López Date: Wed, 15 Jul 2026 12:51:37 +0200 Subject: KVM: arm64: vgic: Fix race between LPI release and re-registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a potential race between decrementing an LPI's reference count and evicting that structure from the LPI xarray. LPI structures are maintained in the VGIC LPI xarray (dist->lpi_xa). When the reference count of an LPI structure drops to zero, vgic_release_lpi_locked() removes the structure from the xarray and frees it under the xarray lock. However, the release of an LPI can race with a concurrent LPI re-registration with the same INTID via vgic_add_lpi() on another CPU, since the reference count drop and the xarray eviction are not performed in a single atomic step. This can happen e.g. if the guest issues a DISCARD while the LPI is still referenced from a vCPU's active-pending list (ap_list), and the same INTID is re-mapped via MAPTI. Particularly, vgic_release_lpi_locked() is called from two distinct paths: direct release via vgic_put_irq(), and deferred release via vgic_release_deleted_lpis(). During direct release, the issue can result in deleting a newly registered LPI from the xarray: CPU0 (Releasing LPI) CPU1 (Adding new LPI) ==================== ===================== vgic_put_irq() __vgic_put_irq() refcount_dec_and_test() vgic_add_lpi() xa_lock_irqsave() old_irq = xa_load(.., intid) vgic_try_get_irq_ref(old_irq) == false new IRQ inserted --> __xa_store(.., intid, ..) xa_unlock_irqrestore() xa_lock_irqsave(); vgic_release_lpi_locked() __xa_erase(.., irq->intid) <-- BUG: new IRQ is erased kfree_rcu(old_irq) During the deferred release path, the old IRQ can be leaked: CPU0 (Releasing LPI) CPU1 (Adding new LPI) ==================== ===================== vgic_put_irq_norelease() __vgic_put_irq() refcount_dec_and_test() irq->pending_release = true vgic_add_lpi() xa_lock_irqsave() old_irq = xa_load(.., intid) vgic_try_get_irq_ref(oldirq) == false BUG: old IRQ overwritten --> __xa_store(.., intid, ..) xa_unlock_irqrestore() vgic_release_deleted_lpis() xa_lock_irqsave() xa_for_each() { .. } <-- old IRQ with pending_release = true is gone, so it cannot be released To fix the direct release path, move the reference count drop inside the xarray lock, making sure that vgic_add_lpi() never encounters the to-be-released LPI. In the deferred release path, the refcount drop must happen under a raw spinlock, so the xarray lock cannot be grabbed, and the same solution does not work. Instead, update vgic_add_lpi(), so that if it evicts an LPI from the xarray, it takes on the responsibility of freeing it. Consequently, an LPI may now be freed concurrently after a deferred release drops the refcount, so accessing the pending_release field is no longer safe from use-after-free. Delete all uses of the flag, and update vgic_release_deleted_lpis() to identify orphaned LPIs purely based on their refcount. Reported-by: Claude:claude-opus-4-6 Fixes: 3a08a6ca7c37 ("KVM: arm64: vgic-v3: Use bare refcount for VGIC LPIs") Fixes: d54594accf73 ("KVM: arm64: vgic-v3: Erase LPIs from xarray outside of raw spinlocks") Signed-off-by: Carlos López Link: https://patch.msgid.link/20260715105137.3973823-4-clopez@suse.de Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-its.c | 24 ++++++++++++++++-------- arch/arm64/kvm/vgic/vgic.c | 18 ++++++++---------- include/kvm/arm_vgic.h | 3 --- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 740b39875728..5c5d1772147d 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -116,18 +116,26 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid, kfree(irq); irq = oldirq; } else { - ret = xa_err(__xa_store(&dist->lpi_xa, intid, irq, 0)); - } - - xa_unlock_irqrestore(&dist->lpi_xa, flags); + /* + * The entry is either empty or contains a dead LPI (refcount=0) + * from the deferred release path, pending cleanup by + * vgic_release_deleted_lpis(). Evict and free it if present. + */ + oldirq = __xa_store(&dist->lpi_xa, intid, irq, 0); + ret = xa_err(oldirq); + if (ret) { + xa_unlock_irqrestore(&dist->lpi_xa, flags); + kfree(irq); - if (ret) { - xa_release(&dist->lpi_xa, intid); - kfree(irq); + return ERR_PTR(ret); + } - return ERR_PTR(ret); + if (oldirq && !WARN_ON_ONCE(refcount_read(&oldirq->refcount))) + kfree_rcu(oldirq, rcu); } + xa_unlock_irqrestore(&dist->lpi_xa, flags); + /* * We "cache" the configuration table entries in our struct vgic_irq's. * However we only have those structs for mapped IRQs, so we read in diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index ccb7e3a90cd0..74bace10a22e 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -146,11 +146,7 @@ static __must_check bool __vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) static __must_check bool vgic_put_irq_norelease(struct kvm *kvm, struct vgic_irq *irq) { - if (!__vgic_put_irq(kvm, irq)) - return false; - - irq->pending_release = true; - return true; + return __vgic_put_irq(kvm, irq); } void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) @@ -167,12 +163,14 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq) guard(spinlock_irqsave)(&dist->lpi_xa.xa_lock); } - if (!__vgic_put_irq(kvm, irq)) + if (!irq_is_lpi(kvm, irq->intid)) return; - xa_lock_irqsave(&dist->lpi_xa, flags); - vgic_release_lpi_locked(dist, irq); - xa_unlock_irqrestore(&dist->lpi_xa, flags); + if (refcount_dec_and_lock_irqsave(&irq->refcount, + &dist->lpi_xa.xa_lock, &flags)) { + vgic_release_lpi_locked(dist, irq); + xa_unlock_irqrestore(&dist->lpi_xa, flags); + } } static void vgic_release_deleted_lpis(struct kvm *kvm) @@ -184,7 +182,7 @@ static void vgic_release_deleted_lpis(struct kvm *kvm) xa_lock_irqsave(&dist->lpi_xa, flags); xa_for_each(&dist->lpi_xa, intid, irq) { - if (irq->pending_release) + if (!refcount_read(&irq->refcount)) vgic_release_lpi_locked(dist, irq); } diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index fe49fb56dc3c..cefddc9c621d 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -247,9 +247,6 @@ struct vgic_irq { * affinity reg (v3). */ - bool pending_release:1; /* Used for LPIs only, unreferenced IRQ - * pending a release */ - bool pending_latch:1; /* The pending latch state used to calculate * the pending state for both level * and edge triggered IRQs. */ -- cgit v1.2.3 From 21f12496fdd357ad4e1fcdd07dc80ab7378f7d24 Mon Sep 17 00:00:00 2001 From: Carlos López Date: Wed, 15 Jul 2026 12:51:38 +0200 Subject: KVM: arm64: vgic: Mitigate potential LPI registration failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mitigate a potential failure when inserting a new LPI into the VGIC LPI xarray. When vgic_add_lpi() is preparing to register a new LPI, it pre-allocates an xarray entry using xa_reserve_irq(), so that it can later perform the insertion under the xarray lock without allocating. However, since xa_reserve_irq() is called before acquiring such lock, there is a potential race where xa_reserve_irq() observes a populated entry, thus not performing the allocation, and another CPU removes that entry before the xarray lock is grabbed to perform the insertion. CPU0 (Adding new LPI) CPU1 (Releasing LPI) ===================== =================== vgic_add_lpi() /* Entry populated, does not allocate */ xa_reserve_irq(.., intid, ..) vgic_release_deleted_lpis() xa_lock_irqsave() vgic_release_lpi_locked() xarray node freed --> __xa_erase(.., intid) xa_unlock_irqrestore() xa_lock_irqsave() xa_load(.., intid) == NULL vgic_try_get_irq_ref(NULL) == false __xa_store(.., intid, irq, 0) <-- xarray node was freed, gfp=0 cannot allocate, returns -ENOMEM This can happen e.g. if the guest issues a DISCARD while the LPI is still referenced from a vCPU's active-pending list (ap_list), and the same INTID is re-mapped via MAPTI. Mitigate this by passing GFP_NOWAIT to __xa_store(), so that the allocation can happen under the lock in the rare case that this condition is hit. Add __GFP_ACCOUNT as well to match xa_reserve_irq()'s flags. Reported-by: Sashiko Fixes: 1d6f83f60f79 ("KVM: arm64: vgic: Store LPIs in an xarray") Signed-off-by: Carlos López Link: https://patch.msgid.link/20260715105137.3973823-5-clopez@suse.de Signed-off-by: Marc Zyngier --- arch/arm64/kvm/vgic/vgic-its.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 5c5d1772147d..36ab3e492915 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -121,7 +121,8 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid, * from the deferred release path, pending cleanup by * vgic_release_deleted_lpis(). Evict and free it if present. */ - oldirq = __xa_store(&dist->lpi_xa, intid, irq, 0); + oldirq = __xa_store(&dist->lpi_xa, intid, irq, + GFP_NOWAIT | __GFP_ACCOUNT); ret = xa_err(oldirq); if (ret) { xa_unlock_irqrestore(&dist->lpi_xa, flags); -- cgit v1.2.3 From bbece712cfc7f286b2908ac120dcf700279d87eb Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Wed, 15 Jul 2026 11:51:00 +0100 Subject: KVM: arm64: Fix hyp_trace clock disabling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the disable path in hyp_trace_clock_enable(), which fell through to re-initialize and reschedule the clock after cancelling the work. Return early instead. While at it, cleanup hyp_trace_clock::lock which is unused and hyp_trace_clock::running which is redundant: the trace_remote framework already serializes calls to the callback enable_tracing. Fixes: b22888917fa4 ("KVM: arm64: Sync boot clock with the nVHE/pKVM hyp") Signed-off-by: Vincent Donnefort Reviewed-by: Fuad Tabba (✓ DKIM/linux.dev) Link: https://patch.msgid.link/20260715105100.3178255-1-vdonnefort@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp_trace.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index 2411b4c32932..9bfa368dd841 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -37,8 +37,6 @@ static struct hyp_trace_clock { u32 shift; struct delayed_work work; struct completion ready; - struct mutex lock; - bool running; } hyp_clock; static void __hyp_clock_work(struct work_struct *work) @@ -110,12 +108,9 @@ static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enabl { struct system_time_snapshot snap; - if (hyp_clock->running == enable) - return; - if (!enable) { cancel_delayed_work_sync(&hyp_clock->work); - hyp_clock->running = false; + return; } ktime_get_snapshot_id(CLOCK_BOOTTIME, &snap); @@ -128,7 +123,6 @@ static void hyp_trace_clock_enable(struct hyp_trace_clock *hyp_clock, bool enabl INIT_DELAYED_WORK(&hyp_clock->work, __hyp_clock_work); schedule_delayed_work(&hyp_clock->work, msecs_to_jiffies(CLOCK_INIT_MS)); wait_for_completion(&hyp_clock->ready); - hyp_clock->running = true; } /* Access to this struct within the trace_remote_callbacks are protected by the trace_remote lock */ @@ -304,9 +298,15 @@ static void hyp_trace_unload(struct trace_buffer_desc *desc, void *priv) static int hyp_trace_enable_tracing(bool enable, void *priv) { + int ret; + hyp_trace_clock_enable(&hyp_clock, enable); - return kvm_call_hyp_nvhe(__tracing_enable, enable); + ret = kvm_call_hyp_nvhe(__tracing_enable, enable); + if (ret) + hyp_trace_clock_enable(&hyp_clock, !enable); + + return ret; } static int hyp_trace_swap_reader_page(unsigned int cpu, void *priv) -- cgit v1.2.3 From df7a9d376f7a388ecfacf8dfe0b5819ddfba6972 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 10 Jul 2026 12:48:18 +0100 Subject: KVM: arm64: Fix potential leak in hyp_trace_buffer_alloc_bpages_backing In the very unlikely event of a failure in __map_hyp, the allocated backing pages are leaked in hyp_trace_buffer_alloc_bpages_backing(). Fix this by freeing the pages on error. Fixes: 3aed038aac8d ("KVM: arm64: Add trace remote for the nVHE/pKVM hyp") Reported-by: Sashiko Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Signed-off-by: Vincent Donnefort Link: https://patch.msgid.link/20260710114819.2689386-2-vdonnefort@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp_trace.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index 9bfa368dd841..aabf2989d70d 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -154,6 +154,7 @@ static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_ int nr_bpages = (PAGE_ALIGN(size) / PAGE_SIZE) + 1; size_t backing_size; void *start; + int ret; backing_size = PAGE_ALIGN(sizeof(struct simple_buffer_page) * nr_bpages * num_possible_cpus()); @@ -162,10 +163,16 @@ static int hyp_trace_buffer_alloc_bpages_backing(struct hyp_trace_buffer *trace_ if (!start) return -ENOMEM; + ret = __map_hyp(start, backing_size); + if (ret) { + free_pages_exact(start, backing_size); + return ret; + } + trace_buffer->desc->bpages_backing_start = (unsigned long)start; trace_buffer->desc->bpages_backing_size = backing_size; - return __map_hyp(start, backing_size); + return ret; } static void hyp_trace_buffer_free_bpages_backing(struct hyp_trace_buffer *trace_buffer) -- cgit v1.2.3 From ca28278d10ec234592989ad370d58294b9d43e0f Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 10 Jul 2026 12:48:19 +0100 Subject: KVM: arm64: Fix hyp_trace_desc allocation size in hyp_trace_load() The footprint calculated for struct hyp_trace_desc sizes only trace_buffer_desc and do not take into account the other fields. It worked so far thanks to the follow-up PAGE_ALIGN(). Fix the descriptor size and while at it, enforce an overflow check after PAGE_ALIGN(). Reported-by: Sashiko Fixes: 3aed038aac8d ("KVM: arm64: Add trace remote for the nVHE/pKVM hyp") Signed-off-by: Vincent Donnefort Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260710114819.2689386-3-vdonnefort@google.com Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp_trace.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index aabf2989d70d..1adfdc800187 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -229,18 +229,22 @@ static int hyp_trace_buffer_share_hyp(struct hyp_trace_buffer *trace_buffer) static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv) { struct hyp_trace_buffer *trace_buffer = priv; + size_t desc_size, tb_desc_size; struct hyp_trace_desc *desc; - size_t desc_size; int ret; if (WARN_ON(trace_buffer->desc)) return ERR_PTR(-EINVAL); - desc_size = trace_buffer_desc_size(size, num_possible_cpus()); + tb_desc_size = trace_buffer_desc_size(size, num_possible_cpus()); + desc_size = size_add(tb_desc_size, offsetof(struct hyp_trace_desc, trace_buffer_desc)); if (desc_size == SIZE_MAX) return ERR_PTR(-E2BIG); desc_size = PAGE_ALIGN(desc_size); + if (!desc_size) + return ERR_PTR(-E2BIG); + desc = (struct hyp_trace_desc *)alloc_pages_exact(desc_size, GFP_KERNEL); if (!desc) return ERR_PTR(-ENOMEM); @@ -256,7 +260,7 @@ static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv) if (ret) goto err_free_desc; - ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, desc_size, size, + ret = trace_remote_alloc_buffer(&desc->trace_buffer_desc, tb_desc_size, size, cpu_possible_mask); if (ret) goto err_free_backing; -- cgit v1.2.3 From e7821048e8d72a94b1fb7422f8b45aa374ff076f Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Wed, 17 Jun 2026 10:52:38 +0100 Subject: KVM: arm64: Add missing hyp_enter when trapping sysreg Add a missing hypervisor event call for hyp_enter on sysreg trapping, causing an unbalanced hyp_enter/hyp_exit. The enum hyp_enter_exit_reason is not ABI, so we can keep the ERET reasons at the end for clarity. Fixes: 696dfec22b8e ("KVM: arm64: Add hyp_enter/hyp_exit events to nVHE/pKVM hyp") Signed-off-by: Vincent Donnefort Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260617095238.1530121-1-vdonnefort@google.com Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_hypevents.h | 1 + arch/arm64/kvm/hyp/nvhe/hyp-main.c | 1 + arch/arm64/kvm/hyp_trace.c | 1 + 3 files changed, 3 insertions(+) diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h index 743c49bd878f..5f6e6789d121 100644 --- a/arch/arm64/include/asm/kvm_hypevents.h +++ b/arch/arm64/include/asm/kvm_hypevents.h @@ -12,6 +12,7 @@ enum hyp_enter_exit_reason { HYP_REASON_SMC, HYP_REASON_HVC, + HYP_REASON_SYS, HYP_REASON_PSCI, HYP_REASON_HOST_ABORT, HYP_REASON_GUEST_EXIT, diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index d3c69de698f4..d3df96ed8ba4 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -932,6 +932,7 @@ void handle_trap(struct kvm_cpu_context *host_ctxt) handle_host_mem_abort(host_ctxt); break; case ESR_ELx_EC_SYS64: + trace_hyp_enter(host_ctxt, HYP_REASON_SYS); if (handle_host_mte(esr)) break; fallthrough; diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index 1adfdc800187..9644c424819b 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -409,6 +409,7 @@ static const char *__hyp_enter_exit_reason_str(u8 reason) static const char strs[][12] = { "smc", "hvc", + "sys", "psci", "host_abort", "guest_exit", -- cgit v1.2.3 From 679d7201c1f09e37fa1c12ce28d84079c17fc87f Mon Sep 17 00:00:00 2001 From: Alexandru Elisei Date: Wed, 22 Jul 2026 10:03:54 +0100 Subject: KVM: arm64: Reject guest_memfd memslots when the VM has MTE The user cannot use MTE on VMAs created by mapping a guest_memfd file, as arch_calc_vm_flag_bits() does not set VM_MTE_ALLOWED. When creating a guest_memfd backed memslot, kvm_arch_prepare_memory_region() rejects the memslot if MTE is enabled for the VM and if guest_memfd has been mapped in a VMA that intersects the memslot. However, the documentation for KVM_SET_USER_MEMORY_REGION2 explicitly states that the only condition for userspace_addr is for it to be a legal userspace address, but the mapping is not required to be valid nor populated at memslot creation. If userspace sets userspace_addr to an address that hasn't been mapped, or if userspace_addr belongs to a VMA that isn't backed by the guest_memfd file, or if the VMA doesn't intersect the memslot, memslot creation is successful and KVM ends up with a VM with MTE and guest_memfd-backed memslots. The same happens if the order is reversed: when userspace enables MTE, KVM does not check if memslots backed by guest_memfd are already present. Fix both issues by rejecting guest_memfd-backed memslots when MTE is enabled, and by rejecting MTE when guest_memfd-backed memslots are already present. Fixes: 32e200bd6e44 ("KVM: arm64: Enable support for guest_memfd backed memory") Tested-by: Fuad Tabba Reviewed-by: Fuad Tabba Signed-off-by: Alexandru Elisei Link: https://patch.msgid.link/20260722090354.94245-1-alexandru.elisei@arm.com Signed-off-by: Marc Zyngier --- Documentation/virt/kvm/api.rst | 6 ++++++ arch/arm64/kvm/arm.c | 25 +++++++++++++++++++------ arch/arm64/kvm/mmu.c | 4 ++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index a5f9ee92f43e..e3003a241d5b 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -8414,6 +8414,12 @@ When this capability is enabled all memory in memslots must be mapped as attempts to create a memslot with an invalid mmap will result in an -EINVAL return. +``guest_memfd``, even though it is an anonymous file, is not supported with MTE. +Attempting to create a memslot backed by ``guest_memfd`` when the MTE capability +is enabled, or attempting to enable the MTE capability after +``guest_memfd``-backed memslots have been created, will result in an -EINVAL +return. + When enabled the VMM may make use of the ``KVM_ARM_MTE_COPY_TAGS`` ioctl to perform a bulk copy of tags to/from the guest. diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 50adfff75be8..9a6c72a18672 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -149,14 +149,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, set_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER, &kvm->arch.flags); break; - case KVM_CAP_ARM_MTE: - mutex_lock(&kvm->lock); - if (system_supports_mte() && !kvm->created_vcpus) { - r = 0; - set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); + case KVM_CAP_ARM_MTE: { + struct kvm_memory_slot *memslot; + int bkt; + + guard(mutex)(&kvm->lock); + if (!system_supports_mte() || kvm->created_vcpus) + break; + + r = 0; + guard(mutex)(&kvm->slots_lock); + kvm_for_each_memslot(memslot, bkt, kvm_memslots(kvm)) { + if (kvm_slot_has_gmem(memslot)) { + r = -EINVAL; + break; + } } - mutex_unlock(&kvm->lock); + if (r == 0) + set_bit(KVM_ARCH_FLAG_MTE_ENABLED, &kvm->arch.flags); break; + + } case KVM_CAP_ARM_SYSTEM_SUSPEND: r = 0; set_bit(KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED, &kvm->arch.flags); diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 6c941aaa10c6..2d95203386ba 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -2652,6 +2652,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, if (kvm_slot_has_gmem(new) && !kvm_memslot_is_gmem_only(new)) return -EINVAL; + /* guest_memfd is incompatible with MTE. */ + if (kvm_slot_has_gmem(new) && kvm_has_mte(kvm)) + return -EINVAL; + hva = new->userspace_addr; reg_end = hva + (new->npages << PAGE_SHIFT); -- cgit v1.2.3 From 88c26515313169806a412a362b32a1eca53d21bd Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 14 Jul 2026 16:23:21 +0800 Subject: iomap: correct the range of a partial dirty clear The block range calculation in ifs_clear_range_dirty() is incorrect when partially clearing a range in a folio. We cannot clear the dirty bit of the first block or the last block if the start or end offset is not blocksize-aligned. This has not yet caused any issues since we always clear a whole folio in iomap_writeback_folio(). Fix this by rounding up the first block to blocksize alignment, and calculate the last block by rounding down (using truncation). Correct the nr_blks calculation accordingly. Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance") Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260714082325.325163-2-yi.zhang@huaweicloud.com Reviewed-by: Joanne Koong Reviewed-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/buffered-io.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 276720bc18dc..238b8b1dea91 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -177,13 +177,17 @@ static void ifs_clear_range_dirty(struct folio *folio, { struct inode *inode = folio->mapping->host; unsigned int blks_per_folio = i_blocks_per_folio(inode, folio); - unsigned int first_blk = (off >> inode->i_blkbits); - unsigned int last_blk = (off + len - 1) >> inode->i_blkbits; - unsigned int nr_blks = last_blk - first_blk + 1; + unsigned int first_blk = round_up(off, i_blocksize(inode)) >> + inode->i_blkbits; + unsigned int last_blk = (off + len) >> inode->i_blkbits; unsigned long flags; + if (first_blk >= last_blk) + return; + spin_lock_irqsave(&ifs->state_lock, flags); - bitmap_clear(ifs->state, first_blk + blks_per_folio, nr_blks); + bitmap_clear(ifs->state, first_blk + blks_per_folio, + last_blk - first_blk); spin_unlock_irqrestore(&ifs->state_lock, flags); } -- cgit v1.2.3 From 562d192c43459d70d955775e8a17eebd995539d4 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 14 Jul 2026 16:23:22 +0800 Subject: iomap: support invalidating partial folios Current iomap_invalidate_folio() can only invalidate an entire folio. If we truncate a partial folio on a filesystem where the block size is smaller than the folio size, it will leave behind dirty bits for the truncated or punched blocks. During the write-back process, it will attempt to map the invalid hole range. Fortunately, this has not caused any real problems so far because the ->writeback_range() function corrects the length. However, the implementation of FALLOC_FL_ZERO_RANGE in ext4 depends on the support for invalidating partial folios. When ext4 partially zeroes out a dirty and unwritten folio, it does not perform a flush first like XFS. Therefore, if the dirty bits of the corresponding area cannot be cleared, the zeroed area after writeback remains in the written state rather than reverting to the unwritten state. Fix this by supporting invalidation of partial folios. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260714082325.325163-3-yi.zhang@huaweicloud.com Reviewed-by: "Darrick J. Wong" Reviewed-by: Joanne Koong Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/buffered-io.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 238b8b1dea91..b482e112321f 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -815,6 +815,8 @@ void iomap_invalidate_folio(struct folio *folio, size_t offset, size_t len) WARN_ON_ONCE(folio_test_writeback(folio)); folio_cancel_dirty(folio); ifs_free(folio); + } else { + iomap_clear_range_dirty(folio, offset, len); } } EXPORT_SYMBOL_GPL(iomap_invalidate_folio); -- cgit v1.2.3 From 7a6fd6b21d7e1737b40de1a210acf9e6a1e4d59e Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 14 Jul 2026 16:23:23 +0800 Subject: iomap: fix incorrect did_zero setting in iomap_zero_iter() The did_zero output parameter was unconditionally set after the loop, which is incorrect. It should only be set when the zeroing operation actually completes, not when IOMAP_F_STALE is set or when IOMAP_F_FOLIO_BATCH is set but !folio causes the loop to break early, or when iomap_iter_advance() returns an error. This causes did_zero to be incorrectly set when zeroing a clean unwritten extent because the loop exits early without actually zeroing any data. Fix it by using a local variable to track whether any folio was actually zeroed, and only set did_zero after the loop if zeroing happened. Fixes: 98eb8d95025b ("iomap: set did_zero to true when zeroing successfully") Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260714082325.325163-4-yi.zhang@huaweicloud.com Reviewed-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/buffered-io.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index b482e112321f..0cf62e516827 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1625,6 +1625,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero, const struct iomap_write_ops *write_ops) { u64 bytes = iomap_length(iter); + bool zeroed = false; int status; do { @@ -1645,6 +1646,8 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero, /* a NULL folio means we're done with a folio batch */ if (!folio) { status = iomap_iter_advance_full(iter); + if (status) + return status; break; } @@ -1655,6 +1658,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero, bytes); folio_zero_range(folio, offset, bytes); + zeroed = true; folio_mark_accessed(folio); ret = iomap_write_end(iter, bytes, bytes, folio); @@ -1664,10 +1668,10 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero, status = iomap_iter_advance(iter, bytes); if (status) - break; + return status; } while ((bytes = iomap_length(iter)) > 0); - if (did_zero) + if (did_zero && zeroed) *did_zero = true; return status; } -- cgit v1.2.3 From 9c7d8f7c8994c790fca501dc45ce66e7356cbe05 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 14 Jul 2026 16:23:24 +0800 Subject: iomap: fix out-of-bounds bitmap_set() with zero-length range ifs_set_range_dirty() and ifs_set_range_uptodate() compute last_blk as (off + len - 1) >> i_blkbits. When off is 0 and len is 0, the unsigned subtraction underflows to SIZE_MAX, producing a huge last_blk and nr_blks value that causes bitmap_set() to write far beyond the ifs->state allocation. Regarding ifs_set_range_uptodate(), it is temporarily safe because len cannot be passed in as 0. However, for ifs_set_range_dirty() this is reachable from __iomap_write_end(): when copy_folio_from_iter_atomic() returns 0 (e.g. user buffer fault) and the folio is already uptodate, the guard at the top of __iomap_write_end() does not trigger because !folio_test_uptodate() is false, and iomap_set_range_dirty() is called with copied == 0. Add a !len guard to both functions before the computation, so that a zero-length range is a no-op. Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance") Cc: stable@vger.kernel.org # v6.6 Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260714082325.325163-5-yi.zhang@huaweicloud.com Reviewed-by: Joanne Koong Reviewed-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/buffered-io.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 0cf62e516827..3a3ac3051fb0 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -68,11 +68,13 @@ static bool ifs_set_range_uptodate(struct folio *folio, struct iomap_folio_state *ifs, size_t off, size_t len) { struct inode *inode = folio->mapping->host; - unsigned int first_blk = off >> inode->i_blkbits; - unsigned int last_blk = (off + len - 1) >> inode->i_blkbits; - unsigned int nr_blks = last_blk - first_blk + 1; + unsigned int first_blk, last_blk; - bitmap_set(ifs->state, first_blk, nr_blks); + if (len) { + first_blk = off >> inode->i_blkbits; + last_blk = (off + len - 1) >> inode->i_blkbits; + bitmap_set(ifs->state, first_blk, last_blk - first_blk + 1); + } return ifs_is_fully_uptodate(folio, ifs); } @@ -204,13 +206,17 @@ static void ifs_set_range_dirty(struct folio *folio, { struct inode *inode = folio->mapping->host; unsigned int blks_per_folio = i_blocks_per_folio(inode, folio); - unsigned int first_blk = (off >> inode->i_blkbits); - unsigned int last_blk = (off + len - 1) >> inode->i_blkbits; - unsigned int nr_blks = last_blk - first_blk + 1; + unsigned int first_blk, last_blk; unsigned long flags; + if (!len) + return; + + first_blk = off >> inode->i_blkbits; + last_blk = (off + len - 1) >> inode->i_blkbits; spin_lock_irqsave(&ifs->state_lock, flags); - bitmap_set(ifs->state, first_blk + blks_per_folio, nr_blks); + bitmap_set(ifs->state, first_blk + blks_per_folio, + last_blk - first_blk + 1); spin_unlock_irqrestore(&ifs->state_lock, flags); } -- cgit v1.2.3 From 09b53b0787ee80b71b1dcceb99d004a33e55b823 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 14 Jul 2026 16:23:25 +0800 Subject: iomap: add comments for ifs_clear/set_range_dirty() The range alignment strategy differs between ifs_clear_range_dirty() and ifs_set_range_dirty(). The former rounds inwards to clear only fully-covered blocks, while the latter rounds outwards to mark any partially-touched block as dirty. Add comments to document this asymmetry in block range calculation. Suggested-by: "Darrick J. Wong" Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260714082325.325163-6-yi.zhang@huaweicloud.com Reviewed-by: Joanne Koong Reviewed-by: Christoph Hellwig Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/buffered-io.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 3a3ac3051fb0..6d9a2efd4bee 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -174,6 +174,13 @@ static unsigned iomap_find_dirty_range(struct folio *folio, u64 *range_start, return range_end - *range_start; } +/* + * Clear the per-block dirty bits for the range [@off, @off + @len) within a + * folio. The range is rounded inwards so that only blocks fully covered by + * the range are cleared. This is required for operations like folio + * invalidation, where we must ensure a block is fully clean before discarding + * it. + */ static void ifs_clear_range_dirty(struct folio *folio, struct iomap_folio_state *ifs, size_t off, size_t len) { @@ -201,6 +208,13 @@ static void iomap_clear_range_dirty(struct folio *folio, size_t off, size_t len) ifs_clear_range_dirty(folio, ifs, off, len); } +/* + * Set the per-block dirty bits for the range [@off, @off + @len) within a + * folio. The range is rounded outwards so that any block partially touched + * by the range is marked dirty. This ensures blocks containing even a + * single dirty byte will be included in subsequent writeback, preventing + * data loss when partial blocks are written. + */ static void ifs_set_range_dirty(struct folio *folio, struct iomap_folio_state *ifs, size_t off, size_t len) { -- cgit v1.2.3 From c97cd6f447d8727af3d457bca3a9283a77dd70f8 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Mon, 13 Jul 2026 15:42:06 +0800 Subject: iomap: prevent ioend merge when io_private differs Different io_private values indicate distinct completion contexts that must not be merged together, as this could leak or corrupt the private data associated with each ioend. Signed-off-by: Zhang Yi Link: https://patch.msgid.link/20260713074206.1768006-1-yi.zhang@huaweicloud.com Reviewed-by: Christoph Hellwig Reviewed-by: Ojaswin Mujoo Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/ioend.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 0565328764c1..30468d51b5ad 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -385,6 +385,8 @@ static bool iomap_ioend_can_merge(struct iomap_ioend *ioend, if (ioend->io_bio.bi_status != next->io_bio.bi_status) return false; + if (ioend->io_private != next->io_private) + return false; if (next->io_flags & IOMAP_IOEND_BOUNDARY) return false; if ((ioend->io_flags & IOMAP_IOEND_NOMERGE_FLAGS) != -- cgit v1.2.3 From 62d9853aa4ce6e9797b6949804891be14b219752 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 13 Jul 2026 16:22:55 +0100 Subject: afs: Fix afs_edit_dir_remove() to get, not find, block 0 Fix afs_edit_dir_remove() to use afs_dir_get_block() to get block 0 rather than afs_dir_find_block() as the latter caches the found block in the afs_dir_iter and may[*] switch out the page it's on if another afs_dir_find_block() is done. This parallels what afs_edit_dir_add() does. [*] There's more than one block per page. Fixes: a5b5beebcf96 ("afs: Use the contained hashtable to search a directory") Closes: https://sashiko.dev/#/patchset/20260706153408.1231650-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/2380759.1783956175@warthog.procyon.org.uk cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/dir_edit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c index fd3aa9f97ce6..3ead36a07048 100644 --- a/fs/afs/dir_edit.c +++ b/fs/afs/dir_edit.c @@ -415,7 +415,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, if (!afs_dir_init_iter(&iter, name)) return; - meta = afs_dir_find_block(&iter, 0); + meta = afs_dir_get_block(&iter, 0); if (!meta) return; -- cgit v1.2.3 From 0ef8faff490be6aa1a1e5dfcb0c8492689e91c0f Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Wed, 15 Jul 2026 03:35:16 -0700 Subject: fs: push nr_cached_objects memcg gating into individual filesystems Commit 0baad6f9b997 ("fs/super: skip non-memcg-aware nr_cached_objects in memcg slab shrink") added a check in fs/super.c that skipped every ->nr_cached_objects() hook whenever the shrinker was invoked for a non-root memcg, on the assumption that none of them honour sc->memcg. That assumption is wrong for XFS, whose inode-reclaim hook is intentionally driven from per-memcg contexts to free memcg-charged slab. Encoding a blanket "never memcg-aware" policy in fs/super.c short-circuits that path. Push the check down into the callbacks whose counters really are irrelevant to per-memcg reclaim - btrfs_nr_cached_objects() and shmem_unused_huge_count() - and drop the fs/super.c gate. Each filesystem can now lift the restriction independently if its counter later grows memcg awareness, without touching fs/super.c. Introduce mem_cgroup_shrink_is_root() in so the callbacks don't open-code "sc->memcg is NULL or root". Fixes: 0baad6f9b997 ("fs/super: skip non-memcg-aware nr_cached_objects in memcg slab shrink") Acked-by: Qi Zheng Reviewed-by: Jan Kara Reviewed-by: Shakeel Butt Signed-off-by: Usama Arif Link: https://patch.msgid.link/20260715103516.2410175-1-usama.arif@linux.dev Acked-by: David Sterba Reviewed-by: Baolin Wang Signed-off-by: Christian Brauner (Amutable) --- fs/btrfs/super.c | 10 ++++++++++ include/linux/memcontrol.h | 21 +++++++++++++++++++++ mm/shmem.c | 10 ++++++++++ 3 files changed, 41 insertions(+) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index a7d804219bec..cc4537435399 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -2434,6 +2435,15 @@ static long btrfs_nr_cached_objects(struct super_block *sb, struct shrink_contro struct btrfs_fs_info *fs_info = btrfs_sb(sb); const s64 nr = percpu_counter_read_positive(&fs_info->evictable_extent_maps); + /* + * The evictable extent map counter is filesystem-global and does not + * honour sc->memcg, so it is only meaningful on the global (kswapd or + * root direct reclaim) shrink path. Skip the per-memcg iterations of + * shrink_slab_memcg() to avoid queueing duplicate global work. + */ + if (!mem_cgroup_shrink_is_root(sc)) + return 0; + trace_btrfs_extent_map_shrinker_count(fs_info, nr); return nr; diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index e1f46a0016fc..5407e4200460 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -520,6 +520,22 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) return (memcg == root_mem_cgroup); } +/** + * mem_cgroup_shrink_is_root - is this a global or root-memcg shrink invocation? + * @sc: shrink_control describing the current shrinker call + * + * Returns true when @sc represents a global reclaim shrink (sc->memcg == NULL) + * or a root-memcg shrink, i.e. not a per-memcg iteration of + * shrink_slab_memcg(). Filesystems whose ->nr_cached_objects()/ + * ->free_cached_objects() implementations operate on filesystem-global state + * and do not honour sc->memcg can use this to early-return 0 in per-memcg + * contexts. + */ +static inline bool mem_cgroup_shrink_is_root(struct shrink_control *sc) +{ + return !sc->memcg || mem_cgroup_is_root(sc->memcg); +} + static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg) { return objcg->is_root; @@ -1071,6 +1087,11 @@ static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) return true; } +static inline bool mem_cgroup_shrink_is_root(struct shrink_control *sc) +{ + return true; +} + static inline bool obj_cgroup_is_root(const struct obj_cgroup *objcg) { return true; diff --git a/mm/shmem.c b/mm/shmem.c index b51f83c970bb..9001aaf3b7b9 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -846,6 +846,16 @@ static long shmem_unused_huge_count(struct super_block *sb, struct shrink_control *sc) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); + + /* + * The per-superblock shrinklist is filesystem-global and does not + * honour sc->memcg, so it is only meaningful on the global (kswapd or + * root direct reclaim) shrink path. Skip the per-memcg iterations of + * shrink_slab_memcg() to avoid queueing duplicate global work. + */ + if (!mem_cgroup_shrink_is_root(sc)) + return 0; + return READ_ONCE(sbinfo->shrinklist_len); } #else /* !CONFIG_TRANSPARENT_HUGEPAGE */ -- cgit v1.2.3 From 9f29cd8a8e7901a2617c8064ce9f50fc67b97cb8 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Tue, 14 Jul 2026 00:15:41 -0400 Subject: tipc: fix u16 MTU truncation in media and bearer MTU validation Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied MTU values but only enforce a minimum bound, not a maximum. When a user sets the MTU to a value exceeding U16_MAX (65535), it passes validation but is silently truncated when assigned to u16 fields l->mtu and l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000) truncate to 0, causing a division by zero in tipc_link_set_queue_limits() which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing values (e.g. 65537-131071) produce small incorrect MTU values, resulting in link malfunction behaviors. Crash stack (triggered as unprivileged user via user namespace): tipc_link_set_queue_limits net/tipc/link.c:2531 tipc_link_create net/tipc/link.c:520 tipc_node_check_dest net/tipc/node.c:1279 tipc_disc_rcv net/tipc/discover.c:252 tipc_rcv net/tipc/node.c:2129 tipc_udp_recv net/tipc/udp_media.c:392 Two independent paths lack the upper bound check: 1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET) 2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET) Fix both by rejecting MTU values above U16_MAX. Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU") Reported-by: AutonomousCodeSecurity@microsoft.com Closes: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com Reviewed-by: Vadim Fedorenko Signed-off-by: Cen Zhang (Microsoft) Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260714041541.307702-1-blbllhy@gmail.com Signed-off-by: Paolo Abeni --- net/tipc/netlink.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 8336a9664703..1307dd1a9613 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = { }; /* Properties valid for media, bearer and link */ +static const struct netlink_range_validation tipc_nl_mtu_range = { + .max = U16_MAX, +}; + const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = { [TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC }, [TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 }, [TIPC_NLA_PROP_TOL] = { .type = NLA_U32 }, [TIPC_NLA_PROP_WIN] = { .type = NLA_U32 }, - [TIPC_NLA_PROP_MTU] = { .type = NLA_U32 }, + [TIPC_NLA_PROP_MTU] = NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range), [TIPC_NLA_PROP_BROADCAST] = { .type = NLA_U32 }, [TIPC_NLA_PROP_BROADCAST_RATIO] = { .type = NLA_U32 } }; -- cgit v1.2.3 From b04a248cfa6cfa1e7dc9ce91cb1eb88b1a70dd69 Mon Sep 17 00:00:00 2001 From: José Expósito Date: Fri, 3 Jul 2026 17:07:43 +0200 Subject: drm/tests: shmem: Set DMA mask to 64-bit in drm_gem_shmem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_gem_shmem_test_purge [1] and drm_gem_shmem_test_get_pages_sgt [2] intermittently fail on ppc64le and s390x CI systems with a DMA address overflow: DMA addr 0x0000000100307000+4096 overflow (mask ffffffff, bus limit 0) WARNING: kernel/dma/direct.h:114 dma_direct_map_sg+0x778/0x920 drm_gem_shmem_test_purge: ASSERTION FAILED at drivers/gpu/drm/tests/drm_gem_shmem_test.c:330 Expected sgt is not error, but is: -5 The call chain leading to the failure is: drm_gem_shmem_test_purge() / drm_gem_shmem_test_get_pages_sgt() drm_gem_shmem_get_pages_sgt() drm_gem_shmem_get_pages_sgt_locked() [drm_gem_shmem_helper.c] dma_map_sgtable() [mapping.c] __dma_map_sg_attrs() dma_direct_map_sg() [direct.c] dma_direct_map_phys() [kernel/dma/direct.h] dma_capable() Checks addr against DMA mask -> FAILS: addr > 0xFFFFFFFF The root cause is that KUnit devices are initialized with a 32-bit DMA mask (DMA_BIT_MASK(32)) in lib/kunit/device.c. On ppc64le and s390x systems with physical memory above 4GB, page allocations can land at addresses that exceed this mask. When drm_gem_shmem_get_pages_sgt() attempts to DMA-map these pages via dma_map_sgtable(), the DMA layer rejects the mapping because the physical address overflows the 32-bit mask. The failure is intermittent because pages may or may not be allocated above 4GB on any given run depend on memory pressure. Fix by setting a 64-bit DMA mask on the device before calling drm_gem_shmem_get_pages_sgt() for all tests, following the same pattern already used in drm_gem_shmem_test_obj_create_private(). [1] https://s3.amazonaws.com/arr-cki-prod-trusted-artifacts/trusted-artifacts/2643976103/test_s390x/15128551935/artifacts/jobwatch/logs/recipes/21561049/tasks/220716793/results/1014626315/logs/dmesg.log [2] https://s3.amazonaws.com/arr-cki-prod-trusted-artifacts/trusted-artifacts/2643976103/test_ppc64le/15128551933/artifacts/jobwatch/logs/recipes/21561041/tasks/220716705/results/1014628163/logs/dmesg.log Fixes: 93032ae634d4 ("drm/test: add a test suite for GEM objects backed by shmem") Closes: https://datawarehouse.cki-project.org/issue/5345 Closes: https://datawarehouse.cki-project.org/issue/3184 Assisted-by: Claude:claude-4.6-opus Reviewed-by: Thomas Zimmermann Signed-off-by: José Expósito Link: https://patch.msgid.link/20260703150808.3832-1-jose.exposito89@gmail.com --- drivers/gpu/drm/tests/drm_gem_shmem_test.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tests/drm_gem_shmem_test.c b/drivers/gpu/drm/tests/drm_gem_shmem_test.c index 44a190109249..5e69ff1d1ad6 100644 --- a/drivers/gpu/drm/tests/drm_gem_shmem_test.c +++ b/drivers/gpu/drm/tests/drm_gem_shmem_test.c @@ -95,13 +95,9 @@ static void drm_gem_shmem_test_obj_create_private(struct kunit *test) sg_init_one(sgt->sgl, buf, TEST_SIZE); /* - * Set the DMA mask to 64-bits and map the sgtables - * otherwise drm_gem_shmem_free will cause a warning - * on debug kernels. + * Map the sgtables otherwise drm_gem_shmem_free will cause a warning on + * debug kernels. */ - ret = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(64)); - KUNIT_ASSERT_EQ(test, ret, 0); - ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0); KUNIT_ASSERT_EQ(test, ret, 0); @@ -352,11 +348,19 @@ static int drm_gem_shmem_test_init(struct kunit *test) { struct device *dev; struct drm_device *drm_dev; + int ret; /* Allocate a parent device */ dev = drm_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + /* + * Set the DMA mask to 64-bits to avoid intermittent failures calling + * drm_gem_shmem_get_pages_sgt(). + */ + ret = dma_set_mask(dev, DMA_BIT_MASK(64)); + KUNIT_ASSERT_EQ(test, ret, 0); + /* * The DRM core will automatically initialize the GEM core and create * a DRM Memory Manager object which provides an address space pool -- cgit v1.2.3 From 5499e0602d2faafd42c580d25f615903c3fbe11b Mon Sep 17 00:00:00 2001 From: David Lee Date: Mon, 13 Jul 2026 10:47:50 +0000 Subject: net/x25: fix use-after-free in x25_kill_by_neigh() x25_kill_by_neigh() walks the global X.25 socket list looking for sockets attached to a terminating neighbour. x25_list_lock protects list membership while the lookup is in progress, but it does not pin a socket's lifetime after the lock is dropped. The function currently drops x25_list_lock before calling lock_sock(s). A concurrent close can run x25_release(), remove the same socket from x25_list, and drop the last socket reference in that window. The neighbour teardown path can then lock or inspect a freed struct sock/struct x25_sock. Take sock_hold(s) while x25_list_lock still proves that the list entry is live, then drop the temporary reference after the socket has been locked, rechecked, and released. Recheck x25_sk(s)->neighbour after lock_sock(), because another path may have disconnected the socket before this path acquired the socket lock. Restart the list walk after each disconnect because the list lock was dropped and the previous iterator state may no longer be valid. A QEMU/KASAN run against origin/master reproduced a slab-use-after-free in x25_kill_by_neigh(). Fixes: 7781607938c8 ("net/x25: Fix null-ptr-deref caused by x25_disconnect") Cc: stable@vger.kernel.org Signed-off-by: David Lee Assisted-by: Codex:gpt-5.5 Acked-by: Martin Schiller Link: https://patch.msgid.link/20260713104752.241175-1-david.lee@trailofbits.com Signed-off-by: Paolo Abeni --- net/x25/af_x25.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index c31d2af5dd22..8aae9273b7c1 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1768,15 +1768,19 @@ void x25_kill_by_neigh(struct x25_neigh *nb) { struct sock *s; +again: write_lock_bh(&x25_list_lock); sk_for_each(s, &x25_list) { if (x25_sk(s)->neighbour == nb) { + sock_hold(s); write_unlock_bh(&x25_list_lock); lock_sock(s); - x25_disconnect(s, ENETUNREACH, 0, 0); + if (x25_sk(s)->neighbour == nb) + x25_disconnect(s, ENETUNREACH, 0, 0); release_sock(s); - write_lock_bh(&x25_list_lock); + sock_put(s); + goto again; } } write_unlock_bh(&x25_list_lock); -- cgit v1.2.3 From 980a813452754f8001704744e92f7aa697c53dd3 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Mon, 13 Jul 2026 23:32:30 +0000 Subject: bpf: tcp: fix double sock release on batch realloc bpf_iter_tcp_batch() releases the current batch via bpf_iter_tcp_put_batch(), which drops the socket refs and rewrites each slot with the socket cookie, then grows the batch. cur_sk/end_sk are kept for bpf_iter_tcp_resume(), but on realloc failure the function returns ERR_PTR() before resume runs, leaving cur_sk < end_sk over slots that now hold cookies rather than sock pointers. bpf_iter_tcp_seq_stop() then calls bpf_iter_tcp_put_batch() again and dereferences a cookie as a struct sock. Empty the batch on the failure path so stop() does not release it again. The sockets were already freed by the first bpf_iter_tcp_put_batch(), so nothing leaks, and a later read() rescans the bucket from the start instead of skipping it. The sibling GFP_NOWAIT failure path still holds real socket references and is left for stop() to release. BUG: KASAN: null-ptr-deref in __sock_gen_cookie Read of size 8 at addr 0000000000000059 by task exploit ... __sock_gen_cookie (net/core/sock_diag.c:28) bpf_iter_tcp_put_batch (net/ipv4/tcp_ipv4.c:2918) bpf_iter_tcp_seq_stop (net/ipv4/tcp_ipv4.c:3270) bpf_seq_read (kernel/bpf/bpf_iter.c:205) vfs_read (fs/read_write.c:572) ksys_read (fs/read_write.c:716) do_syscall_64 entry_SYSCALL_64_after_hwframe Kernel panic - not syncing: Fatal exception Fixes: cdec67a489d4 ("bpf: tcp: Make sure iter->batch always contains a full bucket snapshot") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Eric Dumazet Reviewed-by: Jordan Rife Link: https://patch.msgid.link/20260713233230.3553593-1-xmei5@asu.edu Signed-off-by: Paolo Abeni --- net/ipv4/tcp_ipv4.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index aada52769057..b8887cdd66c5 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -3146,8 +3146,11 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) bpf_iter_tcp_put_batch(iter); err = bpf_iter_tcp_realloc_batch(iter, expected * 3 / 2, GFP_USER); - if (err) + if (err) { + iter->cur_sk = 0; + iter->end_sk = 0; return ERR_PTR(err); + } sk = bpf_iter_tcp_resume(seq); if (!sk) -- cgit v1.2.3 From 9fcf274d93af17396f20cccb63f1d4c17492a000 Mon Sep 17 00:00:00 2001 From: Nazim Amirul Date: Mon, 13 Jul 2026 19:37:14 -0700 Subject: net: stmmac: xgmac: fix l4 filter port overwrite on register update The XGMAC_L4_ADDR register holds both source and destination port match values. The current implementation overwrites the entire register when configuring either port, so setting one silently erases the other. Fix this by reading the register first, then masking and updating only the relevant field before writing back. Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower") Signed-off-by: Rohan G Thomas Signed-off-by: Nazim Amirul Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260714023716.29865-3-muhammad.nazim.amirul.nazle.asmade@altera.com Reviewed-by: Jakub Raczynski Signed-off-by: Paolo Abeni --- .../net/ethernet/stmicro/stmmac/dwxgmac2_core.c | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index f02b434bbd50..52054f31376d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -1370,36 +1370,40 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no, value &= ~XGMAC_L4PEN0; } - value &= ~(XGMAC_L4SPM0 | XGMAC_L4SPIM0); - value &= ~(XGMAC_L4DPM0 | XGMAC_L4DPIM0); if (sa) { value |= XGMAC_L4SPM0; if (inv) value |= XGMAC_L4SPIM0; + else + value &= ~XGMAC_L4SPIM0; } else { value |= XGMAC_L4DPM0; if (inv) value |= XGMAC_L4DPIM0; + else + value &= ~XGMAC_L4DPIM0; } ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, value); if (ret) return ret; - if (sa) { - value = FIELD_PREP(XGMAC_L4SP0, match); + ret = dwxgmac2_filter_read(hw, filter_no, XGMAC_L4_ADDR, &value); + if (ret) + return ret; - ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); - if (ret) - return ret; + if (sa) { + value &= ~XGMAC_L4SP0; + value |= FIELD_PREP(XGMAC_L4SP0, match); } else { - value = FIELD_PREP(XGMAC_L4DP0, match); - - ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); - if (ret) - return ret; + value &= ~XGMAC_L4DP0; + value |= FIELD_PREP(XGMAC_L4DP0, match); } + ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value); + if (ret) + return ret; + if (!en) return dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, 0); -- cgit v1.2.3 From 5536d7c843637e9430279b94935fcf7df98babb3 Mon Sep 17 00:00:00 2001 From: Nazim Amirul Date: Mon, 13 Jul 2026 19:37:15 -0700 Subject: net: stmmac: fix l3l4 filter rejecting unsupported offload requests The basic flow parser in tc_add_basic_flow() does not validate match keys before proceeding. Unsupported offload configurations such as partial protocol masks, non-IPv4 network proto, or non-TCP/UDP transport proto are silently accepted instead of returning -EOPNOTSUPP. Add validation to return -EOPNOTSUPP early for: - No network or transport proto present in the key - Partial protocol mask (only full mask supported) - Network proto is not IPv4 - Transport proto is not TCP or UDP Each rejection includes an extack message so the user knows which part of the match is unsupported. Also propagate -EOPNOTSUPP from tc_add_basic_flow() in tc_add_flow() by returning it directly rather than using break. The break was silently discarding the error for FLOW_CLS_REPLACE operations where entry->in_use is already true, causing tc_add_flow() to return 0 (success) for unsupported replace requests. Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower") Signed-off-by: Rohan G Thomas Signed-off-by: Nazim Amirul Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260714023716.29865-4-muhammad.nazim.amirul.nazle.asmade@altera.com Reviewed-by: Jakub Raczynski Signed-off-by: Paolo Abeni --- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index d78652718599..1f8c9f47306b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -446,6 +446,7 @@ static int tc_parse_flow_actions(struct stmmac_priv *priv, } #define ETHER_TYPE_FULL_MASK cpu_to_be16(~0) +#define IP_PROTO_FULL_MASK 0xFF static int tc_add_basic_flow(struct stmmac_priv *priv, struct flow_cls_offload *cls, @@ -461,6 +462,37 @@ static int tc_add_basic_flow(struct stmmac_priv *priv, flow_rule_match_basic(rule, &match); + /* Both network proto and transport proto not present in the key */ + if (!match.mask || !(match.mask->n_proto || match.mask->ip_proto)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "filter must specify network or transport protocol"); + return -EOPNOTSUPP; + } + + /* If the proto is present in the key and is not full mask */ + if ((match.mask->n_proto && match.mask->n_proto != ETHER_TYPE_FULL_MASK) || + (match.mask->ip_proto && match.mask->ip_proto != IP_PROTO_FULL_MASK)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only full protocol mask is supported"); + return -EOPNOTSUPP; + } + + /* Network proto is present in the key and is not IPv4 */ + if (match.mask->n_proto && match.key->n_proto != cpu_to_be16(ETH_P_IP)) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only IPv4 network protocol is supported"); + return -EOPNOTSUPP; + } + + /* Transport proto is present in the key and is not TCP or UDP */ + if (match.mask->ip_proto && + match.key->ip_proto != IPPROTO_TCP && + match.key->ip_proto != IPPROTO_UDP) { + NL_SET_ERR_MSG_MOD(cls->common.extack, + "only TCP and UDP transport protocols are supported"); + return -EOPNOTSUPP; + } + entry->ip_proto = match.key->ip_proto; return 0; } @@ -598,6 +630,8 @@ static int tc_add_flow(struct stmmac_priv *priv, ret = tc_flow_parsers[i].fn(priv, cls, entry); if (!ret) entry->in_use = true; + else if (ret == -EOPNOTSUPP) + return ret; } if (!entry->in_use) -- cgit v1.2.3 From a448f821289934b961dd9d8d0beb006cc8937ba2 Mon Sep 17 00:00:00 2001 From: Nazim Amirul Date: Mon, 13 Jul 2026 19:37:16 -0700 Subject: net: stmmac: reset residual action in L3L4 filters on delete When deleting an L3/L4 flower filter entry, the action field is not reset. If a filter was previously configured with a drop action, that action may persist and affect subsequent filter configurations unintentionally. Clear the action field when the filter entry is deleted. Fixes: 425eabddaf0f ("net: stmmac: Implement L3/L4 Filters using TC Flower") Signed-off-by: Rohan G Thomas Signed-off-by: Nazim Amirul Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260714023716.29865-5-muhammad.nazim.amirul.nazle.asmade@altera.com Reviewed-by: Jakub Raczynski Signed-off-by: Paolo Abeni --- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 1f8c9f47306b..14cabe76e53e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -661,6 +661,7 @@ static int tc_del_flow(struct stmmac_priv *priv, entry->in_use = false; entry->cookie = 0; entry->is_l4 = false; + entry->action = 0; return ret; } -- cgit v1.2.3 From 8b7e8245e2293078f657521236ac92c045552e5a Mon Sep 17 00:00:00 2001 From: Guidong Han <2045gemini@gmail.com> Date: Sat, 18 Jul 2026 18:44:06 +0800 Subject: eventpoll: pin files while checking reverse paths Commit 319c15174757 ("epoll: take epitem list out of struct file") intentionally removed temporary file references from the reverse path check list. At the time, both epitems and their files were freed after an RCU grace period, so unlist_file() could obtain file->f_lock through an epitem while clear_tfile_check_list() held rcu_read_lock(). Commit 0ede61d8589c ("file: convert to SLAB_TYPESAFE_BY_RCU") made struct file SLAB_TYPESAFE_BY_RCU and removed its RCU-delayed freeing. RCU still protects the epitem, but no longer keeps the referenced file from being freed and reused. A concurrent close can therefore make unlist_file() lock or unlock f_lock in a recycled file object. This violates the documented SLAB_TYPESAFE_BY_RCU rule requiring a reference before acquiring an object's lock. The race was reproduced, causing a wild unlock of f_lock in a recycled file and breaking its mutual exclusion. Add ->file to epitems_head to remember the pinned file independently of ->epitems. A concurrent EPOLL_CTL_DEL can empty ->epitems before the head is unlisted, leaving no epi->ffd.file from which to drop the reference. In list_file(), acquire the reference before adding the head to the check list. The caller either owns a reference or holds the ep->mtx for the epitem leading to the file. In the latter case, file_ref_get() can fail after the last reference is dropped, but eventpoll_release_file() must acquire the same mutex before the file can be freed. The dying leaf can be skipped because removing links cannot increase the reverse path count. In unlist_file(), epnested_mutex excludes another list_file() or unlist_file(), while head->next prevents a concurrent EPOLL_CTL_DEL from freeing the head. Save head->file locally, clear it with head->next under f_lock, and drop the reference after the RCU-protected operation. Christian Brauner quotes: > SLAB_TYPESAFE_BY_RCU allows a slab slot to be reused while an RCU reader > still holds its old address. Once that address contains a new live > struct file, KASAN sees valid, unpoisoned memory and cannot distinguish > the stale object identity. CONFIG_DEBUG_SPINLOCK exposes the failure > instead. > > The failing interleaving is: > > CPU0: nested EPOLL_CTL_ADD CPU1: close/open churn > ------------------------------------ --------------------------------- > p = hlist_first_rcu(&head->epitems) > epi = container_of(p, ...) > close(victim) > __fput() > eventpoll_release_file() > file_free(victim) > // the slot is free; f_lock remains > spin_lock(&epi->ffd.file->f_lock) > open() reuses the slot as new_file > spin_lock_init(&new_file->f_lock) > spin_unlock(&epi->ffd.file->f_lock) // wild unlock of new_file's lock > > CONFIG_DEBUG_SPINLOCK reports: > > BUG: spinlock already unlocked on CPU#0, poc_unlist/150 > lock: 0xffff8880067fb200, .magic: dead4ead, .owner: /-1, .owner_cpu: -1 > CPU: 0 UID: 1000 PID: 150 Comm: poc_unlist Not tainted 7.2.0-rc3-dirty #22 PREEMPTLAZY > 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+0x64/0x80 > do_raw_spin_unlock+0x75/0xb0 > _raw_spin_unlock+0xe/0x30 > clear_tfile_check_list+0x88/0xe0 > do_epoll_ctl_file+0x519/0xcf0 > ? __pfx_ep_ptable_queue_proc+0x10/0x10 > do_epoll_ctl+0x8f/0x100 > __x64_sys_epoll_ctl+0x6f/0xa0 > do_syscall_64+0xdc/0x520 > ? srso_alias_return_thunk+0x5/0xfbef5 > entry_SYSCALL_64_after_hwframe+0x76/0x7e > RIP: 0033:0x42034e > Code: 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 e9 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48 > RSP: 002b:00007a657ff3c198 EFLAGS: 00000202 ORIG_RAX: 00000000000000e9 > RAX: ffffffffffffffda RBX: 00007a657ff3ccdc RCX: 000000000042034e > RDX: 0000000000000003 RSI: 0000000000000001 RDI: 0000000000000004 > RBP: 00007a657ff3c2f0 R08: 0000000000000000 R09: 00007a657ff3c6c0 > R10: 00007a657ff3c1a4 R11: 0000000000000202 R12: 00007a657ff3c6c0 > R13: ffffffffffffffb8 R14: 000000000000000d R15: 00007fffb7de0210 > > ------------[ cut here ]------------ > > unlist_file() does not appear as a separate frame because it was inlined > into clear_tfile_check_list(). This report was obtained with mdelay() > instrumentation immediately before spin_lock() and spin_unlock() in > unlist_file() to widen the two race windows. > > More importantly, this is a wild unlock. The stale unlock can target > f_lock of a different live file and invalidate mutual exclusion for > state protected by that lock. Turning this into a reliable exploit > would require precise scheduling and same-slot reuse and is likely > difficult, but the primitive is potentially exploitable. Reported-by: Qi Tang Reported-by: Junxi Qian Fixes: 0ede61d8589c ("file: convert to SLAB_TYPESAFE_BY_RCU") Cc: stable@vger.kernel.org Signed-off-by: Guidong Han <2045gemini@gmail.com> Link: https://patch.msgid.link/20260718104406.27897-1-2045gemini@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/eventpoll.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 0e65c7431dfc..eed8cecd94e3 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -459,11 +459,14 @@ static struct kmem_cache *pwq_cache __ro_after_init; * Wrapper anchor for file->f_ep when the watched file is not itself an * eventpoll; for the epoll-watches-epoll case, file->f_ep points at * &watched_ep->refs directly. The ->next field threads - * ctx->tfile_check_list during one EPOLL_CTL_ADD path check. + * ctx->tfile_check_list during one EPOLL_CTL_ADD path check. The ->file + * field holds a reference to the associated file while the head is on + * the list. */ struct epitems_head { struct hlist_head epitems; struct epitems_head *next; + struct file *file; }; static struct kmem_cache *ephead_cache __ro_after_init; @@ -480,6 +483,16 @@ static void list_file(struct file *file, struct ep_ctl_ctx *ctx) head = container_of(file->f_ep, struct epitems_head, epitems); if (!head->next) { + /* + * The caller owns a reference to @file or holds the ep->mtx for the + * epitem that led here. The latter blocks eventpoll_release_file() + * before the file allocation can be freed and reused. A dying leaf + * can be skipped since removing links cannot increase the reverse + * path count. + */ + if (!file_ref_get(&file->f_ref)) + return; + head->file = file; head->next = ctx->tfile_check_list; ctx->tfile_check_list = head; } @@ -489,15 +502,18 @@ static void unlist_file(struct epitems_head *head) { struct epitems_head *to_free = head; struct hlist_node *p = rcu_dereference(hlist_first_rcu(&head->epitems)); + struct file *file = head->file; if (p) { struct epitem *epi= container_of(p, struct epitem, fllink); spin_lock(&epi->ffd.file->f_lock); if (!hlist_empty(&head->epitems)) to_free = NULL; head->next = NULL; + head->file = NULL; spin_unlock(&epi->ffd.file->f_lock); } free_ephead(to_free); + fput(file); } #ifdef CONFIG_SYSCTL -- cgit v1.2.3 From 9c99db3a2080b8c2cbbb1100369586a9bea43321 Mon Sep 17 00:00:00 2001 From: vadik likholetov Date: Mon, 13 Jul 2026 10:49:11 +0300 Subject: net: stmmac: enable the MAC on link up for all supported speeds stmmac_mac_link_down() clears the MAC's transmit and receive enable bits. stmmac_mac_link_up() is expected to set them again through stmmac_mac_set(..., true), but it first switches on the negotiated speed and returns early for a speed the switch does not list. The MAC is then left gated off. The speed selection is split into three switches, keyed on the interface. The generic branch -- taken for everything that is neither USXGMII nor XLGMII, so including PHY_INTERFACE_MODE_10GBASER -- lists only SPEED_2500, SPEED_1000, SPEED_100 and SPEED_10. MGBE on Tegra234 runs 10GBASE-R into an Aquantia AQR113C. That PHY does rate matching, so phylink_link_up() replaces the media speed with the MAC-side interface speed before calling into the MAC: case RATE_MATCH_PAUSE: speed = phylink_interface_max_speed(link_state.interface); duplex = DUPLEX_FULL; The driver is therefore called as stmmac_mac_link_up(interface=10GBASER, speed=10000, duplex=1) which falls through to "default: return;". The interface stops passing traffic after the first link flap. The failure is easy to misread. The link still comes up, because the PHY is polled over MDIO and needs no MAC, so the interface reports carrier 1 at the media speed. The DMA is untouched, so its start bits stay set and descriptors are still consumed. Only the MAC itself is gated off: the receiver counts nothing (mmc_rx_framecount_gb stops advancing, RE is 0) and nothing reaches the wire (TE is 0). The interface survives boot only because stmmac_hw_setup(), called from ndo_open, enables the MAC unconditionally -- so the problem appears only once the cable has been unplugged and plugged back in, and "ip link set dev down && ip link set dev up" appears to fix it. The interface is not what the speed bits depend on: with the single exception of 2.5G, which is selected through the XGMII block on USXGMII and through the regular speed bits otherwise, each speed maps to one field of struct mac_link. The per-interface switches are speed validation, and phylink already validates the speed against priv->hw->link.caps. So collapse the three switches into one keyed on the speed alone, keeping the interface test only for the 2.5G case. This covers 10G on 10GBASE-R, and equally 5G, and 1G/100/10 on USXGMII, all of which hit "default: return;" today. A core that does not support a speed leaves the corresponding mac_link field at 0, and phylink will not offer it that speed in the first place. For dwxgmac2 at 10G, link.xgmii.speed10000 is XGMAC_CONFIG_SS_10000, which is 0 and is the correct speed selection for a 10GBASE-R MAC: ctrl then equals old_ctrl, the register write is skipped, and execution reaches stmmac_mac_set(..., true). Log an error in the default case, since a speed with no entry here leaves the MAC disabled and the symptom does not point at the cause. Fixes: d8ca113724e7 ("net: stmmac: tegra: Add MGBE support") Suggested-by: Maxime Chevallier Signed-off-by: vadik likholetov Reviewed-by: Jacob Keller Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260713074911.30090-1-vadikas@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 92 +++++++++-------------- 1 file changed, 37 insertions(+), 55 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index af29a50ddb89..151c77713025 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1083,63 +1083,45 @@ static void stmmac_mac_link_up(struct phylink_config *config, old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG); ctrl = old_ctrl & ~priv->hw->link.speed_mask; - if (interface == PHY_INTERFACE_MODE_USXGMII) { - switch (speed) { - case SPEED_10000: - ctrl |= priv->hw->link.xgmii.speed10000; - break; - case SPEED_5000: - ctrl |= priv->hw->link.xgmii.speed5000; - break; - case SPEED_2500: + switch (speed) { + case SPEED_100000: + ctrl |= priv->hw->link.xlgmii.speed100000; + break; + case SPEED_50000: + ctrl |= priv->hw->link.xlgmii.speed50000; + break; + case SPEED_40000: + ctrl |= priv->hw->link.xlgmii.speed40000; + break; + case SPEED_25000: + ctrl |= priv->hw->link.xlgmii.speed25000; + break; + case SPEED_10000: + ctrl |= priv->hw->link.xgmii.speed10000; + break; + case SPEED_5000: + ctrl |= priv->hw->link.xgmii.speed5000; + break; + case SPEED_2500: + if (interface == PHY_INTERFACE_MODE_USXGMII) ctrl |= priv->hw->link.xgmii.speed2500; - break; - default: - return; - } - } else if (interface == PHY_INTERFACE_MODE_XLGMII) { - switch (speed) { - case SPEED_100000: - ctrl |= priv->hw->link.xlgmii.speed100000; - break; - case SPEED_50000: - ctrl |= priv->hw->link.xlgmii.speed50000; - break; - case SPEED_40000: - ctrl |= priv->hw->link.xlgmii.speed40000; - break; - case SPEED_25000: - ctrl |= priv->hw->link.xlgmii.speed25000; - break; - case SPEED_10000: - ctrl |= priv->hw->link.xgmii.speed10000; - break; - case SPEED_2500: - ctrl |= priv->hw->link.speed2500; - break; - case SPEED_1000: - ctrl |= priv->hw->link.speed1000; - break; - default: - return; - } - } else { - switch (speed) { - case SPEED_2500: + else ctrl |= priv->hw->link.speed2500; - break; - case SPEED_1000: - ctrl |= priv->hw->link.speed1000; - break; - case SPEED_100: - ctrl |= priv->hw->link.speed100; - break; - case SPEED_10: - ctrl |= priv->hw->link.speed10; - break; - default: - return; - } + break; + case SPEED_1000: + ctrl |= priv->hw->link.speed1000; + break; + case SPEED_100: + ctrl |= priv->hw->link.speed100; + break; + case SPEED_10: + ctrl |= priv->hw->link.speed10; + break; + default: + netdev_err(priv->dev, + "unsupported speed %s on %s, leaving the MAC disabled\n", + phy_speed_to_str(speed), phy_modes(interface)); + return; } if (priv->plat->fix_mac_speed) -- cgit v1.2.3 From ba0533fc163f905fe817cfabdf8ed4058da44800 Mon Sep 17 00:00:00 2001 From: Daehyeon Ko <4ncienth@gmail.com> Date: Tue, 14 Jul 2026 22:19:39 +0900 Subject: tipc: clear sock->sk on the failed-insert path in tipc_sk_create() When tipc_sk_create() fails to insert the new socket (tipc_sk_insert() returns non-zero), its error path frees the sk with sk_free() but leaves sock->sk pointing at the freed object: if (tipc_sk_insert(tsk)) { sk_free(sk); pr_warn("Socket create failed; port number exhausted\n"); return -EINVAL; } This is harmless for plain socket(): the syscall layer clears sock->ops before releasing, so tipc_release() is never called. It is not harmless on the accept() path. tipc_accept() creates the pre-allocated child socket with tipc_sk_create(net, new_sock, 0, kern); on failure it leaves new_sock->sk dangling and new_sock->ops non-NULL, and do_accept() then fput()s the new file, so __sock_release() -> tipc_release() runs lock_sock(new_sock->sk) on the freed sk -- a use-after-free write of the sk_lock spinlock. tipc_release() already guards this exact "failed accept() releases a pre-allocated child" case with "if (sk == NULL) return 0;", but the guard is bypassed because tipc_sk_create() left sock->sk non-NULL (dangling) rather than NULL. Clear sock->sk on the failed-insert path so the existing tipc_release() NULL check fires and the use-after-free is avoided. The tipc_sk_insert() failure is reached when the per-netns socket rhashtable hits its max_size (tsk_rht_params.max_size = 1048576, ~2M elements) -- i.e. once a netns holds ~2M TIPC sockets every insert returns -E2BIG. BUG: KASAN: slab-use-after-free in lock_sock_nested (net/core/sock.c:3839) Write of size 8 at addr ffff8880047cdc38 by task init/1 lock_sock_nested (net/core/sock.c:3839) tipc_release (net/tipc/socket.c:638) __sock_release (net/socket.c:710) sock_close (net/socket.c:1501) __fput (fs/file_table.c:512) Allocated by task 1: sk_alloc (net/core/sock.c:2308) tipc_sk_create (net/tipc/socket.c:487) tipc_accept (net/tipc/socket.c:2744) do_accept (net/socket.c:2034) Freed by task 1: __sk_destruct (net/core/sock.c:2391) tipc_sk_create (net/tipc/socket.c:504) tipc_accept (net/tipc/socket.c:2744) do_accept (net/socket.c:2034) Fixes: 00aff3590fc0 ("net: tipc: fix possible refcount leak in tipc_sk_create()") Cc: stable@vger.kernel.org Reviewed-by: Tung Nguyen Reviewed-by: Breno Leitao Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260714131939.1255974-1-4ncienth@gmail.com Signed-off-by: Paolo Abeni --- net/tipc/socket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index e564341e0216..55e695748332 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -502,6 +502,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, tipc_set_sk_state(sk, TIPC_OPEN); if (tipc_sk_insert(tsk)) { sk_free(sk); + sock->sk = NULL; pr_warn("Socket create failed; port number exhausted\n"); return -EINVAL; } -- cgit v1.2.3 From 675ed582c1aa4d919dd535490de08c015005c653 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Mon, 13 Jul 2026 23:09:45 +0800 Subject: net: gre: fix lltx regression for GRE tunnels with SEQ/CSUM Before commit 00d066a4d4ed ("netdev_features: convert NETIF_F_LLTX to dev->lltx"), NETIF_F_LLTX was set unconditionally in both __gre_tunnel_init() and ip6gre_tnl_init_features() alongside GRE_FEATURES: dev->features |= GRE_FEATURES | NETIF_F_LLTX; When that commit converted NETIF_F_LLTX to the dev->lltx flag, it placed 'dev->lltx = true' after the SEQ/CSUM early returns instead of before them. This causes GRE/GRETAP/ip6gre tunnels with SEQ or CSUM+encap to lose lockless TX, reintroducing _xmit_lock acquisition around their ndo_start_xmit. Since GRE xmit re-enters the stack via ip_tunnel_xmit(), holding _xmit_lock risks ABBA deadlock with the underlay device. CPU0 CPU1 ---- ---- lock(&qdisc_xmit_lock_key#6); lock(&qdisc_xmit_lock_key#3); lock(&qdisc_xmit_lock_key#6); lock(&qdisc_xmit_lock_key#3); Fix by moving dev->lltx = true before the early returns in both functions, restoring the original unconditional behavior. Fixes: 00d066a4d4ed ("netdev_features: convert NETIF_F_LLTX to dev->lltx") Signed-off-by: Yun Zhou Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260713150945.1779628-1-yun.zhou@windriver.com Signed-off-by: Paolo Abeni --- net/ipv4/ip_gre.c | 4 ++-- net/ipv6/ip6_gre.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 3efdfb4ffa21..0ba1e94e9012 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1018,6 +1018,8 @@ static void __gre_tunnel_init(struct net_device *dev) dev->features |= GRE_FEATURES; dev->hw_features |= GRE_FEATURES; + dev->lltx = true; + /* TCP offload with GRE SEQ is not supported, nor can we support 2 * levels of outer headers requiring an update. */ @@ -1029,8 +1031,6 @@ static void __gre_tunnel_init(struct net_device *dev) dev->features |= NETIF_F_GSO_SOFTWARE; dev->hw_features |= NETIF_F_GSO_SOFTWARE; - - dev->lltx = true; } static int ipgre_tunnel_init(struct net_device *dev) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 7c09a269b352..b843116e9b70 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1455,6 +1455,8 @@ static void ip6gre_tnl_init_features(struct net_device *dev) dev->features |= GRE6_FEATURES; dev->hw_features |= GRE6_FEATURES; + dev->lltx = true; + /* TCP offload with GRE SEQ is not supported, nor can we support 2 * levels of outer headers requiring an update. */ @@ -1466,8 +1468,6 @@ static void ip6gre_tnl_init_features(struct net_device *dev) dev->features |= NETIF_F_GSO_SOFTWARE; dev->hw_features |= NETIF_F_GSO_SOFTWARE; - - dev->lltx = true; } static int ip6gre_tunnel_init_common(struct net_device *dev) -- cgit v1.2.3 From 3671f0419d90b98a02f313830595ab958c8b2025 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 16 Jul 2026 17:06:07 +0000 Subject: mpls: Set rt->rt_nhn just before returning from mpls_nh_build_multi(). Commit f0914b8436c5 ("mpls: Hold dev refcnt for mpls_nh.") added change_nexthops() loop to call netdev_put() for the nexthop devices before freeing mpls_route. Then, mpls_nh_build_multi() was also changed to avoid iterating uninitialised nexthops in mpls_rt_free_rcu(). However, setting rt->rt_nhn to 0 at the entry of mpls_nh_build_multi() makes the following change_nexthops() no-op. Let's set rt->rt_nhn just before returning from mpls_nh_build_multi(). Fixes: f0914b8436c5 ("mpls: Hold dev refcnt for mpls_nh.") Reported-by: Anthony Doeraene Closes: https://lore.kernel.org/netdev/036a0c95-f5d4-46ab-88e7-1eab567d7a84@uclouvain.be/ Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260716170609.804629-1-kuniyu@google.com Signed-off-by: Paolo Abeni --- net/mpls/af_mpls.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 318cb7e2ac5f..4406c304b639 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -922,8 +922,7 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg, struct nlattr *nla_via, *nla_newdst; int remaining = cfg->rc_mp_len; int err = 0; - - rt->rt_nhn = 0; + u8 nhs = 0; change_nexthops(rt) { int attrlen; @@ -959,12 +958,15 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg, rt->rt_nhn_alive--; rtnh = rtnh_next(rtnh, &remaining); - rt->rt_nhn++; + nhs++; } endfor_nexthops(rt); + rt->rt_nhn = nhs; + return 0; errout: + rt->rt_nhn = nhs; return err; } -- cgit v1.2.3 From a9df7939a2c5e3794769fde809ea892e5a414b93 Mon Sep 17 00:00:00 2001 From: Jorn Baayen Date: Tue, 21 Jul 2026 17:19:23 +0200 Subject: ASoC: amd: yc: Add DMI quirk for Acer Aspire AG14-22P The Acer Aspire AG14-22P has its internal microphone connected to the ACP as a digital microphone, but the BIOS does not advertise it via the AcpDmicConnected ACPI property, so the internal microphone does not work out of the box. Add a DMI quirk to enable it. Tested on an Aspire AG14-22P (board Dove2_MDU, BIOS V1.03): the acp6x DMIC card is created and the internal microphone captures audio. Assisted-by: Claude-Code:claude-fable-5 Signed-off-by: Jorn Baayen Link: https://patch.msgid.link/20260721151924.30316-1-jhbaayen@fastmail.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 0d93e45b6178..de5c895e9fbd 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -45,6 +45,13 @@ static struct snd_soc_card acp6x_card = { }; static const struct dmi_system_id yc_acp_quirk_table[] = { + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire AG14-22P"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From 0d4d31e3cc5dd6204fa1495c4107f5075acce5ed Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Wed, 15 Jul 2026 10:50:07 +0530 Subject: octeontx2-vf: set TC flower flag on MCAM entry allocation When MCAM entries are allocated for a VF netdev via the devlink mcam_count parameter, only OTX2_FLAG_NTUPLE_SUPPORT was set. That enabled ethtool ntuple filters but not tc flower offload. Also set OTX2_FLAG_TC_FLOWER_SUPPORT when entries are successfully allocated. Fixes: 2da489432747 ("octeontx2-pf: devlink params support to set mcam entry count") Signed-off-by: Suman Ghosh Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260715052007.2099851-1-rkannoth@marvell.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 5dd0591fed99..99d78fc5a2c4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -272,6 +272,7 @@ exit: if (allocated) { pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC; pfvf->flags |= OTX2_FLAG_NTUPLE_SUPPORT; + pfvf->flags |= OTX2_FLAG_TC_FLOWER_SUPPORT; } if (allocated != count) -- cgit v1.2.3 From 793b9b729f1e8de57be8c8daf1a9838be96cabed Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Wed, 15 Jul 2026 10:20:21 +0200 Subject: mctp: serial: handle zero-length frames to prevent rx buffer overflow The MCTP serial receive state machine reads a frame length byte in mctp_serial_push_header() case 2 and validates it upper-bound-only: if (c > MCTP_SERIAL_FRAME_MTU) { dev->rxstate = STATE_ERR; } else { dev->rxlen = c; dev->rxpos = 0; dev->rxstate = STATE_DATA; ... } A length of zero passes this check, so rxlen is set to 0 and the state machine advances to STATE_DATA. In mctp_serial_push() STATE_DATA, the incoming byte is stored and rxpos incremented before the terminator is tested: dev->rxbuf[dev->rxpos] = c; dev->rxpos++; dev->rxstate = STATE_DATA; if (dev->rxpos == dev->rxlen) { dev->rxpos = 0; dev->rxstate = STATE_TRAILER; } With rxlen == 0 the "rxpos == rxlen" terminator can never fire (rxpos is already 1 on the first data byte), so subsequent bytes are written past the end of the fixed 74-byte rxbuf, which is the last member of the netdev private area. Every following data byte is an attacker-controlled 1-byte out-of-bounds heap write, and the overflow continues until a frame (0x7e) or escape byte resets the parser -- effectively unbounded. Reaching this requires CAP_NET_ADMIN to attach the N_MCTP line discipline and bring the resulting mctpserialN netdev up, after which the bytes arrive via the tty receive path. Route a zero-length frame straight to STATE_TRAILER instead of STATE_DATA. The trailer/framing bytes are still consumed, and the frame resolves to a zero-length skb that the MCTP core rejects; the parser never enters STATE_DATA with rxlen == 0, so the out-of-bounds write can no longer occur. KASAN, on a frame of 0x7e 0x01 0x00 followed by data bytes (before this change): UBSAN: array-index-out-of-bounds in drivers/net/mctp/mctp-serial.c:370 index 74 is out of range for type 'u8 [74]' BUG: KASAN: slab-out-of-bounds in mctp_serial_tty_receive_buf Write of size 1 at addr ... by task kworker/u16:0 mctp_serial_tty_receive_buf tty_ldisc_receive_buf flush_to_ldisc Allocated by task 152: alloc_netdev_mqs mctp_serial_open v2: route zero-length frames to STATE_TRAILER instead of STATE_ERR so the trailer/framing bytes are still consumed (Jeremy Kerr). Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: a0c2ccd9b5ad ("mctp: Add MCTP-over-serial transport binding") Cc: stable@vger.kernel.org Suggested-by: Jeremy Kerr Assisted-by: 0sec:multi-model Signed-off-by: Doruk Tan Ozturk Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260715082021.46315-1-doruk@0sec.ai Signed-off-by: Paolo Abeni --- drivers/net/mctp/mctp-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/mctp/mctp-serial.c b/drivers/net/mctp/mctp-serial.c index 26c9a33fd636..a5070ffa9a95 100644 --- a/drivers/net/mctp/mctp-serial.c +++ b/drivers/net/mctp/mctp-serial.c @@ -318,7 +318,7 @@ static void mctp_serial_push_header(struct mctp_serial *dev, u8 c) } else { dev->rxlen = c; dev->rxpos = 0; - dev->rxstate = STATE_DATA; + dev->rxstate = c > 0 ? STATE_DATA : STATE_TRAILER; dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c); } break; -- cgit v1.2.3 From 249447ff83967980ed6751660665cc682ff84e0c Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 22 Jul 2026 15:41:43 +0200 Subject: MAINTAINERS: remove Rengarajan Sundararajan from LAN78XX Rengarajan has left Microchip and mails to his address bounce. Remove him from the USB LAN78XX entry. Link: https://lore.kernel.org/netdev/DSWPR11MB971547088066C2CA91638F3BECF42@DSWPR11MB9715.namprd11.prod.outlook.com/ Signed-off-by: Nicolai Buchwitz Reviewed-by: Thangaraj Samynathan Link: https://patch.msgid.link/20260722134143.4141579-1-nb@tipi-net.de Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6fd196545966..b8b3b3b7e183 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27930,7 +27930,6 @@ F: drivers/usb/isp1760/* USB LAN78XX ETHERNET DRIVER M: Thangaraj Samynathan -M: Rengarajan Sundararajan M: UNGLinuxDriver@microchip.com L: netdev@vger.kernel.org S: Maintained -- cgit v1.2.3 From ea20c44935d6142daecfa9b39d635033a7553e1b Mon Sep 17 00:00:00 2001 From: Shihuang Liu Date: Wed, 22 Jul 2026 19:39:19 +0800 Subject: amt: fix use-after-free in AMT delayed works When an AMT device is removed, pending delayed works can still access the freed amt_dev structure, which may result in kernel crashes or memory corruption. amt_dev_stop() cancels req_wq and discovery_wq with cancel_delayed_work_sync(), but these works can be scheduled again from event_wq after the cancellation. This allows delayed works to access the freed amt_dev structure after the netdev has been released. The following is a simple race scenario: CPU0 CPU1 amt_dev_stop() cancel_delayed_work_sync() amt_event_work() mod_delayed_work(req_wq) free netdev req_wq accesses freed amt_dev Use disable_delayed_work_sync() in amt_dev_stop() to prevent req_wq and discovery_wq from being queued again and wait for running work items to complete. The delayed works are disabled after initialization in amt_newlink() and enabled only when the device is successfully opened. This keeps the delayed work lifecycle synchronized with the lifetime of the AMT device. Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface") Cc: stable@vger.kernel.org Signed-off-by: Shihuang Liu Reviewed-by: Simon Horman Reviewed-by: Taehee Yoo Link: https://patch.msgid.link/20260722113919.7723-1-shlomojune6@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/amt.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/amt.c b/drivers/net/amt.c index b733309b866f..182a41d59a75 100644 --- a/drivers/net/amt.c +++ b/drivers/net/amt.c @@ -3034,9 +3034,15 @@ static int amt_dev_open(struct net_device *dev) amt->event_idx = 0; amt->nr_events = 0; + enable_delayed_work(&amt->discovery_wq); + enable_delayed_work(&amt->req_wq); + err = amt_socket_create(amt); - if (err) + if (err) { + disable_delayed_work(&amt->req_wq); + disable_delayed_work(&amt->discovery_wq); return err; + } amt->req_cnt = 0; amt->remote_ip = 0; @@ -3062,8 +3068,8 @@ static int amt_dev_stop(struct net_device *dev) struct sock *sk; int i; - cancel_delayed_work_sync(&amt->req_wq); - cancel_delayed_work_sync(&amt->discovery_wq); + disable_delayed_work_sync(&amt->req_wq); + disable_delayed_work_sync(&amt->discovery_wq); cancel_delayed_work_sync(&amt->secret_wq); /* shutdown */ @@ -3317,6 +3323,8 @@ static int amt_newlink(struct net_device *dev, INIT_DELAYED_WORK(&amt->req_wq, amt_req_work); INIT_DELAYED_WORK(&amt->secret_wq, amt_secret_work); INIT_WORK(&amt->event_wq, amt_event_work); + disable_delayed_work(&amt->req_wq); + disable_delayed_work(&amt->discovery_wq); INIT_LIST_HEAD(&amt->tunnel_list); return 0; err: -- cgit v1.2.3 From 853e164c2b321f0711361bc23505aaeb7dc432c3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 Jul 2026 10:42:36 +0000 Subject: ipv4: icmp: fill flow parameters in icmp_route_lookup decoy lookup When Linux forwards a packet and needs to generate an ICMP error, icmp_route_lookup() performs a reverse-path relookup. For non-local destinations, it performs a decoy lookup to find the expected egress interface (rt2->dst.dev) before validating the path with ip_route_input(). Currently, the decoy flow structure (fl4_2) only sets .daddr = fl4_dec.saddr, leaving .saddr, .flowi4_dscp, .flowi4_proto, .flowi4_mark, .flowi4_oif, .fl4_sport, .fl4_dport, and .flowi4_uid zeroed out. When policy routing rules (such as ip rule add from $SRC lookup 100, or dscp/fwmark/ipproto/port rules, or VRF bindings) are configured: 1. The decoy lookup fails to match the policy rule because saddr and other key flow selectors are missing in fl4_2. 2. It resolves a route using the default table instead, returning an incorrect egress netdev. 3. Passing the wrong netdev to ip_route_input() causes strict reverse-path filtering (rp_filter=1) to fail, logging false-positive "martian source" warnings and causing the relookup to fail. Fix this by initializing fl4_2 from fl4_dec and: - Swapping source/destination IP addresses. - Swapping L4 ports for transport protocols with ports (TCP, UDP, SCTP, DCCP) so port-based policy routing matches correctly. Non-port protocols (such as ICMP or GRE) leave the flowi_uli union fields intact to prevent corruption. - Setting .flowi4_oif = l3mdev_master_ifindex(route_lookup_dev) to ensure VRF routing tables are respected. - Setting .flowi4_flags |= FLOWI_FLAG_ANYSRC to allow output route lookups for non-local source IP addresses. - Using __ip_route_output_key() instead of ip_route_output_key() for fl4_2 so that raw FIB routing is used without triggering spurious XFRM policy lookups on the decoy flow (the actual XFRM lookup is performed later using fl4_dec). Fixes: 415b3334a21a ("icmp: Fix regression in nexthop resolution during replies.") Reported-by: Muhammad Ziad Closes: https://lore.kernel.org/netdev/CAOAwikA60AYKdFr_UDLyja3oU4hqyAE7uFZWqum5uRdaQsgRYg@mail.gmail.com/ Signed-off-by: Eric Dumazet Reviewed-by: David Ahern Link: https://patch.msgid.link/20260722104236.2938082-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv4/icmp.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 23e921d313b3..0caedfc7ca92 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -548,11 +548,23 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4, if (IS_ERR(rt2)) err = PTR_ERR(rt2); } else { - struct flowi4 fl4_2 = {}; + struct flowi4 fl4_2 = fl4_dec; unsigned long orefdst; - fl4_2.daddr = fl4_dec.saddr; - rt2 = ip_route_output_key(net, &fl4_2); + swap(fl4_2.daddr, fl4_2.saddr); + switch (fl4_2.flowi4_proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + case IPPROTO_DCCP: + swap(fl4_2.fl4_sport, fl4_2.fl4_dport); + break; + } + + fl4_2.flowi4_oif = l3mdev_master_ifindex(route_lookup_dev); + fl4_2.flowi4_flags |= FLOWI_FLAG_ANYSRC; + + rt2 = __ip_route_output_key(net, &fl4_2); if (IS_ERR(rt2)) { err = PTR_ERR(rt2); goto relookup_failed; -- cgit v1.2.3 From 543adf072165aaf2e3b635c0476204f9658ed3bf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 Jul 2026 10:16:05 +0000 Subject: ppp: annotate data races in ppp_generic Several fields in struct ppp can be read or updated concurrently from multiple CPUs without synchronization, causing data races: 1. ppp->mru is read concurrently in ppp_receive_nonmp_frame() while being updated via PPPIOCSMRU ioctl. Protect ppp->mru updates in PPPIOCSMRU with ppp_recv_lock(ppp). 2. PPPIOCGFLAGS reads ppp->flags, ppp->xstate, and ppp->rstate unlocked. Wrap the read in ppp_lock(ppp) to get a consistent snapshot. 3. ppp->debug is updated via PPPIOCSDEBUG and read concurrently on fast paths. Annotate reads with READ_ONCE() and writes with WRITE_ONCE(). 4. ppp->last_xmit and ppp->last_recv are updated on TX/RX data paths and read via PPPIOCGIDLE32 / PPPIOCGIDLE64 ioctls. Annotate with WRITE_ONCE() / READ_ONCE() and use max() to handle jiffies subtraction. 5. ppp->npmode[] is updated via PPPIOCSNPMODE and read on TX/RX paths. Annotate with WRITE_ONCE() / READ_ONCE(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Eric Dumazet Reviewed-by: Qingfang Deng Link: https://patch.msgid.link/20260722101605.2868548-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ppp/ppp_generic.c | 50 ++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index ef54e0a0462a..cacc4c3a37d2 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -810,7 +810,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPPIOCSMRU: if (get_user(val, p)) break; + ppp_recv_lock(ppp); ppp->mru = val; + ppp_recv_unlock(ppp); err = 0; break; @@ -831,7 +833,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case PPPIOCGFLAGS: + ppp_lock(ppp); val = ppp->flags | ppp->xstate | ppp->rstate; + ppp_unlock(ppp); if (put_user(val, p)) break; err = 0; @@ -855,7 +859,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPPIOCSDEBUG: if (get_user(val, p)) break; - ppp->debug = val; + WRITE_ONCE(ppp->debug, val); err = 0; break; @@ -866,16 +870,16 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case PPPIOCGIDLE32: - idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ; - idle32.recv_idle = (jiffies - ppp->last_recv) / HZ; - if (copy_to_user(argp, &idle32, sizeof(idle32))) + idle32.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ; + idle32.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ; + if (copy_to_user(argp, &idle32, sizeof(idle32))) break; err = 0; break; case PPPIOCGIDLE64: - idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ; - idle64.recv_idle = (jiffies - ppp->last_recv) / HZ; + idle64.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ; + idle64.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ; if (copy_to_user(argp, &idle64, sizeof(idle64))) break; err = 0; @@ -916,7 +920,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_to_user(argp, &npi, sizeof(npi))) break; } else { - ppp->npmode[i] = npi.mode; + WRITE_ONCE(ppp->npmode[i], npi.mode); /* we may be able to transmit more packets now (??) */ netif_wake_queue(ppp->dev); } @@ -1454,7 +1458,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev) goto outf; /* Drop, accept or reject the packet */ - switch (ppp->npmode[npi]) { + switch (READ_ONCE(ppp->npmode[npi])) { case NPMODE_PASS: break; case NPMODE_QUEUE: @@ -1790,7 +1794,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_OUTBOUND_TAG); if (ppp->pass_filter && bpf_prog_run(ppp->pass_filter, skb) == 0) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: outbound frame " "not passed\n"); @@ -1800,11 +1804,11 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb) /* if this packet passes the active filter, record the time */ if (!(ppp->active_filter && bpf_prog_run(ppp->active_filter, skb) == 0)) - ppp->last_xmit = jiffies; + WRITE_ONCE(ppp->last_xmit, jiffies); skb_pull(skb, 2); #else /* for data packets, record the time */ - ppp->last_xmit = jiffies; + WRITE_ONCE(ppp->last_xmit, jiffies); #endif /* CONFIG_PPP_FILTER */ } @@ -2154,7 +2158,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) noskb: spin_unlock(&pch->downl); err_linearize: - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); DEV_STATS_INC(ppp->dev, tx_errors); ++ppp->nxseq; @@ -2502,7 +2506,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) *(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_INBOUND_TAG); if (ppp->pass_filter && bpf_prog_run(ppp->pass_filter, skb) == 0) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: inbound frame " "not passed\n"); @@ -2511,14 +2515,14 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) } if (!(ppp->active_filter && bpf_prog_run(ppp->active_filter, skb) == 0)) - ppp->last_recv = jiffies; + WRITE_ONCE(ppp->last_recv, jiffies); __skb_pull(skb, 2); } else #endif /* CONFIG_PPP_FILTER */ - ppp->last_recv = jiffies; + WRITE_ONCE(ppp->last_recv, jiffies); if ((ppp->dev->flags & IFF_UP) == 0 || - ppp->npmode[npi] != NPMODE_PASS) { + READ_ONCE(ppp->npmode[npi]) != NPMODE_PASS) { kfree_skb(skb); } else { /* chop off protocol */ @@ -2771,7 +2775,7 @@ ppp_mp_reconstruct(struct ppp *ppp) seq = seq_before(minseq, PPP_MP_CB(p)->sequence)? minseq + 1: PPP_MP_CB(p)->sequence; - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "lost frag %u..%u\n", oldseq, seq-1); @@ -2820,7 +2824,7 @@ ppp_mp_reconstruct(struct ppp *ppp) struct sk_buff *tmp2; skb_queue_reverse_walk_from_safe(list, p, tmp2) { - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "discarding frag %u\n", PPP_MP_CB(p)->sequence); @@ -2842,7 +2846,7 @@ ppp_mp_reconstruct(struct ppp *ppp) skb_queue_walk_safe(list, p, tmp) { if (p == head) break; - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, "discarding frag %u\n", PPP_MP_CB(p)->sequence); @@ -2850,7 +2854,7 @@ ppp_mp_reconstruct(struct ppp *ppp) kfree_skb(p); } - if (ppp->debug & 1) + if (READ_ONCE(ppp->debug) & 1) netdev_printk(KERN_DEBUG, ppp->dev, " missed pkts %u..%u\n", ppp->nextseq, @@ -3161,7 +3165,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) if (!ppp->rc_state) break; if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len, - ppp->file.index, 0, ppp->mru, ppp->debug)) { + ppp->file.index, 0, ppp->mru, + READ_ONCE(ppp->debug))) { ppp->rstate |= SC_DECOMP_RUN; ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR); } @@ -3170,7 +3175,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) if (!ppp->xc_state) break; if (ppp->xcomp->comp_init(ppp->xc_state, dp, len, - ppp->file.index, 0, ppp->debug)) + ppp->file.index, 0, + READ_ONCE(ppp->debug))) ppp->xstate |= SC_COMP_RUN; } break; -- cgit v1.2.3 From e9c238f6fe42fb1b4dba3a578277de32cb487937 Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Wed, 22 Jul 2026 09:38:43 +0000 Subject: pppoe: reload header pointer after dev_hard_header() pppoe_sendmsg() saves a pointer to the PPPoE header before calling dev_hard_header(). Device header callbacks are allowed to reallocate the skb head, invalidating pointers into it. This can happen when a send is blocked in copy_from_user() while the first non-Ethernet port is added to an empty team device. The team's delegated GRE header callback then expands the skb head. PPPoE subsequently writes six bytes through the stale pointer into the freed head. Reload the PPPoE header through the skb's network-header offset after device header creation. pskb_expand_head() updates that offset when it relocates the head. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Asim Viladi Oglu Manizada Reviewed-by: Vadim Fedorenko Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260722093814.3017176-1-manizada@pm.me Signed-off-by: Jakub Kicinski --- drivers/net/ppp/pppoe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 4a018acb5262..6874a1a8edaf 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -825,6 +825,7 @@ static int pppoe_sendmsg(struct socket *sock, struct msghdr *m, dev_hard_header(skb, dev, ETH_P_PPP_SES, po->pppoe_pa.remote, NULL, total_len); + ph = pppoe_hdr(skb); memcpy(ph, &hdr, sizeof(struct pppoe_hdr)); ph->length = htons(total_len); -- cgit v1.2.3 From 4a05269bb723073a9299a5209876d4d40c51a030 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Wed, 22 Jul 2026 16:21:24 +0200 Subject: MAINTAINERS: Add myself for stmmac ethernet driver maintainance The stmmac driver based on Synopsys' dwmac IP is used in a very wide variety of SoCs and is currently very actively used and contributed to. It has been orphaned in January 2025 after the previous maintainers became inactive, but Russell King was providing very valuable reviews and fixes for the driver at that point. Now we're seeing more and more activity on the driver, but are lacking people to test and review contributions to both glue drivers as well as core stmmac code. I have access to some variety of stmmac-based platforms such as socfpga CycloneV, imx8mp, some Allwinner SoCs and stm32mp1xx boards that I can run regression tests on, and I'm offering to step-up as a maintainer for driver, for the time being at least. Let's hope other people will eventually join this effort. Signed-off-by: Maxime Chevallier Link: https://patch.msgid.link/20260722142125.1767689-1-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index b8b3b3b7e183..fdc42ef971e2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25973,8 +25973,9 @@ F: Documentation/devicetree/bindings/phy/st,stm32mp25-combophy.yaml F: drivers/phy/st/phy-stm32-combophy.c STMMAC ETHERNET DRIVER +M: Maxime Chevallier L: netdev@vger.kernel.org -S: Orphan +S: Maintained F: Documentation/networking/device_drivers/ethernet/stmicro/ F: drivers/net/ethernet/stmicro/stmmac/ -- cgit v1.2.3 From 9de445d8296a7f2b011ebb5834fdc94dcda5c778 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 14 Jul 2026 15:03:41 +0200 Subject: s390/ptff: Export ptff_function_mask[] Export the ptff_function_mask to make ptff_query() usable in modules. Signed-off-by: Sven Schnelle Acked-by: Heiko Carstens Link: https://patch.msgid.link/20260714130342.1971700-2-svens@linux.ibm.com Signed-off-by: Jakub Kicinski --- arch/s390/kernel/time.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index bd0df61d1907..2b989bebd220 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -65,6 +65,7 @@ ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier); EXPORT_SYMBOL(s390_epoch_delta_notifier); unsigned char ptff_function_mask[16]; +EXPORT_SYMBOL(ptff_function_mask); static unsigned long lpar_offset; static unsigned long initial_leap_seconds; -- cgit v1.2.3 From e78f1ac37afcb16cb6fef8a2c92591eab6558956 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Tue, 14 Jul 2026 15:03:42 +0200 Subject: ptp: ptp_s390: Add missing facility check Only register the physical clock when facility 28 is installed and PTFF QAF returns that PTFF QPT is available. Fixes: 2d7de7a3010d ("s390/time: Add PtP driver") Signed-off-by: Sven Schnelle Cc: stable@kernel.org Reviewed-by: Heiko Carstens Link: https://patch.msgid.link/20260714130342.1971700-3-svens@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_s390.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_s390.c b/drivers/ptp/ptp_s390.c index 29618eb9bf44..02d624d89a0a 100644 --- a/drivers/ptp/ptp_s390.c +++ b/drivers/ptp/ptp_s390.c @@ -107,6 +107,9 @@ static __init int ptp_s390_init(void) if (IS_ERR(ptp_stcke_clock)) return PTR_ERR(ptp_stcke_clock); + if (!test_facility(28) || !ptff_query(PTFF_QPT)) + return 0; + ptp_qpt_clock = ptp_clock_register(&ptp_s390_qpt_info, NULL); if (IS_ERR(ptp_qpt_clock)) { ptp_clock_unregister(ptp_stcke_clock); @@ -117,7 +120,8 @@ static __init int ptp_s390_init(void) static __exit void ptp_s390_exit(void) { - ptp_clock_unregister(ptp_qpt_clock); + if (ptp_qpt_clock) + ptp_clock_unregister(ptp_qpt_clock); ptp_clock_unregister(ptp_stcke_clock); } -- cgit v1.2.3 From fe0c002928c6749b7f4a726f6f600f6dd70280ea Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Wed, 22 Jul 2026 10:53:53 +0800 Subject: hinic: remove unused ethtool RSS user configuration buffers rss_indir_user and rss_hkey_user are allocated and filled in __set_rss_rxfh() when the user configures RSS via ethtool, but nothing ever reads them. hinic_get_rxfh() fetches the state from the device, and the hardware is programmed from the original indir/key arguments. These buffers only leaked on driver unload. Drop the unused allocations, memcpys, and struct fields. Fixes: 4fdc51bb4e92 ("hinic: add support for rss parameters with ethtool") Signed-off-by: Chenguang Zhao Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260722025353.328179-1-chenguang.zhao@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 2 -- drivers/net/ethernet/huawei/hinic/hinic_ethtool.c | 21 --------------------- 2 files changed, 23 deletions(-) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 52ea97c818b8..d9ab94910a2a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -104,8 +104,6 @@ struct hinic_dev { u16 num_rss; u16 rss_limit; struct hinic_rss_type rss_type; - u8 *rss_hkey_user; - s32 *rss_indir_user; struct hinic_intr_coal_info *rx_intr_coalesce; struct hinic_intr_coal_info *tx_intr_coalesce; struct hinic_sriov_info sriov_info; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index a8b129ce1b7e..f75e8563f23a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -1064,17 +1064,6 @@ static int __set_rss_rxfh(struct net_device *netdev, int err; if (indir) { - if (!nic_dev->rss_indir_user) { - nic_dev->rss_indir_user = - kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, - GFP_KERNEL); - if (!nic_dev->rss_indir_user) - return -ENOMEM; - } - - memcpy(nic_dev->rss_indir_user, indir, - sizeof(u32) * HINIC_RSS_INDIR_SIZE); - err = hinic_rss_set_indir_tbl(nic_dev, nic_dev->rss_tmpl_idx, indir); if (err) @@ -1082,16 +1071,6 @@ static int __set_rss_rxfh(struct net_device *netdev, } if (key) { - if (!nic_dev->rss_hkey_user) { - nic_dev->rss_hkey_user = - kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL); - - if (!nic_dev->rss_hkey_user) - return -ENOMEM; - } - - memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE); - err = hinic_rss_set_template_tbl(nic_dev, nic_dev->rss_tmpl_idx, key); if (err) -- cgit v1.2.3 From 7917d16d14fb512f8ffe3815b7940b6c93ff4fde Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 23 Jul 2026 22:27:15 +0800 Subject: LoongArch: Increase TASK_STRUCT_OFFSET up to 2040 for 32BIT THREAD_INFO_IN_TASK increase the size of task_struct, which casuses a build error for the 32BIT kernel if RANDSTRUCT is enabled. So increase TASK_STRUCT_OFFSET as big as possible (2040), but can still be aligned and be fit in the addi.w instruction. Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/asmmacro.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/asmmacro.h b/arch/loongarch/include/asm/asmmacro.h index a648be5f723f..b7423d1ac568 100644 --- a/arch/loongarch/include/asm/asmmacro.h +++ b/arch/loongarch/include/asm/asmmacro.h @@ -14,7 +14,7 @@ #ifdef CONFIG_64BIT #define TASK_STRUCT_OFFSET 0 #else -#define TASK_STRUCT_OFFSET 2000 +#define TASK_STRUCT_OFFSET 2040 #endif .macro cpu_save_nonscratch thread -- cgit v1.2.3 From 7ea74820edcb22ffa3fb068076d73c6821d7e6d2 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 23 Jul 2026 22:27:16 +0800 Subject: LoongArch: Fix build errors due to wrong instructions for 32BIT In some assembly files there are some instructions that only valid for 64BIT, but those files can be compiled for 32BIT and cause build errors. So, replace those instructions with macros: li.d --> LONG_LI (li.w or li.d), addi.d --> PTR_ADDI (addi.w or addi.d). BTW, Re-tab the indention in the assembly files for alignment. Cc: stable@vger.kernel.org # 6.19+ Signed-off-by: Huacai Chen --- arch/loongarch/kernel/rethook_trampoline.S | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/loongarch/kernel/rethook_trampoline.S b/arch/loongarch/kernel/rethook_trampoline.S index d4ceb2fa2a5c..2e009fbea53f 100644 --- a/arch/loongarch/kernel/rethook_trampoline.S +++ b/arch/loongarch/kernel/rethook_trampoline.S @@ -71,27 +71,27 @@ cfi_ld s7, PT_R30 cfi_ld s8, PT_R31 LONG_L t0, sp, PT_CRMD - li.d t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */ + LONG_LI t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */ csrxchg t0, t1, LOONGARCH_CSR_CRMD .endm SYM_CODE_START(arch_rethook_trampoline) UNWIND_HINT_UNDEFINED - addi.d sp, sp, -PT_SIZE + PTR_ADDI sp, sp, -PT_SIZE save_all_base_regs - addi.d t0, sp, PT_SIZE - LONG_S t0, sp, PT_R3 + PTR_ADDI t0, sp, PT_SIZE + LONG_S t0, sp, PT_R3 - move a0, sp /* pt_regs */ + move a0, sp /* pt_regs */ - bl arch_rethook_trampoline_callback + bl arch_rethook_trampoline_callback /* use the result as the return-address */ - move ra, a0 + move ra, a0 restore_all_base_regs - addi.d sp, sp, PT_SIZE + PTR_ADDI sp, sp, PT_SIZE - jr ra + jr ra SYM_CODE_END(arch_rethook_trampoline) -- cgit v1.2.3 From ea68d444a658783234a06f05414e41cf93a18fb2 Mon Sep 17 00:00:00 2001 From: Kanglong Wang Date: Thu, 23 Jul 2026 22:27:29 +0800 Subject: LoongArch: Move jump_label_init() before parse_early_param() When enabling both CONFIG_MEM_ALLOC_PROFILING=y and CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=y, then diabling memory profiling by adding the boot parameter 'sysctl.vm.mem_profiling=0' will cause the kernel failed to boot. After analysis, this is because jump_label_init() must be called before parse_early_param(), the early param handlers may modify static keys by static_branch_enable/disable(). Fix this by moving jump_label_init() to before parse_early_param(). The solution is similar to other architectures. Cc: Signed-off-by: Kanglong Wang Signed-off-by: Huacai Chen --- arch/loongarch/kernel/setup.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index eaebb52bd36e..6fa4a22a58fd 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -603,6 +603,7 @@ void __init setup_arch(char **cmdline_p) memblock_init(); pagetable_init(); bootcmdline_init(cmdline_p); + jump_label_init(); /* Initialise the static keys for early params */ parse_early_param(); reserve_initrd_mem(); @@ -610,8 +611,6 @@ void __init setup_arch(char **cmdline_p) arch_mem_init(cmdline_p); resource_init(); - jump_label_init(); /* Initialise the static keys for paravirtualization */ - #ifdef CONFIG_SMP plat_smp_setup(); prefill_possible_map(); -- cgit v1.2.3 From 4e8f58620f6717f72f3d88a2c8f25c0c656d0ba7 Mon Sep 17 00:00:00 2001 From: Rong Bao Date: Thu, 23 Jul 2026 22:27:29 +0800 Subject: LoongArch: Retrieve CPU package ID from PPTT when available Currently, the LoongArch CPU topology initialization code calculates each core's package ID by dividing its physical ID by loongson_sysconf. cores_per_package. This relies on the assumption that cores_per_package counts in the same domain as physical IDs. On Loongson-3B6000 (XB612B0V_1.2), cores_per_package matches the visible core count -- 24 in this case. However, the physical IDs range from 0 to 31 in a noncontinuous fashion: $ cat /proc/cpuinfo | grep -i -F 'global_id' global_id : 0 global_id : 1 global_id : 4 global_id : 5 global_id : 6 global_id : 7 global_id : 8 global_id : 9 global_id : 10 global_id : 11 global_id : 14 global_id : 15 global_id : 16 global_id : 17 global_id : 20 global_id : 21 global_id : 22 global_id : 23 global_id : 26 global_id : 27 global_id : 28 global_id : 29 global_id : 30 global_id : 31 Retrieve the exact package ID from ACPI PPTT when available, in the same style as retrieving the core ID and thread ID in parse_acpi_topology(). Use this information in loongson_init_secondary() when the PPTT readout is successful. The original division logic is kept as a fallback. Meanwhile, since some existing code paths like loongson3_cpufreq expect a continuous integer sequence of package IDs in [0, MAX_PACKAGES) when retrieving from cpu_data[], here we also canonicalize the package ID to be filled in parse_acpi_topology() to meet such an expectation. Cc: stable@vger.kernel.org Tested-by: Mingcong Bai Co-developed-by: Xi Ruoyao Signed-off-by: Xi Ruoyao Signed-off-by: Rong Bao Signed-off-by: Huacai Chen --- arch/loongarch/kernel/acpi.c | 27 ++++++++++++++++++++++++++- arch/loongarch/kernel/smp.c | 4 ++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 8f650c9ffecd..873e90990771 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -201,10 +201,12 @@ static void __init acpi_process_madt(void) } int pptt_enabled; +static int acpi_nr_packages; +static int acpi_package_ids[MAX_PACKAGES]; int __init parse_acpi_topology(void) { - int cpu, topology_id; + int i, cpu, topology_id; for_each_possible_cpu(cpu) { topology_id = find_acpi_cpu_topology(cpu, 0); @@ -222,6 +224,29 @@ int __init parse_acpi_topology(void) cpu_data[cpu].core = topology_id; } + + topology_id = find_acpi_cpu_topology_package(cpu); + if (topology_id < 0) { + pr_warn("Invalid BIOS PPTT\n"); + return -ENOENT; + } + + for (i = 0; i < acpi_nr_packages; i++) + if (acpi_package_ids[i] == topology_id) + break; + + if (i == acpi_nr_packages) + acpi_package_ids[acpi_nr_packages++] = topology_id; + + cpu_data[cpu].package = topology_id; + } + + for_each_possible_cpu(cpu) { + for (i = 0; i < acpi_nr_packages; i++) + if (cpu_data[cpu].package == acpi_package_ids[i]) { + cpu_data[cpu].package = i; /* Canonicalize */ + break; + } } pptt_enabled = 1; diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 5d792256bbb9..d4b5d1b6bb01 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -426,10 +426,10 @@ void loongson_init_secondary(void) numa_add_cpu(cpu); #endif per_cpu(cpu_state, cpu) = CPU_ONLINE; - cpu_data[cpu].package = - cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; cpu_data[cpu].core = pptt_enabled ? cpu_data[cpu].core : cpu_logical_map(cpu) % loongson_sysconf.cores_per_package; + cpu_data[cpu].package = pptt_enabled ? cpu_data[cpu].package : + cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; cpu_data[cpu].global_id = cpu_logical_map(cpu); } -- cgit v1.2.3 From 485ed44db5694d8d2e5027f63ad608e705286f30 Mon Sep 17 00:00:00 2001 From: George Guo Date: Thu, 23 Jul 2026 22:27:30 +0800 Subject: LoongArch: Fix address space mismatch in kexec command line lookup When searching the loaded segments for the "kexec" command line marker, the kexec_load(2) path (file_mode == 0) passes the user-space segment buffer straight to strncmp() through a bogus (char __user *) cast. This dereferences a user pointer in kernel context, which is wrong and is flagged by sparse: arch/loongarch/kernel/machine_kexec.c:84:51: sparse: incorrect type in argument 2 (different address spaces) @@ expected char const * @@ got char [noderef] __user * Here copy the marker-sized prefix of each segment into a small on-stack buffer with copy_from_user() before comparing, and skip segments that fault. The subsequent copy_from_user() that stages the full command line into the safe area is left unchanged. Cc: stable@vger.kernel.org Fixes: 4a03b2ac06a5 ("LoongArch: Add kexec support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605051639.aEPioXdD-lkp@intel.com/ Co-developed-by: Kexin Liu Signed-off-by: Kexin Liu Signed-off-by: George Guo Signed-off-by: Huacai Chen --- arch/loongarch/kernel/machine_kexec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kernel/machine_kexec.c b/arch/loongarch/kernel/machine_kexec.c index d7fafda1d541..1883cae93bc3 100644 --- a/arch/loongarch/kernel/machine_kexec.c +++ b/arch/loongarch/kernel/machine_kexec.c @@ -42,6 +42,7 @@ static unsigned long first_ind_entry; int machine_kexec_prepare(struct kimage *kimage) { int i; + char head[8]; char *bootloader = "kexec"; void *cmdline_ptr = (void *)KEXEC_CMDLINE_ADDR; @@ -59,7 +60,9 @@ int machine_kexec_prepare(struct kimage *kimage) } else { /* Find the command line */ for (i = 0; i < kimage->nr_segments; i++) { - if (!strncmp(bootloader, (char __user *)kimage->segment[i].buf, strlen(bootloader))) { + if (copy_from_user(head, kimage->segment[i].buf, strlen(bootloader))) + continue; + if (!strncmp(bootloader, head, strlen(bootloader))) { if (!copy_from_user(cmdline_ptr, kimage->segment[i].buf, COMMAND_LINE_SIZE)) kimage->arch.cmdline_ptr = (unsigned long)cmdline_ptr; break; -- cgit v1.2.3 From 73555fdab5e1e4f24ca000c41a616b34edf4b55d Mon Sep 17 00:00:00 2001 From: Haoran Jiang Date: Thu, 23 Jul 2026 22:27:30 +0800 Subject: LoongArch: Fix oops during single-step debugging When entering KDB via a breakpoint and then performing single-step debugging, an oops is triggered. Now during single-step debugging, kdb_local() expects the reason to be KDB_REASON_SSTEP, but it is actually KDB_REASON_OOPS. In kdb_stub(), when determining the reason, the ex_vector for single-step should be 0, as already implemented on other architectures such as arm64 and riscv. Before the patch: [112]kdb> ss Entering kdb (current=0x900020009f520000, pid 10661) on processor 112 Oops: (null) due to oops @ 0x90000000005b57a4 Cc: stable@vger.kernel.org Signed-off-by: Haoran Jiang Signed-off-by: Huacai Chen --- arch/loongarch/kernel/kgdb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/kernel/kgdb.c b/arch/loongarch/kernel/kgdb.c index 17664a6043b1..e7b59f8a4b05 100644 --- a/arch/loongarch/kernel/kgdb.c +++ b/arch/loongarch/kernel/kgdb.c @@ -252,7 +252,8 @@ static int kgdb_loongarch_notify(struct notifier_block *self, unsigned long cmd, if (atomic_read(&kgdb_active) != -1) kgdb_nmicallback(smp_processor_id(), regs); - if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs)) + if (kgdb_handle_exception(regs->csr_era == stepped_address ? 0 : args->trapnr, + args->signr, cmd, regs)) return NOTIFY_DONE; if (atomic_read(&kgdb_setting_breakpoint)) -- cgit v1.2.3 From dacd348b8a993373576fe2ee2d8b114740ba57a6 Mon Sep 17 00:00:00 2001 From: Nicholas Dudar Date: Thu, 23 Jul 2026 22:27:35 +0800 Subject: LoongArch: BPF: Zero-extend signed ALU32 div/mod results ALU32 operations write a 32-bit result and leave the upper 32 bits of the BPF register zero. The LoongArch JIT sign-extends the result of signed ALU32 BPF_DIV and BPF_MOD (off=1), so a negative 32-bit quotient or remainder leaves bits 63:32 set in JITted code while the verifier and interpreter model those bits as zero. Keep sign-extension on the operands, which signed divide needs, and zero-extend the ALU32 result after the divide or modulo instruction, matching the unsigned ALU32 div/mod paths and every other ALU32 operation in this JIT. Fixes: 2425c9e002d2 ("LoongArch: BPF: Support signed div instructions") Fixes: 7b6b13d32965 ("LoongArch: BPF: Support signed mod instructions") Assisted-by: Claude:claude-opus-4-8 Acked-by: Tiezhu Yang Tested-by: Tiezhu Yang Signed-off-by: Nicholas Dudar Signed-off-by: Huacai Chen --- arch/loongarch/net/bpf_jit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 2738b4db1165..c91d474faba7 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -835,7 +835,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext move_reg(ctx, t1, src); emit_sext_32(ctx, t1, is32); emit_insn(ctx, divd, dst, dst, t1); - emit_sext_32(ctx, dst, is32); + emit_zext_32(ctx, dst, is32); } break; @@ -852,7 +852,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext emit_sext_32(ctx, t1, is32); emit_sext_32(ctx, dst, is32); emit_insn(ctx, divd, dst, dst, t1); - emit_sext_32(ctx, dst, is32); + emit_zext_32(ctx, dst, is32); } break; @@ -870,7 +870,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext move_reg(ctx, t1, src); emit_sext_32(ctx, t1, is32); emit_insn(ctx, modd, dst, dst, t1); - emit_sext_32(ctx, dst, is32); + emit_zext_32(ctx, dst, is32); } break; @@ -887,7 +887,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext emit_sext_32(ctx, t1, is32); emit_sext_32(ctx, dst, is32); emit_insn(ctx, modd, dst, dst, t1); - emit_sext_32(ctx, dst, is32); + emit_zext_32(ctx, dst, is32); } break; -- cgit v1.2.3 From 3b536db8fb32da9e9c62f2bb45e2e319331f0426 Mon Sep 17 00:00:00 2001 From: Aldo Ariel Panzardo Date: Thu, 16 Jul 2026 12:43:19 -0300 Subject: net: qrtr: restrict socket creation to the initial network namespace QRTR keeps its entire port and node state in module-global variables that are not partitioned per network namespace: qrtr_local_nid is a single global node id (always 1) and qrtr_ports is a single global xarray. qrtr_port_lookup() and qrtr_local_enqueue() operate on that global state with no network-namespace check, and qrtr_create() places no restriction on the namespace a socket is created in. As a result an unprivileged process that creates an AF_QIPCRTR socket in a separate network namespace, e.g. via unshare(CLONE_NEWUSER | CLONE_NEWNET), can send QRTR datagrams - including control-plane messages such as QRTR_TYPE_NEW_SERVER - to QRTR sockets owned by another namespace, and vice versa. The receiving socket sees such a message as coming from node id 1, indistinguishable from a legitimate local client, breaking the isolation that network namespaces are expected to provide. QRTR is a transport to global hardware endpoints (the modem and other remote processors) and has no per-namespace semantics; its in-kernel name service already creates its socket in init_net only. Confine the socket family to the initial network namespace, as other non-namespace-aware socket families do (see llc_ui_create() and the ieee802154 socket code). Fixes: bdabad3e363d ("net: Add Qualcomm IPC router") Signed-off-by: Aldo Ariel Panzardo Link: https://patch.msgid.link/20260716154319.3297699-1-qwe.aldo@gmail.com Signed-off-by: Jakub Kicinski --- net/qrtr/af_qrtr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index d02ef9a74c3c..a30fa56e6aa3 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -1263,6 +1263,14 @@ static int qrtr_create(struct net *net, struct socket *sock, if (sock->type != SOCK_DGRAM) return -EPROTOTYPE; + /* QRTR keeps its port and node state in module-global variables that + * are not partitioned per network namespace, and the in-kernel name + * service only operates in init_net. Confine the family to init_net so + * a socket in another namespace cannot reach the global control plane. + */ + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + sk = sk_alloc(net, AF_QIPCRTR, GFP_KERNEL, &qrtr_proto, kern); if (!sk) return -ENOMEM; -- cgit v1.2.3 From 18f116931f52e3c3303ad4b15ff41eb89b0e4239 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Thu, 16 Jul 2026 22:29:58 +0800 Subject: raw: annotate lockless match fields in raw_v4_match() raw_v4_match() is a lockless match helper under sk_for_each_rcu(). It still reads inet->inet_daddr, inet->inet_rcv_saddr and sk->sk_bound_dev_if with plain loads while bind, connect and bind-to-device paths can update the same match fields concurrently. Annotate only those mutable match fields in raw_v4_match(), and do so at the point of use instead of hoisting the bound-device read before the earlier short-circuit tests. Also annotate the raw bind writer and the shared IPv4 datagram connect writer used by raw sockets, so the address fields updated on bind and connect match explicit WRITE_ONCE() updates. This version intentionally leaves the shared disconnect-side IPv4 writers to follow-up cleanup and limits the writer changes here to the raw bind path and the datagram connect path directly exercised by raw sockets. Fixes: 0daf07e52709 ("raw: convert raw sockets to RCU") Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260716142958.3064224-1-runyu.xiao@seu.edu.cn Signed-off-by: Jakub Kicinski --- net/ipv4/datagram.c | 4 ++-- net/ipv4/raw.c | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index 1614593b6d72..7d25519a6cdd 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -63,12 +63,12 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int } /* Update addresses before rehashing */ - inet->inet_daddr = fl4->daddr; + WRITE_ONCE(inet->inet_daddr, fl4->daddr); inet->inet_dport = usin->sin_port; if (!inet->inet_saddr) inet->inet_saddr = fl4->saddr; if (!inet->inet_rcv_saddr) { - inet->inet_rcv_saddr = fl4->saddr; + WRITE_ONCE(inet->inet_rcv_saddr, fl4->saddr); if (sk->sk_prot->rehash) sk->sk_prot->rehash(sk); } diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index e9fbab6ad914..7f74d8b95a37 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -118,13 +118,21 @@ bool raw_v4_match(struct net *net, const struct sock *sk, unsigned short num, __be32 raddr, __be32 laddr, int dif, int sdif) { const struct inet_sock *inet = inet_sk(sk); + __be32 daddr, rcv_saddr; - if (net_eq(sock_net(sk), net) && inet->inet_num == num && - !(inet->inet_daddr && inet->inet_daddr != raddr) && - !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) && - raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif)) - return true; - return false; + if (!net_eq(sock_net(sk), net) || inet->inet_num != num) + return false; + + daddr = READ_ONCE(inet->inet_daddr); + if (daddr && daddr != raddr) + return false; + + rcv_saddr = READ_ONCE(inet->inet_rcv_saddr); + if (rcv_saddr && rcv_saddr != laddr) + return false; + + return raw_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if), + dif, sdif); } EXPORT_SYMBOL_GPL(raw_v4_match); @@ -722,7 +730,8 @@ static int raw_bind(struct sock *sk, struct sockaddr_unsized *uaddr, chk_addr_ret)) goto out; - inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr; + inet->inet_saddr = addr->sin_addr.s_addr; + WRITE_ONCE(inet->inet_rcv_saddr, addr->sin_addr.s_addr); if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) inet->inet_saddr = 0; /* Use device */ sk_dst_reset(sk); -- cgit v1.2.3 From 160a783aa65b74782bc17cb874af1a6d3f5fba3c Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 23 Jul 2026 14:54:44 +0800 Subject: power: supply: bq25890: fix the -10 C NTC lookup entry The TSPCT lookup table is monotonically decreasing except for ADC code 121, where the sequence reads -9.0 C, -1.0 C, -12.0 C. This makes the reported battery temperature jump upward by eight degrees for one code and then downward by eleven degrees for the next code. The entry is a missing zero: use -10.0 C so the sequence remains monotonic between -9.0 C and -12.0 C. Fixes: 9652c02428f3 ("power: bq25890: add POWER_SUPPLY_PROP_TEMP") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/0619C8BF15F43B7C+20260723065444.1796002-1-raoxu@uniontech.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index c1c12a447178..180bc137a863 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -320,7 +320,7 @@ static const u32 bq25890_tspct_tbl[] = { 145, 140, 130, 120, 115, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0, -10, -20, -30, -40, -60, -70, -80, - -90, -10, -120, -140, -150, -170, -190, -210, + -90, -100, -120, -140, -150, -170, -190, -210, }; #define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl) -- cgit v1.2.3 From fd3a3f28ed60c6af4b2a39933b151d6b27842c3b Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 16 Jul 2026 21:34:23 +0200 Subject: mac802154: llsec: reject frames shorter than the authentication tag llsec_do_decrypt_auth() computes the associated-data length for the AEAD request as assoclen += datalen - authlen; where datalen is the number of bytes after the MAC header and authlen (4, 8 or 16) is the length of the authentication tag. Nothing verifies that the frame actually carries at least authlen payload bytes. A secured frame whose payload is shorter than the tag makes datalen - authlen negative; assoclen is then passed to aead_request_set_ad() as an unsigned value close to 4 GiB, so crypto_aead_decrypt() walks far off the end of the scatterlist that only spans the real frame. The frame is fully attacker-controlled and reaches this path from any IEEE 802.15.4 peer in radio range. Reject frames whose payload is shorter than the authentication tag before the subtraction. Dynamically reproduced on a KASAN kernel as a general-protection-fault in the AEAD scatterwalk, and the fix confirmed. Fixes: 4c14a2fb5d14 ("mac802154: add llsec decryption method") Cc: stable@vger.kernel.org Reviewed-by: Simon Horman Signed-off-by: Doruk Tan Ozturk Link: https://patch.msgid.link/20260716193423.32498-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski --- net/mac802154/llsec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 5e7cc11fab3a..85452ef9a58c 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -891,6 +891,11 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, data = skb_mac_header(skb) + skb->mac_len; datalen = skb_tail_pointer(skb) - data; + if (datalen < authlen) { + kfree_sensitive(req); + return -EBADMSG; + } + sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen); if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) { -- cgit v1.2.3 From 3a61bd9637f3d929aa846e4eb3d98b48c26fcb0e Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 16 Jul 2026 22:34:59 +0200 Subject: vxlan: require CAP_NET_ADMIN in the device netns for changelink A tunnel changelink() operates on at most two netns, dev_net(dev) and the sticky underlay netns vxlan->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in vxlan->net can rewrite a vxlan device whose underlay lives in vxlan->net. vxlan_changelink() validates and applies the new configuration against vxlan->net (vxlan_config_validate(vxlan->net, ...)) and can reopen the underlay socket in that netns, so the same reasoning as the tunnel changelink series applies here. Gate vxlan_changelink() with rtnl_dev_link_net_capable(), at the top of the op before any attribute is parsed, matching ipgre_changelink() and the rest of the "require CAP_NET_ADMIN in the device netns for changelink" series. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: 8bcdc4f3a20b ("vxlan: add changelink support") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Fernando Fernandez Mancera Link: https://patch.msgid.link/20260716203500.70573-2-doruk@0sec.ai Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 67c367cc5662..d834a4865aec 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4421,6 +4421,9 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], struct vxlan_rdst *dst; int err; + if (!rtnl_dev_link_net_capable(dev, vxlan->net)) + return -EPERM; + dst = &vxlan->default_dst; err = vxlan_nl2conf(tb, data, dev, &conf, true, extack); if (err) -- cgit v1.2.3 From 8efb8f8bbb353b8f2fdf4f37534c6d96c9f69e01 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Thu, 16 Jul 2026 22:35:00 +0200 Subject: geneve: require CAP_NET_ADMIN in the device netns for changelink A tunnel changelink() operates on at most two netns, dev_net(dev) and the sticky underlay netns geneve->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in geneve->net can rewrite a geneve device whose underlay lives in geneve->net. geneve_changelink() applies the new configuration against geneve->net: geneve_link_config() and the geneve_quiesce()/geneve_unquiesce() pair reopen the underlay sockets in that netns (geneve_sock_add() uses geneve->net), so the same reasoning as the tunnel changelink series applies here. Gate geneve_changelink() with rtnl_dev_link_net_capable(), at the top of the op before any attribute is parsed, matching ipgre_changelink() and the rest of the "require CAP_NET_ADMIN in the device netns for changelink" series. Found by 0sec automated security-research tooling (https://0sec.ai). Fixes: 5b861f6baa3a ("geneve: add rtnl changelink support") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Fernando Fernandez Mancera Link: https://patch.msgid.link/20260716203500.70573-3-doruk@0sec.ai Signed-off-by: Jakub Kicinski --- drivers/net/geneve.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index bb275b30d3e5..72023ebd0e1b 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -2394,6 +2394,9 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[], struct geneve_config cfg; int err; + if (!rtnl_dev_link_net_capable(dev, geneve->net)) + return -EPERM; + /* If the geneve device is configured for metadata (or externally * controlled, for example, OVS), then nothing can be changed. */ -- cgit v1.2.3 From 11c057d23465c7a5817a7284c896d19d54c0b616 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Fri, 17 Jul 2026 10:23:38 +0300 Subject: net/mlx5: Fix MCIA register buffer overflow on 32 dword reads The MCIA register can return up to 32 dwords (128 bytes) when the device advertises the mcia_32dwords capability, but struct mlx5_ifc_mcia_reg_bits only defines dword_0..11, leaving room for just 12 dwords (48 bytes) of data. mlx5_query_mcia() clamps the read size to mlx5_mcia_max_bytes() and then memcpy()s that many bytes out of the register, potentially reading past the end of the 'out' buffer. On kernels built with FORTIFY_SOURCE this is caught as a buffer overflow while reading the module EEPROM via ethtool: detected buffer overflow in memcpy kernel BUG at lib/string_helpers.c:1048! RIP: 0010:fortify_panic+0x13/0x20 Call Trace: mlx5_query_mcia.isra.0+0x200/0x210 [mlx5_core] mlx5_query_module_eeprom_by_page+0x4a/0xa0 [mlx5_core] mlx5e_get_module_eeprom_by_page+0xbb/0x120 [mlx5_core] eeprom_prepare_data+0xf3/0x170 ethnl_default_doit+0xf1/0x3b0 Extend the mcia_reg layout to 32 dwords. Fixes: 271907ee2f29 ("net/mlx5: Query the maximum MCIA register read size from firmware") Signed-off-by: Gal Pressman Reviewed-by: Alex Lazar Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260717072338.1240582-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/port.c | 4 ++-- include/linux/mlx5/mlx5_ifc.h | 13 +------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index ddbe9ca8971d..9f682f6bdb50 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -314,7 +314,7 @@ static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num, return -EIO; } - ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0); + ptr = MLX5_ADDR_OF(mcia_reg, out, dwords); *module_id = ptr[0]; @@ -399,7 +399,7 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev, return -EIO; } - ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0); + ptr = MLX5_ADDR_OF(mcia_reg, out, dwords); memcpy(data, ptr, size); return size; diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 695c86ee6d7a..8f18a508320d 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -12215,18 +12215,7 @@ struct mlx5_ifc_mcia_reg_bits { u8 reserved_at_60[0x20]; - u8 dword_0[0x20]; - u8 dword_1[0x20]; - u8 dword_2[0x20]; - u8 dword_3[0x20]; - u8 dword_4[0x20]; - u8 dword_5[0x20]; - u8 dword_6[0x20]; - u8 dword_7[0x20]; - u8 dword_8[0x20]; - u8 dword_9[0x20]; - u8 dword_10[0x20]; - u8 dword_11[0x20]; + u8 dwords[0x400]; }; struct mlx5_ifc_dcbx_param_bits { -- cgit v1.2.3 From d12956d083eb70f2c6d72711aebaf8c2ce21e170 Mon Sep 17 00:00:00 2001 From: Yael Chemla Date: Fri, 17 Jul 2026 10:33:06 +0300 Subject: net/mlx5: E-Switch, fix zero num_dest in prio_tag egress vlan rule esw_egress_acl_vlan_create() hardcodes num_dest=0 in its mlx5_add_flow_rules() call. When invoked from the non-bond path fwd_dest is NULL and num_dest=0 is correct. When invoked from esw_acl_egress_ofld_rules_create() during a bond event, fwd_dest is non-NULL and flow_act.action carries MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, but _mlx5_add_flow_rules() rejects a non-NULL dest pointer paired with dest_num<=0 and returns -EINVAL. The error propagates as "configure slave vport egress fwd, err(-22)". The passive vport's egress ACL table ends up with its flow groups allocated but no FTEs, so prio-tagged packets are not popped and bond failover is broken on prio_tag_required devices. Fix by passing fwd_dest ? 1 : 0 as num_dest to match the actual number of destinations supplied. Fixes: bf773dc0e6d5 ("net/mlx5: E-Switch, Introduce APIs to enable egress acl forward-to-vport rule") Signed-off-by: Yael Chemla Reviewed-by: Cosmin Ratiu Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260717073306.1242399-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c index ba5cce706ea2..9693c74e9b16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c @@ -71,7 +71,7 @@ int esw_egress_acl_vlan_create(struct mlx5_eswitch *esw, flow_act.action = flow_action; vport->egress.allowed_vlan = mlx5_add_flow_rules(vport->egress.acl, spec, - &flow_act, fwd_dest, 0); + &flow_act, fwd_dest, fwd_dest ? 1 : 0); if (IS_ERR(vport->egress.allowed_vlan)) { err = PTR_ERR(vport->egress.allowed_vlan); esw_warn(esw->dev, -- cgit v1.2.3 From def9a4745e105145133e442dd8a1c126caf0f553 Mon Sep 17 00:00:00 2001 From: Coia Prant Date: Fri, 17 Jul 2026 15:43:25 +0800 Subject: net: pcs: xpcs: fix SGMII state reading Commit 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode") added a path in xpcs_get_state_c37_sgmii() that reads speed/duplex from BMCR after AN completes. However, BMCR does not reflect the negotiated result on the hardware where this has been tested: - On RK3568 (MAC side SGMII), BMCR returns a fixed hardware reset value - Wangxun engineer Jiawen Wu confirmed that on their side, "BMCR looks like it only wants to be return as 0" [0] The correct information is available in CL37_ANSGM_STS, which contains the actual link status and negotiated speed/duplex. This bug was previously masked by phylink core, which overrides the PCS link state with the PHY state when a PHY is present: /* If we have a phy, the "up" state is the union of both the * PHY and the MAC */ if (phy) link_state.link &= pl->phy_state.link; Thus, when the link is down, the PHY's link_down state is applied on top of whatever the PCS reports, hiding the broken PCS state reading path. Modify xpcs_get_state_c37_sgmii() to: 1. Read link state from CL37_ANSGM_STS 2. If link is up, report speed/duplex from CL37_ANSGM_STS 3. Remove the broken BMCR reading path entirely Also properly set state->an_complete to reflect the AN completion status, and clear CL37_ANCMPLT_INTR when link is down to avoid stale state. [0] https://lore.kernel.org/all/000c01dd1593$2ac0b0f0$804212d0$@trustnetic.com/ Fixes: 2a22b7ae2fa3 ("net: pcs: xpcs: adapt Wangxun NICs for SGMII mode") Cc: stable@vger.kernel.org Tested-by: Jiawen Wu Signed-off-by: Coia Prant Tested-by: Maxime Chevallier Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260717074324.3250043-2-coiaprant@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/pcs/pcs-xpcs.c | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index e69fa2f0a0e8..0337e2bcc012 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -1058,6 +1058,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, /* Reset link_state */ state->link = false; + state->an_complete = false; state->speed = SPEED_UNKNOWN; state->duplex = DUPLEX_UNKNOWN; state->pause = 0; @@ -1069,6 +1070,8 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, if (ret < 0) return ret; + state->an_complete = ret & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR; + if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { int speed_value; @@ -1086,34 +1089,13 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, state->duplex = DUPLEX_FULL; else state->duplex = DUPLEX_HALF; - } else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { - int speed, duplex; - - state->link = true; - - speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMCR); - if (speed < 0) - return speed; - - speed &= BMCR_SPEED100 | BMCR_SPEED1000; - if (speed == BMCR_SPEED1000) - state->speed = SPEED_1000; - else if (speed == BMCR_SPEED100) - state->speed = SPEED_100; - else if (speed == 0) - state->speed = SPEED_10; - - duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE); - if (duplex < 0) - return duplex; - if (duplex & ADVERTISE_1000XFULL) - state->duplex = DUPLEX_FULL; - else if (duplex & ADVERTISE_1000XHALF) - state->duplex = DUPLEX_HALF; + return 0; + } + /* Clear AN complete status or interrupt */ + if (state->an_complete) xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); - } return 0; } -- cgit v1.2.3 From 35d661c98fe4733490f20b4311616a3c2c30abc0 Mon Sep 17 00:00:00 2001 From: Fabrice Derepas Date: Wed, 22 Jul 2026 19:22:30 +0300 Subject: KEYS: trusted: dcp: fix key_len validation and calc_blob_len() return type Two correctness and type-hygiene issues exist in the DCP trusted keys implementation. First, trusted_dcp_unseal() reads p->key_len from a user-supplied blob without checking if it exceeds MAX_KEY_SIZE. If a crafted blob provides a payload_len larger than 128, the subsequent do_aead_crypto() call writes past the end of the p->key array into the adjacent p->blob buffer within the same struct trusted_key_payload -- the caller's own input, not unrelated kernel memory. While not exploitable, this violates strict array bounds and triggers static analyzers. Fix this by adding a validation check against MIN_KEY_SIZE and MAX_KEY_SIZE immediately after reading the length, matching the checks already done in trusted_core.c. Second, calc_blob_len() calculates a sum in size_t that truncates to unsigned int on 64-bit platforms. Because the DCP hardware is only present on 32-bit i.MX SoC platforms, size_t and unsigned int are functionally equivalent in production, making this truncation harmless in practice. Nevertheless, updating the return type to size_t (and subsequently updating 'blen' in the seal/unseal paths) resolves type-narrowing warnings and improves overall code hygiene. Fixes: 2e8a0f40a39c ("KEYS: trusted: Introduce NXP DCP-backed trusted keys") Signed-off-by: Fabrice Derepas Reviewed-by: David Gstir Reviewed-by: Richard Weinberger Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260719163939.3624767-1-fabrice.derepas@canonical.com Signed-off-by: Jarkko Sakkinen --- security/keys/trusted-keys/trusted_dcp.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c index 7b6eb655df0c..c078adebe190 100644 --- a/security/keys/trusted-keys/trusted_dcp.c +++ b/security/keys/trusted-keys/trusted_dcp.c @@ -69,7 +69,7 @@ static bool skip_zk_test; module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0); MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed"); -static unsigned int calc_blob_len(unsigned int payload_len) +static size_t calc_blob_len(unsigned int payload_len) { return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN; } @@ -200,7 +200,8 @@ static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key) static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) { struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; - int blen, ret; + size_t blen; + int ret; u8 *plain_blob_key; blen = calc_blob_len(p->key_len); @@ -242,7 +243,8 @@ out: static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) { struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; - int blen, ret; + size_t blen; + int ret; u8 *plain_blob_key = NULL; if (b->fmt_version != DCP_BLOB_VERSION) { @@ -253,9 +255,14 @@ static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) } p->key_len = le32_to_cpu(b->payload_len); + if (p->key_len < MIN_KEY_SIZE || p->key_len > MAX_KEY_SIZE) { + ret = -EINVAL; + goto out; + } + blen = calc_blob_len(p->key_len); if (blen != p->blob_len) { - pr_err("DCP blob has bad length: %i != %i\n", blen, + pr_err("DCP blob has bad length: %zu != %u\n", blen, p->blob_len); ret = -EINVAL; goto out; -- cgit v1.2.3 From 63918731f9ae25b5deb022f118e941e6dddfcef4 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Jul 2026 12:15:03 -0400 Subject: keys: fix out-of-bounds read in keyring_get_key_chunk() For description-level chunks keyring_get_key_chunk() advances the read pointer by level * sizeof(long) past the inline prefix but only bounds-checks the prefix, so a long enough key description is read past its kmemdup(desc, desc_len + 1) allocation. Compute the full byte offset and bounds-check the description against it before reading. The walk only reaches a description-level chunk when two keys collide through the hash, x, type and domain_tag chunks, so this is reached from an unprivileged add_key(2) with a crafted pair of same-type keys whose index hashes collide; KASAN reports a slab-out-of-bounds read. Fixes: f771fde82051 ("keys: Simplify key description management") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com Signed-off-by: Jarkko Sakkinen --- security/keys/keyring.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 7a2ee0ded7c9..085f7a743354 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) unsigned long chunk = 0; const u8 *d; int desc_len = index_key->desc_len, n = sizeof(chunk); + unsigned int offset; level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; switch (level) { @@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) return (unsigned long)index_key->domain_tag; default: level -= 4; - if (desc_len <= sizeof(index_key->desc)) + offset = sizeof(index_key->desc) + level * sizeof(long); + if (desc_len <= offset) return 0; - d = index_key->description + sizeof(index_key->desc); - d += level * sizeof(long); - desc_len -= sizeof(index_key->desc); + d = index_key->description + offset; + desc_len -= offset; if (desc_len > n) desc_len = n; do { -- cgit v1.2.3 From 58565eef0f8d861aae92abfb7658458d661cee17 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Jul 2026 12:15:04 -0400 Subject: keys: make keyring key-chunk byte order agree with keyring_diff_objects() keyring_get_key_chunk() loads description bytes into the index chunk low address first, while keyring_diff_objects() numbers the first differing bit from the low end and folds the absolute byte index into the level without removing the inline-prefix offset the level already carries. The two disagree on byte order and bit position, so the array can be told two keys first differ at a bit that does not differ in the chunk the walker uses, letting crafted descriptions collide into one node. Load the chunk in the order keyring_diff_objects() assumes and drop the inline-prefix length when folding the byte index into the level. This only changes the in-memory ordering used to place keys within a keyring; add, search and read of non-colliding keys are unaffected. Fixes: f771fde82051 ("keys: Simplify key description management") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260719161505.2423935-3-michael.bommarito@gmail.com Signed-off-by: Jarkko Sakkinen --- security/keys/keyring.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 085f7a743354..15bf4af8f282 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) desc_len -= offset; if (desc_len > n) desc_len = n; + d += desc_len; do { chunk <<= 8; - chunk |= *d++; + chunk |= *--d; } while (--desc_len > 0); return chunk; } @@ -376,7 +377,7 @@ same: return -1; differ_plus_i: - level += i; + level += i - (int)sizeof(a->desc); differ: i = level * 8 + __ffs(seg_a ^ seg_b); return i; -- cgit v1.2.3 From a82c8a05e86f3f84e09698f65b4515b5d04633f6 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Jul 2026 12:15:05 -0400 Subject: assoc_array: trim the final shortcut word using the current chunk end assoc_array_walk() masks off the bits past shortcut->skip_to_level in the word that contains skip_to_level, gated on round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level. That guard is wrong in two opposite ways: - When sc_level is word-aligned (every word after the first) round_up() is a no-op, so the guard is sc_level > skip_to_level and never fires for the word that holds skip_to_level. A shortcut that spans more than one word and ends in the middle of its last word leaves that word untrimmed, and its stale high bits leak into the dissimilarity word and can steer the walk down the wrong descendant. - When sc_level is unaligned (the first word) and skip_to_level sits on the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears the whole dissimilarity word and makes a differing shortcut compare equal. Use the end of the chunk that contains sc_level instead: skip_to_level < round_down(sc_level, CHUNK) + CHUNK For an aligned sc_level whose word holds skip_to_level this now fires (the first bug); for an unaligned sc_level with skip_to_level on the following boundary it does not, so shift is never 0 when the branch runs and the trim never clears the whole word. Fixes: 3cb989501c26 ("Add a generic associative array implementation.") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Link: https://lore.kernel.org/r/20260719161505.2423935-4-michael.bommarito@gmail.com Signed-off-by: Jarkko Sakkinen --- lib/assoc_array.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/assoc_array.c b/lib/assoc_array.c index bcc6e0a013eb..b6c9723e12ce 100644 --- a/lib/assoc_array.c +++ b/lib/assoc_array.c @@ -255,7 +255,8 @@ follow_shortcut: sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT]; dissimilarity = segments ^ sc_segments; - if (round_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > shortcut->skip_to_level) { + if (shortcut->skip_to_level < round_down(sc_level, + ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) { /* Trim segments that are beyond the shortcut */ int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK; dissimilarity &= ~(ULONG_MAX << shift); -- cgit v1.2.3 From ffb1873b2df11945b8c395e859169248675c91c5 Mon Sep 17 00:00:00 2001 From: Alexei Lazar Date: Fri, 17 Jul 2026 10:51:24 +0300 Subject: net/mlx5e: Report zero bandwidth for non-ETS traffic classes The IEEE 802.1Qaz standard defines that bandwidth allocation percentages only apply to Enhanced Transmission Selection (ETS) traffic classes. For STRICT and VENDOR transmission selection algorithms, bandwidth percentage values are not applicable. Currently for non-ETS 100 bandwidth is being reported for all traffic classes in the get operation due to hardware limitation, regardless of their TSA type. Fix this by reporting 0 for non-ETS traffic classes. Fixes: 820c2c5e773d ("net/mlx5e: Read ETS settings directly from firmware") Signed-off-by: Alexei Lazar Reviewed-by: Carolina Jubran Signed-off-by: Tariq Toukan Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260717075125.1244877-2-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 4b86df6d5b9e..762f0a46c120 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -173,6 +173,13 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev, } memcpy(ets->tc_tsa, priv->dcbx.tc_tsa, sizeof(ets->tc_tsa)); + /* Report 0 for non ETS TSA */ + for (i = 0; i < ets->ets_cap; i++) { + if (ets->tc_tx_bw[i] == MLX5E_MAX_BW_ALLOC && + priv->dcbx.tc_tsa[i] != IEEE_8021QAZ_TSA_ETS) + ets->tc_tx_bw[i] = 0; + } + return err; } -- cgit v1.2.3 From 9173e1d3c7c7d49a71eee813091f9e834ec7cee5 Mon Sep 17 00:00:00 2001 From: Alexei Lazar Date: Fri, 17 Jul 2026 10:51:25 +0300 Subject: net/mlx5e: Reject unsupported CB Shaper TSA in ETS validation Credit Based (CB) TSA is not supported by the mlx5 driver, so reject any configurations that specify it. Fixes: 08fb1dacdd76 ("net/mlx5e: Support DCBNL IEEE ETS") Signed-off-by: Alexei Lazar Reviewed-by: Carolina Jubran Signed-off-by: Tariq Toukan Reviewed-by: Pavan Chebbi Link: https://patch.msgid.link/20260717075125.1244877-3-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 762f0a46c120..00e706e1ede1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -324,6 +324,14 @@ static int mlx5e_dbcnl_validate_ets(struct net_device *netdev, } } + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_CB_SHAPER) { + netdev_err(netdev, + "Failed to validate ETS: CB Shaper is not supported\n"); + return -EOPNOTSUPP; + } + } + /* Validate Bandwidth Sum */ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) { -- cgit v1.2.3 From 9dfd800b374c38681364d4b3606a8b831d1478e3 Mon Sep 17 00:00:00 2001 From: Diego Fernando Mancera Gomez Date: Fri, 17 Jul 2026 02:07:04 -0600 Subject: usb: atm: ueagle-atm: reject descriptors that confuse probe and disconnect uea_probe() distinguishes a pre-firmware device from a post-firmware one using the USB id (UEA_IS_PREFIRM()), and stores a different object as the interface data in each case: a 'struct completion' for a pre-firmware device (to be waited on in .disconnect()), or a 'struct usbatm_data' for a post-firmware one. uea_disconnect() instead tells the two apart by the number of interfaces of the active configuration (a pre-firmware device exposes a single interface, ADI930 has 2 and eagle has 3), and casts the interface data accordingly. Because the two handlers use different criteria, a crafted device that advertises a pre-firmware id together with a multi-interface descriptor (or a post-firmware id with a single interface) makes them disagree: the small 'struct completion' stored by uea_probe() is then passed to usbatm_usb_disconnect(), which casts it to 'struct usbatm_data' and takes instance->serialize, reading past the end of the allocation: BUG: KASAN: slab-out-of-bounds in __mutex_lock+0x152a/0x1b80 Read of size 8 at addr ffff8880470e2c60 by task kworker/1:2/982 ... __mutex_lock+0x152a/0x1b80 usbatm_usb_disconnect+0x70/0x820 uea_disconnect+0x133/0x2c0 usb_unbind_interface+0x1dd/0x9e0 ... which belongs to the cache kmalloc-96 of size 96 The buggy address is located 0 bytes to the right of allocated 96-byte region [ffff8880470e2c00, ffff8880470e2c60) Reject such inconsistent descriptors in uea_probe() so that both handlers always make the same pre/post-firmware decision. Reported-by: syzbot+e62a973f8322b3bbe3ac@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e62a973f8322b3bbe3ac Fixes: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()") Signed-off-by: Diego Fernando Mancera Gomez Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260717080704.1264-1-diegomancera.dev@gmail.com Signed-off-by: Jakub Kicinski --- drivers/usb/atm/ueagle-atm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 4e71ed679a76..4266a0cb7e3b 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2549,6 +2549,7 @@ static struct usbatm_driver uea_usbatm_driver = { static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *usb = interface_to_usbdev(intf); + bool single_iface = usb->config->desc.bNumInterfaces == 1; int ret; uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", @@ -2557,6 +2558,22 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) le16_to_cpu(usb->descriptor.bcdDevice), chip_name[UEA_CHIP_VERSION(id)]); + /* + * uea_probe() decides between the pre-firmware and post-firmware case + * from the USB id and stores a different object as interface data in + * each case: a struct completion for a pre-firmware device, a struct + * usbatm_data for a post-firmware one. uea_disconnect() instead tells + * the two apart by the number of interfaces (a pre-firmware device + * exposes a single interface, ADI930 has 2 and eagle has 3). A crafted + * device advertising a pre-firmware id together with a multi-interface + * descriptor (or the other way around) makes the two disagree, so that + * usbatm_usb_disconnect() treats the small completion object as a + * struct usbatm_data and reads out of bounds. Reject such inconsistent + * descriptors so both paths make the same decision. + */ + if (UEA_IS_PREFIRM(id) != single_iface) + return -ENODEV; + usb_reset_device(usb); if (UEA_IS_PREFIRM(id)) { -- cgit v1.2.3 From a28c4fcbf774e23b4779cae468e3497a5ad1f4a1 Mon Sep 17 00:00:00 2001 From: Yuxiang Yang Date: Fri, 17 Jul 2026 08:14:42 +0000 Subject: tcp: challenge ACK for non-exact RST in SYN-RECEIVED The SYN-RECEIVED request-socket path in tcp_check_req() accepts an in-window RST without requiring SEG.SEQ to exactly match RCV.NXT. A non-exact RST therefore removes the request instead of eliciting a challenge ACK. RFC 9293 section 3.10.7.4 applies the RFC 5961 reset check in SYN-RECEIVED: an exact RST resets the connection, while a non-exact in-window RST must trigger a challenge ACK and be dropped. Apply that check before the ACK-field validation, following the RFC sequence-number, RST, then ACK processing order. Factor the per-netns challenge ACK quota out of tcp_send_challenge_ack() so request sockets can share it. Use the request socket's send_ack() callback and its own out-of-window ACK timestamp to send and rate-limit the response. Reported-by: Yuxiang Yang Reported-by: Yizhou Zhao Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Fixes: 282f23c6ee34 ("tcp: implement RFC 5961 3.2") Cc: stable@vger.kernel.org Signed-off-by: Yuxiang Yang Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260717081443.809393-2-yangyx22@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- include/net/tcp.h | 2 ++ net/ipv4/tcp_input.c | 56 +++++++++++++++++++++++++++++++++++++----------- net/ipv4/tcp_minisocks.c | 12 ++++++++++- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 6d376ea4d1c0..2c5b889530b5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1974,6 +1974,8 @@ static inline void tcp_fast_path_check(struct sock *sk) bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb, int mib_idx, u32 *last_oow_ack_time); +void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req); static inline void tcp_mib_init(struct net *net) { diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 61045a8886e4..daff93d51342 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4038,24 +4038,17 @@ static void tcp_send_ack_reflect_ect(struct sock *sk, bool accecn_reflector) __tcp_send_ack(sk, tp->rcv_nxt, flags); } -/* RFC 5961 7 [ACK Throttling] */ -static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) +/* Consume one slot from the per-netns RFC 5961 challenge ACK quota. + * Returns true if a challenge ACK may be sent. + */ +static bool tcp_challenge_ack_allowed(struct net *net) { - struct tcp_sock *tp = tcp_sk(sk); - struct net *net = sock_net(sk); u32 count, now, ack_limit; - /* First check our per-socket dupack rate limit. */ - if (__tcp_oow_rate_limited(net, - LINUX_MIB_TCPACKSKIPPEDCHALLENGE, - &tp->last_oow_ack_time)) - return; - ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit); if (ack_limit == INT_MAX) - goto send_ack; + return true; - /* Then check host-wide RFC 5961 rate limit. */ now = jiffies / HZ; if (now != READ_ONCE(net->ipv4.tcp_challenge_timestamp)) { u32 half = (ack_limit + 1) >> 1; @@ -4067,12 +4060,49 @@ static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) count = READ_ONCE(net->ipv4.tcp_challenge_count); if (count > 0) { WRITE_ONCE(net->ipv4.tcp_challenge_count, count - 1); -send_ack: + return true; + } + return false; +} + +/* RFC 5961 7 [ACK Throttling] */ +static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct net *net = sock_net(sk); + + /* First check our per-socket dupack rate limit. */ + if (__tcp_oow_rate_limited(net, + LINUX_MIB_TCPACKSKIPPEDCHALLENGE, + &tp->last_oow_ack_time)) + return; + + /* Then check the per-netns RFC 5961 rate limit. */ + if (tcp_challenge_ack_allowed(net)) { NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); tcp_send_ack_reflect_ect(sk, accecn_reflector); } } +/* Send a challenge ACK from a SYN-RECEIVED request socket. Uses + * __tcp_oow_rate_limited() directly so that an RST carrying payload + * cannot bypass the per-request rate limit. + */ +void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) +{ + struct net *net = sock_net(sk); + + if (__tcp_oow_rate_limited(net, LINUX_MIB_TCPACKSKIPPEDCHALLENGE, + &tcp_rsk(req)->last_oow_ack_time)) + return; + + if (tcp_challenge_ack_allowed(net)) { + NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); + req->rsk_ops->send_ack(sk, skb, req); + } +} + static void tcp_store_ts_recent(struct tcp_sock *tp) { tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index ddc4b17a826b..6ab3e3a0b431 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -833,7 +833,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * elsewhere and is checked directly against the child socket rather * than req because user data may have been sent out. */ - if ((flg & TCP_FLAG_ACK) && !fastopen && + if ((flg & TCP_FLAG_ACK) && !(flg & TCP_FLAG_RST) && !fastopen && (TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1)) return sk; @@ -872,6 +872,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, flg &= ~TCP_FLAG_SYN; } + /* RFC 5961 section 3.2, as clarified by RFC 9293 section + * 3.10.7.4, requires a challenge ACK for a non-exact + * in-window RST in SYN-RECEIVED. + */ + if ((flg & TCP_FLAG_RST) && + TCP_SKB_CB(skb)->seq != tcp_rsk(req)->rcv_nxt) { + tcp_reqsk_send_challenge_ack(sk, skb, req); + return NULL; + } + /* RFC793: "second check the RST bit" and * "fourth, check the SYN bit" */ -- cgit v1.2.3 From 7bb18355e5996a70b15ad571f5597e13d61af00d Mon Sep 17 00:00:00 2001 From: Yuxiang Yang Date: Fri, 17 Jul 2026 08:14:43 +0000 Subject: selftests/net: packetdrill: cover RST validation in SYN-RECEIVED Add packetdrill coverage for the RFC 9293 reset checks on request sockets in SYN-RECEIVED. Verify that an exact RST removes the request, a non-exact in-window RST sends a challenge ACK without removing it, and an out-of-window RST is silently discarded. Also cover an RST|ACK with an unacceptable ACK number to ensure RST sequence validation runs before ACK-field validation. Signed-off-by: Yuxiang Yang Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260717081443.809393-3-yangyx22@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- .../net/packetdrill/tcp_rfc5961_rst-syn-recv.pkt | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tools/testing/selftests/net/packetdrill/tcp_rfc5961_rst-syn-recv.pkt diff --git a/tools/testing/selftests/net/packetdrill/tcp_rfc5961_rst-syn-recv.pkt b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_rst-syn-recv.pkt new file mode 100644 index 000000000000..3fc2de03658a --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_rst-syn-recv.pkt @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// RFC 9293 Section 3.10.7.4: in SYN-RECEIVED, an exact RST resets +// the connection. A non-exact in-window RST elicits a challenge ACK, +// while an out-of-window RST is silently discarded. + +`./defaults.sh` + +// An exact RST removes the request socket. + 0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0 < S 0:0(0) win 1000 + +0 > S. 0:0(0) ack 1 <...> + +0 < R 1:1(0) win 1000 + +.1 < . 1:1(0) ack 1 win 1000 + +0 > R 1:1(0) + +0 close(3) = 0 + +// A non-exact in-window RST gets a challenge ACK and the request survives. + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0 < S 0:0(0) win 1000 + +0 > S. 0:0(0) ack 1 <...> + +0 < R 2:2(0) win 1000 + +0 > . 1:1(0) ack 1 + +0 < . 1:1(0) ack 1 win 1000 + +0 accept(3, ..., ...) = 4 + +0 close(4) = 0 + +0 close(3) = 0 + +// RST sequence validation precedes ACK validation. Even an RST|ACK +// with an unacceptable ACK value gets a challenge ACK. + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0 < S 0:0(0) win 1000 + +0 > S. 0:0(0) ack 1 <...> + +0 < R. 2:2(0) ack 100 win 1000 + +0 > . 1:1(0) ack 1 + +0 < . 1:1(0) ack 1 win 1000 + +0 accept(3, ..., ...) = 4 + +0 close(4) = 0 + +0 close(3) = 0 + +// An out-of-window RST is silent and does not remove the request. + +0 socket(..., SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +0 < S 0:0(0) win 1000 + +0 > S. 0:0(0) ack 1 <...> + +0 < R 100001:100001(0) win 1000 + +.1 < . 1:1(0) ack 1 win 1000 + +0 accept(3, ..., ...) = 4 + +0 close(4) = 0 + +0 close(3) = 0 -- cgit v1.2.3 From bb0d96ebe5f4d1acccf4dc36ca7f01f9a8fa1ba1 Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Fri, 17 Jul 2026 14:13:49 +0530 Subject: octeontx2-pf: tc: fix egress ratelimiting The egress rate calculation computes an incorrect mantissa and exponent, causing up to ~50% deviation from the configured rate at lower speeds. Rework the computation to follow the hardware rate formula: rate = 2 * (1 + mantissa/256) * 2^exp / (1 << div_exp) Keep div_exp = 0 and derive exp and mantissa from half of the requested rate. Rates below 2 Mbps are floored to the smallest encodable step (exp = 0, mantissa = 0). Fixes: e638a83f167e ("octeontx2-pf: TC_MATCHALL egress ratelimiting offload") Signed-off-by: Hariprasad Kelam Signed-off-by: Nitin Shetty J Link: https://patch.msgid.link/20260717084349.2227796-1-nshettyj@marvell.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/marvell/octeontx2/nic/otx2_tc.c | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index 40162b08014d..0b46ec29e64e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -30,6 +30,7 @@ #define OTX2_UNSUPP_LSE_DEPTH GENMASK(6, 4) #define MCAST_INVALID_GRP (-1U) +#define RATE_MANTISSA_BITS 8 static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, u32 *burst_exp, u32 *burst_mantissa) @@ -66,28 +67,30 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp, u32 *mantissa, u32 *div_exp) { - u64 tmp; - /* Rate calculation by hardware * * PIR_ADD = ((256 + mantissa) << exp) / 256 * rate = (2 * PIR_ADD) / ( 1 << div_exp) * The resultant rate is in Mbps. + * + * Use div_exp = 0 and compute exp/mantissa for maxrate / 2; the + * leading factor of two yields the full rate. Rates below 2 Mbps + * are floored to the smallest step (exp = 0, mantissa = 0). */ - /* 2Mbps to 100Gbps can be expressed with div_exp = 0. - * Setting this to '0' will ease the calculation of - * exponent and mantissa. - */ *div_exp = 0; - if (maxrate) { - *exp = ilog2(maxrate) ? ilog2(maxrate) - 1 : 0; - tmp = maxrate - rounddown_pow_of_two(maxrate); - if (maxrate < MAX_RATE_MANTISSA) - *mantissa = tmp * 2; - else - *mantissa = tmp / (1ULL << (*exp - 7)); + maxrate = maxrate / 2; + if (!maxrate) { + /* Rates below 2 Mbps map to the smallest step */ + *exp = 0; + *mantissa = 0; + } else { + *exp = ilog2(maxrate); + /* Clear MSB and derive fractional bits */ + maxrate &= ~BIT(*exp); + *mantissa = (maxrate << RATE_MANTISSA_BITS) >> *exp; + } } else { /* Instead of disabling rate limiting, set all values to max */ *exp = MAX_RATE_EXPONENT; -- cgit v1.2.3 From 440e274da4d1b93c7df2cb0ce893c3009dd4db55 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 17 Jul 2026 22:32:30 +0800 Subject: net: ipv6: fix dif and sdif mismatch in raw6_icmp_error In raw6_icmp_error(), raw_v6_match() is called with inet6_iif(skb) passed to both the 'dif' and 'sdif' arguments. This is a copy-paste or typo error, as the last argument should represent the secondary interface index (sdif). This mismatch breaks ICMPv6 error handling for IPv6 raw sockets in VRF (Virtual Routing and Forwarding) environments. When a raw socket is bound to a VRF master device, raw_v6_match() fails to find a match because it is not given the correct sdif value, causing the socket to miss relevant ICMPv6 error notifications. Fix this by properly passing inet6_sdif(skb) as the last argument to raw_v6_match(). Fixes: 5108ab4bf446fa ("net: ipv6: add second dif to raw socket lookups") Signed-off-by: Li RongQing Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260717143230.1836-1-lirongqing@baidu.com Signed-off-by: Jakub Kicinski --- net/ipv6/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 3cc58698cbbd..b88d364e78aa 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -349,7 +349,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr, const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data; if (!raw_v6_match(net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr, - inet6_iif(skb), inet6_iif(skb))) + inet6_iif(skb), inet6_sdif(skb))) continue; rawv6_err(sk, skb, type, code, inner_offset, info); } -- cgit v1.2.3 From f30415929be8aeb002d557c8d3f7ab2d2188003a Mon Sep 17 00:00:00 2001 From: David Lee Date: Mon, 13 Jul 2026 09:59:15 +0000 Subject: netfilter: ipset: do not update comments from kernel-side hash adds mtype_resize() copies comment pointers with memcpy(), not the comment objects themselves. During the window after an entry has been copied but before the table swap and backlog replay, the old table is still published for packet-side updates while the replacement-table entry already holds the same ip_set_comment_rcu pointer. If xt_SET --add-set ... --exist hits that old entry in this window, mtype_add() calls ip_set_init_comment() even though packet-side adds carry no comment payload. That call frees the shared comment through the old entry, so the replacement-table entry now holds a stale pointer. When the queued add is replayed on the new table, mtype_add() calls ip_set_init_comment() again and strlen() dereferences the stale pointer. Fix this in mtype_add() by skipping ip_set_init_comment() when ext->target marks a packet-side add. Userspace adds still update comments, while packet-side adds can no longer free comment storage shared with a resize copy. Fixes: f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports") Cc: stable@vger.kernel.org Signed-off-by: David Lee Assisted-by: Codex:gpt-5.5 Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 8231317b0f1f..b2d77973272d 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1005,7 +1005,7 @@ overwrite_extensions: #endif if (SET_WITH_COUNTER(set)) ip_set_init_counter(ext_counter(data, set), ext); - if (SET_WITH_COMMENT(set)) + if (SET_WITH_COMMENT(set) && !ext->target) ip_set_init_comment(set, ext_comment(data, set), ext); if (SET_WITH_SKBINFO(set)) ip_set_init_skbinfo(ext_skbinfo(data, set), ext); -- cgit v1.2.3 From a63d2dbaeb50a85d4c976b15a36e6b0c7113db5b Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Mon, 13 Jul 2026 19:52:32 +0800 Subject: ipvs: do not propagate one-packet flag to synced conns Synced connections can be created before their destination exists. When the destination is later added, ip_vs_bind_dest() copies connection flags from the destination into cp->flags. IP_VS_CONN_F_ONE_PACKET connections are not synced. If a synced connection inherits IP_VS_CONN_F_ONE_PACKET while it is already hashed, expiry can treat it as a one-packet connection and skip unlinking the existing conn_tab node, leaving stale hash nodes pointing at a freed struct ip_vs_conn. Drop IP_VS_CONN_F_ONE_PACKET from destination flags when binding synced connections. Fixes: 26ec037f9841 ("IPVS: one-packet scheduling") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Suggested-by: Julian Anastasov Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_conn.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 6ed2622363f0..0682cec5f0a7 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1014,6 +1014,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) flags = cp->flags; /* Bind with the destination and its corresponding transmitter */ if (flags & IP_VS_CONN_F_SYNC) { + /* Synced conns are hashed, so they can not get this flag */ + conn_flags &= ~IP_VS_CONN_F_ONE_PACKET; + /* if the connection is not template and is created * by sync, preserve the activity flag. */ -- cgit v1.2.3 From 712d2993bea555f1f09cd53cbdb25714f28e85db Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Mon, 13 Jul 2026 19:52:33 +0800 Subject: ipvs: adjust double hashing when fwd method changes Synced conns can be created with one forwarding method and later updated with different one after the dest server is configured. This needs adjusting the hashing for node hn1 because only MASQ supports double hashing. Modify conn_tab_lock() to support seeking for hash node hn0 together with adding for hn1. By this way we can safely modify the forwarding method and hn1.hash_key under bucket lock for the first node hn0. The forwarding method is also protected by cp->lock as it is part of cp->flags. Fix the usage of stale idx/idx2 values in conn_tab_lock after jumping to the retry label. Instead, use idx/idx2 values just to order the locking for the old/new tables. Reported-by: Zhiling Zou Link: https://patch.msgid.link/1b914f41d725bc064c9ba9830dc8169329737270.1782540466.git.roxy520tt@gmail.com/ Link: https://sashiko.dev/#/patchset/CALMqdkR704S2BG_QD_bgHTFp2%2B1QCi7n0T4zoZyTo8mDZevYSA%40mail.gmail.com Fixes: f20c73b0460d ("ipvs: use more keys for connection hashing") Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_conn.c | 189 +++++++++++++++++++++++++++++++--------- 1 file changed, 147 insertions(+), 42 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 0682cec5f0a7..36c5cba03f5b 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -70,25 +70,45 @@ static struct kmem_cache *ip_vs_conn_cachep __read_mostly; * bucket or hash table * - hash table resize works like rehash but always rehashes into new table * - bit lock on bucket serializes all operations that modify the chain + * - on resize, bucket from the old table is locked before bucket from the + * new table * - cp->lock protects conn fields like cp->flags, cp->dest */ -/* Lock conn_tab bucket for conn hash/unhash, not for rehash */ +/** + * conn_tab_lock - Lock conn_tab buckets for conn hash/unhash, not for rehash + * @t: hash table for hn0, new_tbl when new_hash=true + * @t2: hash table for hn1, new_tbl when new_hash2=true + * @cp: connection + * @hash_key: hash key for hn0 + * @hash_key2: hash key for hn1 + * @use2: using hn1 (double hashing) based on the forwarding method + * @new_hash: mode for hn0, hash node (true) or seek node (false) + * @new_hash2: mode for hn1, hash node (true) or seek node (false) + * @head_ret: returned head for hn0 + * @head2_ret: returned head for hn1 + * + * We support 3 modes: + * - seek mode for both nodes, used for unhashing + * - hash mode for both nodes, used for hashing + * - seek hn0 and hash hn1, used when forwarding method is changed + */ static __always_inline void -conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key, - u32 hash_key2, bool use2, bool new_hash, - struct hlist_bl_head **head_ret, struct hlist_bl_head **head2_ret) +conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_rht *t2, struct ip_vs_conn *cp, + u32 hash_key, u32 hash_key2, bool use2, bool new_hash, + bool new_hash2, struct hlist_bl_head **head_ret, + struct hlist_bl_head **head2_ret) { struct hlist_bl_head *head, *head2; u32 hash_key_new, hash_key_new2; - struct ip_vs_rht *t2 = t; - u32 idx, idx2; + int idx = 0, idx2 = 0; + + /* Advance idx2 when new_hash is not set but hash_key2 + * is for new table + */ + if (new_hash2 && use2 && t != t2) + idx2++; - idx = hash_key & t->mask; - if (use2) - idx2 = hash_key2 & t->mask; - else - idx2 = idx; if (!new_hash) { /* We need to lock the bucket in the right table */ @@ -100,46 +120,45 @@ retry: * both nodes in different tables, use idx/idx2 * for proper lock ordering for heads. */ - idx = hash_key & t->mask; - idx |= IP_VS_RHT_TABLE_ID_MASK; - } - if (use2) { - if (!ip_vs_rht_same_table(t2, hash_key2)) { - /* It is already moved to new table */ - t2 = rcu_dereference(t2->new_tbl); - idx2 = hash_key2 & t2->mask; - idx2 |= IP_VS_RHT_TABLE_ID_MASK; - } - } else { - idx2 = idx; + idx++; } } + if (use2 && !new_hash2 && !ip_vs_rht_same_table(t2, hash_key2)) { + /* It is already moved to new table */ + t2 = rcu_dereference(t2->new_tbl); + idx2++; + } + if (!use2) + idx2 = idx; head = t->buckets + (hash_key & t->mask); head2 = use2 ? t2->buckets + (hash_key2 & t2->mask) : head; - local_bh_disable(); - /* Do not touch seqcount, this is a safe operation */ - - if (idx <= idx2) { + if (idx > idx2 || (head > head2 && idx == idx2)) { + hlist_bl_lock(head2); hlist_bl_lock(head); - if (head != head2) - hlist_bl_lock(head2); } else { - hlist_bl_lock(head2); hlist_bl_lock(head); + if (head != head2) + hlist_bl_lock(head2); } if (!new_hash) { + bool changed; + /* Ensure hash_key is read under lock */ hash_key_new = READ_ONCE(cp->hn0.hash_key); - hash_key_new2 = READ_ONCE(cp->hn1.hash_key); + changed = hash_key != hash_key_new; + if (use2 && !new_hash2) { + hash_key_new2 = READ_ONCE(cp->hn1.hash_key); + changed |= hash_key2 != hash_key_new2; + } else { + hash_key_new2 = hash_key2; + } /* Hash changed ? */ - if (hash_key != hash_key_new || - (hash_key2 != hash_key_new2 && use2)) { + if (changed) { if (head != head2) hlist_bl_unlock(head2); hlist_bl_unlock(head); - local_bh_enable(); hash_key = hash_key_new; hash_key2 = hash_key_new2; goto retry; @@ -155,7 +174,6 @@ static inline void conn_tab_unlock(struct hlist_bl_head *head, if (head != head2) hlist_bl_unlock(head2); hlist_bl_unlock(head); - local_bh_enable(); } static void ip_vs_conn_expire(struct timer_list *t); @@ -268,8 +286,9 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) use2 = false; } - conn_tab_lock(t, cp, hash_key, hash_key2, use2, true /* new_hash */, - &head, &head2); + local_bh_disable(); + conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, true /* new_hash */, + true /* new_hash2 */, &head, &head2); cp->flags |= IP_VS_CONN_F_HASHED; WRITE_ONCE(cp->hn0.hash_key, hash_key); @@ -280,6 +299,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) hlist_bl_add_head_rcu(&cp->hn1.node, head2); conn_tab_unlock(head, head2); + local_bh_enable(); ret = 1; /* Schedule resizing if load increases */ @@ -306,18 +326,20 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) return refcount_dec_if_one(&cp->refcnt); rcu_read_lock(); + local_bh_disable(); t = rcu_dereference(ipvs->conn_tab); hash_key = READ_ONCE(cp->hn0.hash_key); hash_key2 = READ_ONCE(cp->hn1.hash_key); use2 = ip_vs_conn_use_hash2(cp); - conn_tab_lock(t, cp, hash_key, hash_key2, use2, false /* new_hash */, - &head, &head2); + conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, false /* new_hash */, + false /* new_hash2 */, &head, &head2); if (cp->flags & IP_VS_CONN_F_HASHED) { /* Decrease refcnt and unlink conn only if we are last user */ - if (refcount_dec_if_one(&cp->refcnt)) { + if (use2 == ip_vs_conn_use_hash2(cp) && + refcount_dec_if_one(&cp->refcnt)) { hlist_bl_del_rcu(&cp->hn0.node); if (use2) hlist_bl_del_rcu(&cp->hn1.node); @@ -328,6 +350,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) conn_tab_unlock(head, head2); + local_bh_enable(); rcu_read_unlock(); return ret; @@ -632,6 +655,7 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport) int ntbl; int dir; +restart: /* No packets from inside, so we can do it in 2 steps. */ dir = use2 ? 1 : 0; @@ -686,6 +710,23 @@ retry: /* Protect the cp->flags modification */ spin_lock_bh(&cp->lock); + /* Recheck the forwarding method under lock */ + if (use2 != ip_vs_conn_use_hash2(cp)) { + use2 = !use2; + if (use2) { + spin_unlock_bh(&cp->lock); + /* Restart with new use2 value */ + goto restart; + } + if (dir) { + /* Not started yet, so just skip dir 1 */ + spin_unlock_bh(&cp->lock); + dir--; + goto next_dir; + } + /* Just finish dir 0 */ + } + /* Lock seqcount only for the old bucket, even if we are on new table * because it affects the del operation, not the adding. */ @@ -752,6 +793,61 @@ retry: goto next_dir; } +/* Change forwarding method for hashed conn */ +static void ip_vs_conn_change_fwd_mask(struct ip_vs_conn *cp, u32 new_flags) +{ + struct netns_ipvs *ipvs = cp->ipvs; + struct hlist_bl_head *head, *head2; + u32 hash2, hash_key, hash_key2; + struct ip_vs_rht *t, *t2; + + /* See ip_vs_conn_use_hash2() for reference */ + if ((cp->flags & IP_VS_CONN_F_TEMPLATE) || + /* No change in double hashing ? */ + (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ) == + ((new_flags & IP_VS_CONN_F_FWD_MASK) == IP_VS_CONN_F_MASQ)) { + cp->flags = new_flags; + return; + } + t = rcu_dereference(ipvs->conn_tab); + if (ip_vs_conn_use_hash2(cp)) { + /* Stop double hashing */ + hash_key = READ_ONCE(cp->hn0.hash_key); + hash_key2 = READ_ONCE(cp->hn1.hash_key); + + conn_tab_lock(t, t, cp, hash_key, hash_key2, true /* use2 */, + false /* new_hash */, false /* new_hash2 */, + &head, &head2); + + /* Keep both hash keys in same table */ + hash_key = READ_ONCE(cp->hn0.hash_key); + WRITE_ONCE(cp->hn1.hash_key, hash_key); + hlist_bl_del_rcu(&cp->hn1.node); + cp->flags = new_flags; + + conn_tab_unlock(head, head2); + } else { + /* Start double hashing */ + + hash_key = READ_ONCE(cp->hn0.hash_key); + + t2 = rcu_dereference(t->new_tbl); + hash2 = ip_vs_conn_hashkey_conn(t2, cp, true); + hash_key2 = ip_vs_rht_build_hash_key(t2, hash2); + + /* Change the forwarding method under locked hn0 */ + conn_tab_lock(t, t2, cp, hash_key, hash_key2, true /* use2 */, + false /* new_hash */, true /* new_hash2 */, + &head, &head2); + + WRITE_ONCE(cp->hn1.hash_key, hash_key2); + cp->flags = new_flags; + hlist_bl_add_head_rcu(&cp->hn1.node, head2); + + conn_tab_unlock(head, head2); + } +} + /* Get default load factor to map conn_count/u_thresh to t->size */ static int ip_vs_conn_default_load_factor(struct netns_ipvs *ipvs) { @@ -1024,9 +1120,18 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) conn_flags &= ~IP_VS_CONN_F_INACTIVE; /* connections inherit forwarding method from dest */ flags &= ~(IP_VS_CONN_F_FWD_MASK | IP_VS_CONN_F_NOOUTPUT); + flags |= conn_flags; + /* Changing forwarding method for hashed conn can + * happen only under locks + */ + if (cp->flags & IP_VS_CONN_F_HASHED) + ip_vs_conn_change_fwd_mask(cp, flags); + else + cp->flags = flags; + } else { + flags |= conn_flags; + cp->flags = flags; } - flags |= conn_flags; - cp->flags = flags; cp->dest = dest; IP_VS_DBG_BUF(7, "Bind-dest %s c:%s:%d v:%s:%d " -- cgit v1.2.3 From f4f699790590bd0896c48a71e9232a65198f92f0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 16 Jul 2026 10:13:37 +0200 Subject: netfilter: nf_tables: make nft_object rhltable per table The nft_object rhltable is global, this allows for accessing objects that are being dismangled from lookup path by other existing netns. Given the nft_obj_destroy() releases the object inmediately, this might lead to use-after-free of these objects that are being released. Make the existing rhltable per table to address this issue to deal with with the nft_rcv_nl_event() path too. Update nft_obj_lookup() to take the table as non-const, otherwise, compiler complains when passing the objname_ht to rhltable_lookup(). Fixes: 4d44175aa5bb ("netfilter: nf_tables: handle nft_object lookups via rhltable") Suggested-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 4 +++- net/netfilter/nf_tables_api.c | 34 ++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 9d844354c4d9..3be612145c13 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1294,6 +1294,7 @@ static inline void nft_use_inc_restore(u32 *use) * @sets: sets in the table * @objects: stateful objects in the table * @flowtables: flow tables in the table + * @objname_ht: hashtable for objects lookup by name * @hgenerator: handle generator state * @handle: table handle * @use: number of chain references to this table @@ -1313,6 +1314,7 @@ struct nft_table { struct list_head sets; struct list_head objects; struct list_head flowtables; + struct rhltable objname_ht; u64 hgenerator; u64 handle; u32 use; @@ -1400,7 +1402,7 @@ static inline void *nft_obj_data(const struct nft_object *obj) #define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr)) struct nft_object *nft_obj_lookup(const struct net *net, - const struct nft_table *table, + struct nft_table *table, const struct nlattr *nla, u32 objtype, u8 genmask); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index a9eaf9455c77..af357f6c5070 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -45,8 +45,6 @@ enum { NFT_VALIDATE_DO, }; -static struct rhltable nft_objname_ht; - static u32 nft_chain_hash(const void *data, u32 len, u32 seed); static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed); static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *); @@ -1635,6 +1633,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, if (err) goto err_chain_ht; + err = rhltable_init(&table->objname_ht, &nft_objname_ht_params); + if (err < 0) + goto err_obj_ht; + INIT_LIST_HEAD(&table->chains); INIT_LIST_HEAD(&table->sets); INIT_LIST_HEAD(&table->objects); @@ -1653,6 +1655,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info, list_add_tail_rcu(&table->list, &nft_net->tables); return 0; err_trans: + rhltable_destroy(&table->objname_ht); +err_obj_ht: rhltable_destroy(&table->chains_ht); err_chain_ht: kfree(table->udata); @@ -1819,6 +1823,7 @@ static void nf_tables_table_destroy(struct nft_table *table) return; rhltable_destroy(&table->chains_ht); + rhltable_destroy(&table->objname_ht); kfree(table->name); kfree(table->udata); kfree(table); @@ -8086,7 +8091,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type) EXPORT_SYMBOL_GPL(nft_unregister_obj); struct nft_object *nft_obj_lookup(const struct net *net, - const struct nft_table *table, + struct nft_table *table, const struct nlattr *nla, u32 objtype, u8 genmask) { @@ -8102,7 +8107,7 @@ struct nft_object *nft_obj_lookup(const struct net *net, !lockdep_commit_lock_is_held(net)); rcu_read_lock(); - list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params); + list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params); if (!list) goto out; @@ -8382,7 +8387,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info, if (err < 0) goto err_trans; - err = rhltable_insert(&nft_objname_ht, &obj->rhlhead, + err = rhltable_insert(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); if (err < 0) goto err_obj_ht; @@ -8567,8 +8572,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info, struct netlink_ext_ack *extack = info->extack; u8 genmask = nft_genmask_cur(info->net); u8 family = info->nfmsg->nfgen_family; - const struct nft_table *table; struct net *net = info->net; + struct nft_table *table; struct nft_object *obj; struct sk_buff *skb2; u32 objtype; @@ -10437,9 +10442,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain) nf_tables_commit_chain_free_rules_old(g0); } -static void nft_obj_del(struct nft_object *obj) +static void nft_obj_del(struct nft_table *table, struct nft_object *obj) { - rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params); + rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params); list_del_rcu(&obj->list); } @@ -11124,7 +11129,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) break; case NFT_MSG_DELOBJ: case NFT_MSG_DESTROYOBJ: - nft_obj_del(nft_trans_obj(trans)); + nft_obj_del(table, nft_trans_obj(trans)); nf_tables_obj_notify(&ctx, nft_trans_obj(trans), trans->msg_type); break; @@ -11416,7 +11421,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) nft_trans_destroy(trans); } else { nft_use_dec_restore(&table->use); - nft_obj_del(nft_trans_obj(trans)); + nft_obj_del(table, nft_trans_obj(trans)); } break; case NFT_MSG_DELOBJ: @@ -12043,7 +12048,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table) nft_set_destroy(&ctx, set); } list_for_each_entry_safe(obj, ne, &table->objects, list) { - nft_obj_del(obj); + nft_obj_del(table, obj); nft_use_dec(&table->use); nft_obj_destroy(&ctx, obj); } @@ -12225,10 +12230,6 @@ static int __init nf_tables_module_init(void) if (err < 0) goto err_netdev_notifier; - err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params); - if (err < 0) - goto err_rht_objname; - err = nft_offload_init(); if (err < 0) goto err_offload; @@ -12251,8 +12252,6 @@ err_nfnl_subsys: err_netlink_notifier: nft_offload_exit(); err_offload: - rhltable_destroy(&nft_objname_ht); -err_rht_objname: unregister_netdevice_notifier(&nf_tables_flowtable_notifier); err_netdev_notifier: nf_tables_core_module_exit(); @@ -12274,7 +12273,6 @@ static void __exit nf_tables_module_exit(void) unregister_pernet_subsys(&nf_tables_net_ops); cancel_work_sync(&trans_gc_work); rcu_barrier(); - rhltable_destroy(&nft_objname_ht); nf_tables_core_module_exit(); } -- cgit v1.2.3 From 99d0f42b0e5c57e4c02070a908aaff082881293a Mon Sep 17 00:00:00 2001 From: Vincent Chen Date: Fri, 17 Jul 2026 11:53:23 -0700 Subject: ice: allow creating VFs when !CONFIG_ICE_SWITCHDEV Currently ice_eswitch_attach_vf() is called unconditionally in ice_start_vfs(), which causes VF creation to fail when CONFIG_ICE_SWITCHDEV is not defined. Fix this by adding switchdev mode checks at the call sites before calling ice_eswitch_attach_vf(), consistent with how ice_eswitch_attach_sf() is already handled in ice_devlink_port_new(). This is similar to commit aacca7a83b97 ("ice: allow creating VFs for !CONFIG_NET_SWITCHDEV") which fixed the same issue for the previous ice_eswitch_configure() API. Fixes: 415db8399d06 ("ice: make representor code generic") Signed-off-by: Vincent Chen Reviewed-by: Aleksandr Loktionov Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-2-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_eswitch.c | 3 --- drivers/net/ethernet/intel/ice/ice_sriov.c | 14 ++++++++------ drivers/net/ethernet/intel/ice/ice_vf_lib.c | 3 ++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c index c30e27bbfe6e..b069e6c514fb 100644 --- a/drivers/net/ethernet/intel/ice/ice_eswitch.c +++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c @@ -512,9 +512,6 @@ int ice_eswitch_attach_vf(struct ice_pf *pf, struct ice_vf *vf) struct ice_repr *repr; int err; - if (!ice_is_eswitch_mode_switchdev(pf)) - return 0; - repr = ice_repr_create_vf(vf); if (IS_ERR(repr)) return PTR_ERR(repr); diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 7e00e091756d..e04de0215596 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -484,12 +484,14 @@ static int ice_start_vfs(struct ice_pf *pf) goto teardown; } - retval = ice_eswitch_attach_vf(pf, vf); - if (retval) { - dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d", - vf->vf_id, retval); - ice_vf_vsi_release(vf); - goto teardown; + if (ice_is_eswitch_mode_switchdev(pf)) { + retval = ice_eswitch_attach_vf(pf, vf); + if (retval) { + dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d", + vf->vf_id, retval); + ice_vf_vsi_release(vf); + goto teardown; + } } set_bit(ICE_VF_STATE_INIT, vf->vf_states); diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 27e4acb1620f..9052e71e9c99 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -812,7 +812,8 @@ void ice_reset_all_vfs(struct ice_pf *pf) } ice_vf_post_vsi_rebuild(vf); - ice_eswitch_attach_vf(pf, vf); + if (ice_is_eswitch_mode_switchdev(pf)) + ice_eswitch_attach_vf(pf, vf); mutex_unlock(&vf->cfg_lock); } -- cgit v1.2.3 From 2d19302f628853742c4828381abbd668c1315598 Mon Sep 17 00:00:00 2001 From: Michal Swiatkowski Date: Fri, 17 Jul 2026 11:53:25 -0700 Subject: ice: pass the return value of skb_checksum_help() skb_checksum_help() can fail. Pass its return value back to the caller. Commonize this software path in goto. Instead of just returning error try calculating software checksum first. There is a check for TSO in checksum_sw_fb. Reviewed-by: Aleksandr Loktionov Signed-off-by: Michal Swiatkowski Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-4-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_txrx.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index 4ca1a0602307..c04c5856dad6 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -1654,7 +1654,7 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off) ret = ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto, &frag_off); if (ret < 0) - return -1; + goto checksum_sw_fb; } /* define outer transport */ @@ -1673,11 +1673,7 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off) l4.hdr = skb_inner_network_header(skb); break; default: - if (first->tx_flags & ICE_TX_FLAGS_TSO) - return -1; - - skb_checksum_help(skb); - return 0; + goto checksum_sw_fb; } /* compute outer L3 header size */ @@ -1736,7 +1732,7 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off) ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto, &frag_off); } else { - return -1; + goto checksum_sw_fb; } /* compute inner L3 header size */ @@ -1789,15 +1785,17 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off) break; default: - if (first->tx_flags & ICE_TX_FLAGS_TSO) - return -1; - skb_checksum_help(skb); - return 0; + goto checksum_sw_fb; } off->td_cmd |= cmd; off->td_offset |= offset; return 1; + +checksum_sw_fb: + if (first->tx_flags & ICE_TX_FLAGS_TSO) + return -1; + return skb_checksum_help(skb); } /** -- cgit v1.2.3 From d6da9b7d48599db078aea6144997a381f8d90d45 Mon Sep 17 00:00:00 2001 From: Marcin Szycik Date: Fri, 17 Jul 2026 11:53:28 -0700 Subject: ice: fix LAG recipe to profile association ice_init_lag() associates recipes to profiles, assuming that Link Aggregation-related profiles will always have profile ID lower than 70 (ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER). This value seems arbitrary and might not always be valid for some versions of DDP package, i.e. LAG profiles may have profile ID greater than 70. This would lead to misconfigured switch and LAG not working properly. Fix it by checking up to maximum profile ID. Fixes: 1e0f9881ef79 ("ice: Flesh out implementation of support for SRIOV on bonded interface") Signed-off-by: Marcin Szycik Reviewed-by: Michal Swiatkowski Reviewed-by: Aleksandr Loktionov Reviewed-by: Dave Ertman Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-7-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_lag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c index 310e8fe2925c..08a17ded0ad5 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.c +++ b/drivers/net/ethernet/intel/ice/ice_lag.c @@ -2623,7 +2623,7 @@ int ice_init_lag(struct ice_pf *pf) goto free_lport_res; /* associate recipes to profiles */ - for (n = 0; n < ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER; n++) { + for (n = 0; n < ICE_MAX_NUM_PROFILES; n++) { err = ice_aq_get_recipe_to_profile(&pf->hw, n, &recipe_bits, NULL); if (err) -- cgit v1.2.3 From 2915681b89f817677ab9f1166d95b595bc144f5f Mon Sep 17 00:00:00 2001 From: Sergey Temerkhanov Date: Fri, 17 Jul 2026 11:53:30 -0700 Subject: ice: use READ_ONCE() to access cached PHC time ptp.cached_phc_time is a 64-bit value updated by a periodic work item on one CPU and read locklessly on another. On 32-bit or non-atomic architectures this can result in a torn read. Use READ_ONCE() to enforce a single atomic load. Fixes: 77a781155a65 ("ice: enable receive hardware timestamping") Cc: stable@vger.kernel.org Signed-off-by: Sergey Temerkhanov Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-9-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index ec3d89d8d4d3..1469038bc895 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -346,7 +346,7 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp) return 0; } - return ice_ptp_extend_32b_ts(pf->ptp.cached_phc_time, + return ice_ptp_extend_32b_ts(READ_ONCE(pf->ptp.cached_phc_time), (in_tstamp >> 8) & mask); } -- cgit v1.2.3 From f6a7e00b81e35ef1325234925f2fe1e53b466f92 Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Fri, 17 Jul 2026 11:53:31 -0700 Subject: ice: fix PTP Call Trace during PTP release If a PF reset occurs when the PTP state is ICE_PTP_UNINIT, then ice_ptp_rebuild() will update the state to ICE_PTP_ERROR. This will result in the following PTP release call trace during driver unload: kernel BUG at lib/list_debug.c:52! ice_ptp_release+0x332/0x3c0 [ice] ice_deinit_features.part.0+0x10e/0x120 [ice] ice_remove+0x100/0x220 [ice] This was observed when passing PF1 through to a VM. ice_ptp_init() fails because ctrl_pf is NULL and sets the state to ICE_PTP_UNINIT. Fix by detecting the ICE_PTP_UNINIT state in ice_ptp_rebuild() and returning without error, preventing the invalid state transition to ICE_PTP_ERROR. The only valid path to ICE_PTP_ERROR is from ICE_PTP_RESETTING after a failed rebuild. Fixes: 8293e4cb2ff5 ("ice: introduce PTP state machine") Cc: stable@vger.kernel.org Signed-off-by: Paul Greenwalt Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-10-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_ptp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 1469038bc895..eaec36ab6ae3 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -3037,6 +3037,11 @@ void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) struct ice_ptp *ptp = &pf->ptp; int err; + if (ptp->state == ICE_PTP_UNINIT) { + dev_dbg(ice_pf_to_dev(pf), "PTP was not initialized, skipping rebuild\n"); + return; + } + if (ptp->state == ICE_PTP_READY) { ice_ptp_prepare_for_reset(pf, reset_type); } else if (ptp->state != ICE_PTP_RESETTING) { -- cgit v1.2.3 From 144539bbfd3cea1ab0fb6f5216d6004c1f4f029b Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Fri, 17 Jul 2026 11:53:32 -0700 Subject: ice: prevent tstamp ring allocation for non-PF VSI types The pf->txtime_txqs bitmap tracks which Tx queues have ETF (Earliest TxTime First) offload enabled. This bitmap is indexed by queue number and is set by ice_offload_txtime(), which only operates on PF VSI queues. However, ice_is_txtime_ena() does not check the VSI type before consulting the bitmap. When ETF offload is enabled on PF Tx queue 0, bit 0 is set in pf->txtime_txqs. During a subsequent PCI reset rebuild, the CTRL VSI's Tx queue 0 is reconfigured and ice_is_txtime_ena() is called for that ring. Since it only checks pf->txtime_txqs by queue index without distinguishing VSI type, it finds bit 0 set and returns true, matching the PF VSI's ETF queue, not the CTRL VSI's. This causes ice_vsi_cfg_txq() to spuriously allocate a tstamp_ring for the CTRL VSI ring. Since CTRL VSI rings have no associated netdev, ice_clean_tx_ring() takes an early return at the !netdev check before reaching ice_free_tx_tstamp_ring(), leaking the allocation. Each PCI reset leaks one 64-byte tstamp_ring. Fix this by restricting ice_is_txtime_ena() to return true only for PF VSI rings, since txtime_txqs is only meaningful for PF VSI queues. Fixes: ccde82e90946 ("ice: add E830 Earliest TxTime First Offload support") Signed-off-by: Paul Greenwalt Reviewed-by: Przemek Kitszel Reviewed-by: Aleksandr Loktionov Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-11-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index f72bb1aa4067..fc91b6665f90 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -767,6 +767,9 @@ static inline bool ice_is_txtime_ena(const struct ice_tx_ring *ring) struct ice_vsi *vsi = ring->vsi; struct ice_pf *pf = vsi->back; + if (vsi->type != ICE_VSI_PF) + return false; + return test_bit(ring->q_index, pf->txtime_txqs); } -- cgit v1.2.3 From 59abb87159c53605c063f6e2ceb215b5eba43ee6 Mon Sep 17 00:00:00 2001 From: Aleksandr Loktionov Date: Fri, 17 Jul 2026 11:53:33 -0700 Subject: ice: reject out-of-range ptype in ice_parser_profile_init set_bit(rslt->ptype, prof->ptypes) operates on a DECLARE_BITMAP of ICE_FLOW_PTYPE_MAX (1024) bits. Nothing prevents a malicious VF from providing ptype >= 1024 through VIRTCHNL, resulting in a write past the end of the bitmap and a kernel page fault. Reproduced with a custom kernel module injecting a crafted VIRTCHNL_OP_ADD_RSS_CFG on E810-C QSFP (8086:1592), FW 4.91 0x800214af 1.3909.0, ICE COMMS DDP 1.3.53.0, kernel 7.1.0-rc1. crash_parser: ice_parser_profile_init @ ffffffffc0d61b60 crash_parser: setting ptype=0xffff (max valid=1023) crash_parser: calling ice_parser_profile_init -- expect OOB crash! BUG: kernel NULL pointer dereference, address: 0000000000000000 Oops: Oops: 0002 [#1] SMP NOPTI CPU: 56 UID: 0 PID: 165011 Comm: insmod Kdump: loaded Tainted: G S U OE 7.1.0-rc1 #1 Hardware name: Intel Corporation S2600BPB/S2600BPB RIP: 0010:ice_parser_profile_init+0x2d/0x1d0 [ice] Call Trace: ? __pfx_ice_parser_profile_init+0x10/0x10 [ice] crash_init+0x127/0xff0 [crash_parser] do_one_initcall+0x45/0x310 do_init_module+0x64/0x270 init_module_from_file+0xcc/0xf0 idempotent_init_module+0x17b/0x280 __x64_sys_finit_module+0x6e/0xe0 Bail out early with -EINVAL when ptype is out of range. Fixes: e312b3a1e209 ("ice: add API for parser profile initialization") Cc: stable@vger.kernel.org Signed-off-by: Aleksandr Loktionov Reviewed-by: Marcin Szycik Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-12-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/ice/ice_parser.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_parser.c b/drivers/net/ethernet/intel/ice/ice_parser.c index f8e69630fb72..3ede4c1a5a8a 100644 --- a/drivers/net/ethernet/intel/ice/ice_parser.c +++ b/drivers/net/ethernet/intel/ice/ice_parser.c @@ -2368,6 +2368,9 @@ int ice_parser_profile_init(struct ice_parser_result *rslt, u16 proto_off = 0; u16 off; + if (rslt->ptype >= ICE_FLOW_PTYPE_MAX) + return -EINVAL; + memset(prof, 0, sizeof(*prof)); set_bit(rslt->ptype, prof->ptypes); if (blk == ICE_BLK_SW) { -- cgit v1.2.3 From 237f1f7653b8729169af11fae79f01b90d00b87e Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Fri, 17 Jul 2026 11:53:34 -0700 Subject: idpf: fix max_vport related crash on allocation error during init Set adapter->max_vports only after successful allocation of vports, netdevs and vport_config buffers. This fixes possible crashes on reset or rmmod, following failed allocation on init [ 305.981402] idpf 0000:83:00.0: enabling device (0100 -> 0102) [ 305.994464] idpf 0000:83:00.0: Device HW Reset initiated [ 320.416872] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 320.416918] #PF: supervisor read access in kernel mode [ 320.416942] #PF: error_code(0x0000) - not-present page [ 320.416963] PGD 2099657067 P4D 0 [ 320.416983] Oops: Oops: 0000 [#1] SMP NOPTI ... [ 320.417093] RIP: 0010:idpf_remove+0x118/0x200 [idpf] [ 320.417130] Code: 8b bb 98 09 00 00 e8 17 0f 5b e5 48 8b bb e8 08 00 00 e8 0b 0f 5b e5 66 83 bb 28 06 00 00 00 48 8b bb 20 06 00 00 74 49 31 ed <48> 8b 04 ef 48 85 c0 74 2f 48 8b 78 20 e8 66 58 91 e5 48 8b 83 20 [ 320.417183] RSP: 0018:ff7322212903fdb8 EFLAGS: 00010246 [ 320.417205] RAX: 0000000000000000 RBX: ff4463de40300000 RCX: ff7322212903fd4c [ 320.417228] RDX: 0000000000000001 RSI: ffffffffa7f7d100 RDI: 0000000000000000 [ 320.417250] RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000000 [ 320.417272] R10: 0000000000000001 R11: ff4463de3a638f58 R12: ff4463be89ac7000 [ 320.417294] R13: ff4463be89ac7198 R14: ff4463be94fc7198 R15: ffffffffc0f10f20 [ 320.417317] FS: 00007f963c0e6740(0000) GS:ff4463fdd65d8000(0000) knlGS:0000000000000000 [ 320.417342] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 320.417362] CR2: 0000000000000000 CR3: 00000020ba674002 CR4: 0000000000773ef0 [ 320.417385] PKRU: 55555554 [ 320.417398] Call Trace: [ 320.417412] [ 320.417429] pci_device_remove+0x42/0xb0 [ 320.417459] device_release_driver_internal+0x1a9/0x210 [ 320.417492] driver_detach+0x4b/0x90 [ 320.417516] bus_remove_driver+0x70/0x100 [ 320.417539] pci_unregister_driver+0x2e/0xb0 [ 320.417564] __do_sys_delete_module.constprop.0+0x190/0x2f0 [ 320.417592] ? kmem_cache_free+0x31e/0x550 [ 320.417619] ? lockdep_hardirqs_on_prepare+0xde/0x190 [ 320.417644] ? do_syscall_64+0x38/0x6b0 [ 320.417665] do_syscall_64+0xc8/0x6b0 [ 320.417683] ? clear_bhb_loop+0x30/0x80 [ 320.417706] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 320.417727] RIP: 0033:0x7f963bb30beb Fixes: 0fe45467a104 ("idpf: add create vport and netdev configuration") Reviewed-by: Madhu Chittim Signed-off-by: Emil Tantilov Reviewed-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Samuel Salin Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260717185340.3595286-13-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c index be66f9b2e101..dc5ad784f456 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c @@ -3555,7 +3555,6 @@ restart: pci_sriov_set_totalvfs(adapter->pdev, idpf_get_max_vfs(adapter)); num_max_vports = idpf_get_max_vports(adapter); - adapter->max_vports = num_max_vports; adapter->vports = kzalloc_objs(*adapter->vports, num_max_vports); if (!adapter->vports) return -ENOMEM; @@ -3576,6 +3575,12 @@ restart: goto err_netdev_alloc; } + /* Set max_vports only after vports, netdevs and vport_config buffers + * are allocated to make sure max_vport bound loops don't end up + * crashing, following allocation errors on init. + */ + adapter->max_vports = num_max_vports; + /* Start the mailbox task before requesting vectors. This will ensure * vector information response from mailbox is handled */ -- cgit v1.2.3 From 305b63e1402267459fdabb183af4527f6799eebf Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 21 Jul 2026 22:02:46 +0200 Subject: netfilter: xt_hashlimit: validate hashtable supports XT_HASHLIMIT_RATE_MATCH The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the dsthash_ent structure which represents an entry in the hashtable. There is a union area which uses a different layout to express the rate match mode. Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode flag is requested by two or more different rules that refer to the same hashtable. Otherwise, uninitialized access to the burst field in the union is possible. Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by revision less than 3 too. Fixes: bea74641e378 ("netfilter: xt_hashlimit: add rate match mode") Reported-and-tested-by: Talha Berk Arslan Link: https://patch.msgid.link/20260721074629.668-1-talha.anything.info@gmail.com/ Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 2704b4b60d1e..9af0fa895f73 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -117,6 +117,7 @@ struct xt_hashlimit_htable { refcount_t use; u_int8_t family; bool rnd_initialized; + bool ratematch; struct hashlimit_cfg3 cfg; /* config */ @@ -323,6 +324,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, kvfree(hinfo); return -ENOMEM; } + hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH); spin_lock_init(&hinfo->lock); switch (revision) { @@ -872,7 +874,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, } /* Check for overflow. */ - if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { + if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { + if (revision < 3) + return -EINVAL; + if (cfg->avg == 0 || cfg->avg > U32_MAX) { pr_info_ratelimited("invalid rate\n"); return -ERANGE; @@ -905,6 +910,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, mutex_unlock(&hashlimit_mutex); return ret; } + } else { + if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH && + !(*hinfo)->ratematch) || + (!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) && + (*hinfo)->ratematch)) { + mutex_unlock(&hashlimit_mutex); + htable_put(*hinfo); + return -EINVAL; + } } mutex_unlock(&hashlimit_mutex); -- cgit v1.2.3 From e876b75b9020a97bbdc79721e7fc749024891c65 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 22 Jul 2026 13:15:15 +0300 Subject: ipvs: fix the checksum validations ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 packets from clients. In fact, as for TCP/UDP we should validate the checksum for ICMP packets only when we mangle the packets on MASQ or on reply for tunnel. Also, Sashiko points out that handle_response_icmp() being common for IPv4 and IPv6 is missing the pseudo-header calculation while validating ICMPv6 messages from real servers which is a problem if checksum is not validated by the hardware. Fix the problems by creating ip_vs_checksum_common_check() helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. Rely on the nf_checksum() for validating the ICMP messages but use it also for TCP and UDP. Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP. IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum validation on LOCAL_OUT (local clients or local real servers) and on FORWARD (traffic from servers on LAN). Do it only on LOCAL_IN, in case nf_checksum() is not called on PRE_ROUTING. Also, ip_vs_checksum_complete() can be marked static. Fixes: 2a3b791e6e11 ("IPVS: Add/adjust Netfilter hook functions and helpers for v6") Link: https://sashiko.dev/#/patchset/20260708180315.77413-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 31 ++++++++++++++++++++-- net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++++--- net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++++----- net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 ++++++------------------------ net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++++--------------------------- 5 files changed, 74 insertions(+), 86 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 417ff51f62fc..d8f9ddb0fb38 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -25,7 +25,9 @@ #include /* for union nf_inet_addr */ #include #include /* for struct ipv6hdr */ +#include #include +#include #if IS_ENABLED(CONFIG_NF_CONNTRACK) #include #endif @@ -2066,8 +2068,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir); #endif -__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); - static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) { __be32 diff[2] = { ~old, new }; @@ -2093,6 +2093,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) return csum_partial(diff, sizeof(diff), oldsum); } +static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) +{ + /* Checksum unnecessary or already validated? */ + if (skb_csum_unnecessary(skb)) + return false; + /* LOCAL_OUT ? */ + if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) + return false; + /* !LOCAL_IN (FORWARD) ? */ + if (af == AF_INET6) { + if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) + return false; + } else { + if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) + return false; + } + return true; +} + +static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, + int offset, int proto, int af) +{ + if (!ip_vs_checksum_needed(skb, af)) + return true; + return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); +} + /* Forget current conntrack (unconfirmed) and attach notrack entry */ static inline void ip_vs_notrack(struct sk_buff *skb) { diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index bafab93451d0..c8b512725e6e 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -867,7 +867,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; } #endif -__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) +static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset) { return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0)); } @@ -1038,13 +1038,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, unsigned int offset, unsigned int ihl, unsigned int hooknum) { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; unsigned int verdict = NF_DROP; if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) goto after_nat; /* Ensure the checksum is correct */ - if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { + if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { /* Failed checksum! */ IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", IP_VS_DBG_ADDR(af, snet)); @@ -1898,7 +1899,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, verdict = NF_DROP; /* Ensure the checksum is correct */ - if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) { + if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) && + !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { /* Failed checksum! */ IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", &iph->saddr); @@ -2064,6 +2066,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, goto out; } + verdict = NF_DROP; + + /* Ensure the checksum is correct */ + if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ && + !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6, + AF_INET6)) { + /* Failed checksum! */ + IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n", + &iph->saddr); + goto out; + } + /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index c67317be17df..f6f732b7dfa8 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -11,7 +11,7 @@ static int sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int sctphoff); + struct ip_vs_iphdr *iph); static int sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, @@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) + if (!sctp_csum_check(cp->af, skb, pp, iph)) return 0; /* Call application helper if needed */ @@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!sctp_csum_check(cp->af, skb, pp, sctphoff)) + if (!sctp_csum_check(cp->af, skb, pp, iph)) return 0; /* Call application helper if needed */ @@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, static int sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int sctphoff) + struct ip_vs_iphdr *iph) { + unsigned int sctphoff = iph->len; struct sctphdr *sh; __le32 cmp, val; + if (!ip_vs_checksum_needed(skb, af)) + return 1; sh = (struct sctphdr *)(skb->data + sctphoff); cmp = sh->checksum; val = sctp_compute_cksum(skb, sctphoff); if (val != cmp) { /* CRC failure, dump it. */ - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); + IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, + "Failed checksum for"); return 0; } return 1; diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index f86b763efcc4..533fce3e5e4e 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -29,7 +29,7 @@ static int tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int tcphoff); + struct ip_vs_iphdr *iph); static int tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, @@ -166,7 +166,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) + if (!tcp_csum_check(cp->af, skb, pp, iph)) return 0; /* Call application helper if needed */ @@ -244,7 +244,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!tcp_csum_check(cp->af, skb, pp, tcphoff)) + if (!tcp_csum_check(cp->af, skb, pp, iph)) return 0; /* @@ -302,41 +302,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, static int tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int tcphoff) + struct ip_vs_iphdr *iph) { - switch (skb->ip_summed) { - case CHECKSUM_NONE: - skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0); - fallthrough; - case CHECKSUM_COMPLETE: -#ifdef CONFIG_IP_VS_IPV6 - if (af == AF_INET6) { - if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - skb->len - tcphoff, - IPPROTO_TCP, - skb->csum)) { - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); - return 0; - } - } else -#endif - if (csum_tcpudp_magic(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, - skb->len - tcphoff, - ip_hdr(skb)->protocol, - skb->csum)) { - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); - return 0; - } - break; - default: - /* No need to checksum. */ - break; + if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) { + IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, + "Failed checksum for"); + return 0; } - return 1; } diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index 58f9e255927e..de3597347542 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -25,7 +25,7 @@ static int udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int udphoff); + struct ip_vs_iphdr *iph); static int udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, @@ -155,7 +155,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!udp_csum_check(cp->af, skb, pp, udphoff)) + if (!udp_csum_check(cp->af, skb, pp, iph)) return 0; /* @@ -238,7 +238,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, int ret; /* Some checks before mangling */ - if (!udp_csum_check(cp->af, skb, pp, udphoff)) + if (!udp_csum_check(cp->af, skb, pp, iph)) return 0; /* @@ -298,48 +298,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, static int udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, - unsigned int udphoff) + struct ip_vs_iphdr *iph) { struct udphdr _udph, *uh; - uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph); + uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph); if (uh == NULL) return 0; - if (uh->check != 0) { - switch (skb->ip_summed) { - case CHECKSUM_NONE: - skb->csum = skb_checksum(skb, udphoff, - skb->len - udphoff, 0); - fallthrough; - case CHECKSUM_COMPLETE: -#ifdef CONFIG_IP_VS_IPV6 - if (af == AF_INET6) { - if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - skb->len - udphoff, - IPPROTO_UDP, - skb->csum)) { - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); - return 0; - } - } else -#endif - if (csum_tcpudp_magic(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, - skb->len - udphoff, - ip_hdr(skb)->protocol, - skb->csum)) { - IP_VS_DBG_RL_PKT(0, af, pp, skb, 0, - "Failed checksum for"); - return 0; - } - break; - default: - /* No need to checksum. */ - break; - } + if (!uh->check) + return 1; + if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) { + IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off, + "Failed checksum for"); + return 0; } return 1; } -- cgit v1.2.3 From 15cab31a3730e05f0767b922a7450e5d784b2607 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 22 Jul 2026 13:15:16 +0300 Subject: ipvs: fix places with wrong packet offsets The offsets we use to packet headers and payloads should be based on skb->data. We even already respect non-zero network offset in ip_vs_fill_iph_skb() but some places do it wrongly and support only zero offset which is expected for the IP layer where IPVS has hooks. Change all places that instead of skb->data use offsets based on the network header (skb_network_header, ip_hdr, etc) because this doubles the network offset as noted by Sashiko. For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header parsing done by the caller. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260710143733.29741-2-fw%40strlen.de Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 15 ++-- net/netfilter/ipvs/ip_vs_app.c | 4 +- net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++++++----------------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- net/netfilter/ipvs/ip_vs_xmit.c | 26 ++++--- 7 files changed, 97 insertions(+), 93 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index d8f9ddb0fb38..4a10a01d6e2f 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1974,8 +1974,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, - unsigned int hooknum, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, unsigned int toff, + unsigned int wlen, unsigned int hooknum, + struct ip_vs_iphdr *ciph); void ip_vs_dest_dst_rcu_free(struct rcu_head *head); #ifdef CONFIG_IP_VS_IPV6 @@ -1988,8 +1989,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, - unsigned int hooknum, struct ip_vs_iphdr *iph); + struct ip_vs_protocol *pp, unsigned int toff, + unsigned int wlen, unsigned int hooknum, + struct ip_vs_iphdr *ciph); #endif #ifdef CONFIG_SYSCTL @@ -2061,11 +2063,12 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) } void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int dir); + struct ip_vs_conn *cp, int dir, unsigned int toff); #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int dir); + struct ip_vs_conn *cp, int dir, unsigned int toff, + struct ip_vs_iphdr *ciph); #endif static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index b0e00be85cb1..11cbdbaf561d 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -367,7 +367,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb, if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); + th = (struct tcphdr *)(skb->data + ipvsh->len); /* * Remember seq number in case this pkt gets resized @@ -443,7 +443,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb, if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th))) return 0; - th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len); + th = (struct tcphdr *)(skb->data + 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 c8b512725e6e..cd5eb71543ec 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -924,13 +924,12 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, * - inout: 1=in->out, 0=out->in */ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int inout) + struct ip_vs_conn *cp, int inout, unsigned int toff) { struct iphdr *iph = ip_hdr(skb); - unsigned int icmp_offset = iph->ihl*4; - struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) + - icmp_offset); + struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); struct iphdr *ciph = (struct iphdr *)(icmph + 1); + unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); if (inout) { iph->saddr = cp->vaddr.ip; @@ -957,48 +956,45 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, /* And finally the ICMP checksum */ icmph->checksum = 0; - icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset); + icmph->checksum = ip_vs_checksum_complete(skb, toff); skb->ip_summed = CHECKSUM_UNNECESSARY; if (inout) - IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, - "Forwarding altered outgoing ICMP"); + IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, + "Forwarding altered outgoing ICMP"); else - IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph, - "Forwarding altered incoming ICMP"); + IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, + "Forwarding altered incoming ICMP"); } #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int inout) + struct ip_vs_conn *cp, int inout, unsigned int toff, + struct ip_vs_iphdr *ciph) { struct ipv6hdr *iph = ipv6_hdr(skb); - unsigned int icmp_offset = 0; - unsigned int offs = 0; /* header offset*/ int protocol; struct icmp6hdr *icmph; - struct ipv6hdr *ciph; - unsigned short fragoffs; + struct ipv6hdr *cih; - ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL); - icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset); - offs = icmp_offset + sizeof(struct icmp6hdr); - ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs); + icmph = (struct icmp6hdr *)(skb->data + toff); + cih = (struct ipv6hdr *)(skb->data + ciph->off); - protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL); + protocol = ciph->protocol; if (inout) { iph->saddr = cp->vaddr.in6; - ciph->daddr = cp->vaddr.in6; + cih->daddr = cp->vaddr.in6; } else { iph->daddr = cp->daddr.in6; - ciph->saddr = cp->daddr.in6; + cih->saddr = cp->daddr.in6; } /* the TCP/UDP/SCTP port */ - if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || - IPPROTO_SCTP == protocol)) { - __be16 *ports = (void *)(skb_network_header(skb) + offs); + if (!ciph->fragoffs && + (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || + protocol == IPPROTO_SCTP)) { + __be16 *ports = (void *)(skb->data + ciph->len); IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, ntohs(inout ? ports[1] : ports[0]), @@ -1011,19 +1007,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, /* And finally the ICMP checksum */ icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, - skb->len - icmp_offset, + skb->len - toff, IPPROTO_ICMPV6, 0); - skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset; + skb->csum_start = skb_headroom(skb) + toff; skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum); skb->ip_summed = CHECKSUM_PARTIAL; if (inout) - IP_VS_DBG_PKT(11, AF_INET6, pp, skb, - (void *)ciph - (void *)iph, + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, "Forwarding altered outgoing ICMPv6"); else - IP_VS_DBG_PKT(11, AF_INET6, pp, skb, - (void *)ciph - (void *)iph, + IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off, "Forwarding altered incoming ICMPv6"); } #endif @@ -1033,37 +1027,38 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, */ static int handle_response_icmp(int af, struct sk_buff *skb, union nf_inet_addr *snet, - __u8 protocol, struct ip_vs_conn *cp, + struct ip_vs_conn *cp, struct ip_vs_protocol *pp, - unsigned int offset, unsigned int ihl, - unsigned int hooknum) + struct ip_vs_iphdr *ciph, + unsigned int toff, unsigned int hooknum) { int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; unsigned int verdict = NF_DROP; + unsigned int ctoff = ciph->len; if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) goto after_nat; /* Ensure the checksum is correct */ - if (!ip_vs_checksum_common_check(skb, ihl, iproto, af)) { + if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) { /* Failed checksum! */ IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n", IP_VS_DBG_ADDR(af, snet)); goto out; } - if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol || - IPPROTO_SCTP == protocol) - offset += 2 * sizeof(__u16); - if (skb_ensure_writable(skb, offset)) + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || + ciph->protocol == IPPROTO_SCTP) + ctoff += 2 * sizeof(__u16); + if (skb_ensure_writable(skb, ctoff)) goto out; #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) - ip_vs_nat_icmp_v6(skb, pp, cp, 1); + ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); else #endif - ip_vs_nat_icmp(skb, pp, cp, 1); + ip_vs_nat_icmp(skb, pp, cp, 1, toff); if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) goto out; @@ -1091,9 +1086,9 @@ out: * Currently handles error types - unreachable, quench, ttl exceeded. */ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, - int *related, unsigned int hooknum) + int *related, unsigned int hooknum, + struct ip_vs_iphdr *ipvsh) { - struct iphdr *iph; struct icmphdr _icmph, *ic; struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ struct ip_vs_iphdr ciph; @@ -1108,17 +1103,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, if (ip_is_fragment(ip_hdr(skb))) { if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) return NF_STOLEN; + if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh)) + return NF_ACCEPT; } - iph = ip_hdr(skb); - offset = ihl = iph->ihl * 4; + ihl = ipvsh->len; + offset = ipvsh->len; ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); if (ic == NULL) return NF_DROP; IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n", ic->type, ntohs(icmp_id(ic)), - &iph->saddr, &iph->daddr); + &ipvsh->saddr.ip, &ipvsh->daddr.ip); /* * Work through seeing if this is for us. @@ -1137,7 +1134,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, /* Now find the contained IP header */ offset += sizeof(_icmph); cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (cih == NULL) + if (!(cih && cih->version == 4 && cih->ihl >= 5)) return NF_ACCEPT; /* The packet looks wrong, ignore */ pp = ip_vs_proto_get(cih->protocol); @@ -1160,9 +1157,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, if (!cp) return NF_ACCEPT; - snet.ip = iph->saddr; - return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, - pp, ciph.len, ihl, hooknum); + snet.ip = ipvsh->saddr.ip; + return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, + hooknum); } #ifdef CONFIG_IP_VS_IPV6 @@ -1175,7 +1172,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, struct ip_vs_conn *cp; struct ip_vs_protocol *pp; union nf_inet_addr snet; - unsigned int offset; *related = 1; ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph); @@ -1218,9 +1214,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; snet.in6 = ciph.saddr.in6; - offset = ciph.len; - return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp, - pp, offset, ipvsh->len, hooknum); + return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph, + ipvsh->len, hooknum); } #endif @@ -1546,7 +1541,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat #endif if (unlikely(iph.protocol == IPPROTO_ICMP)) { int related; - int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum); + int verdict = ip_vs_out_icmp(ipvs, skb, &related, + hooknum, &iph); if (related) return verdict; @@ -1754,9 +1750,8 @@ unk: */ static int ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, - unsigned int hooknum) + unsigned int hooknum, struct ip_vs_iphdr *iph) { - struct iphdr *iph; struct icmphdr _icmph, *ic; struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */ struct ip_vs_iphdr ciph; @@ -1766,7 +1761,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, unsigned int offset, offset2, ihl, verdict; bool tunnel, new_cp = false; union nf_inet_addr *raddr; - char *outer_proto = "IPIP"; + char *outer_proto __maybe_unused = "IPIP"; unsigned int hlen_ipip; int ulen = 0; @@ -1776,17 +1771,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (ip_is_fragment(ip_hdr(skb))) { if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum))) return NF_STOLEN; + if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph)) + return NF_ACCEPT; } - iph = ip_hdr(skb); - offset = ihl = iph->ihl * 4; + ihl = iph->len; + offset = iph->len; ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); if (ic == NULL) return NF_DROP; IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n", ic->type, ntohs(icmp_id(ic)), - &iph->saddr, &iph->daddr); + &iph->saddr.ip, &iph->daddr.ip); /* * Work through seeing if this is for us. @@ -1903,7 +1900,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) { /* Failed checksum! */ IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n", - &iph->saddr); + &iph->saddr.ip); goto out; } @@ -1974,7 +1971,8 @@ ignore_tunnel: if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || IPPROTO_SCTP == cih->protocol) offset += 2 * sizeof(__u16); - verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph); + verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, + &ciph); out: if (likely(!new_cp)) @@ -2087,7 +2085,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, IPPROTO_SCTP == ciph.protocol) offset += 2 * sizeof(__u16); /* Also mangle ports */ - verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph); + verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, + &ciph); out: if (likely(!new_cp)) @@ -2166,7 +2165,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state if (unlikely(iph.protocol == IPPROTO_ICMP)) { int related; int verdict = ip_vs_in_icmp(ipvs, skb, &related, - hooknum); + hooknum, &iph); if (related) return verdict; @@ -2302,6 +2301,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct netns_ipvs *ipvs = net_ipvs(state->net); + struct ip_vs_iphdr iphdr; int r; /* ipvs enabled in this netns ? */ @@ -2311,10 +2311,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, if (state->pf == NFPROTO_IPV4) { if (ip_hdr(skb)->protocol != IPPROTO_ICMP) return NF_ACCEPT; + ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr); #ifdef CONFIG_IP_VS_IPV6 } else { - struct ip_vs_iphdr iphdr; - ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr); if (iphdr.protocol != IPPROTO_ICMPV6) @@ -2324,7 +2323,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb, #endif } - return ip_vs_in_icmp(ipvs, skb, &r, state->hook); + return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr); } static const struct nf_hook_ops ip_vs_ops4[] = { diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index f6f732b7dfa8..3dbd3096e163 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - sctph = (void *) skb_network_header(skb) + sctphoff; + sctph = (void *)skb->data + sctphoff; /* Only update csum if we really have to */ if (sctph->source != cp->vport || payload_csum || @@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - sctph = (void *) skb_network_header(skb) + sctphoff; + sctph = (void *)skb->data + sctphoff; /* Only update csum if we really have to */ if (sctph->dest != cp->dport || payload_csum || diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 533fce3e5e4e..99a286fdc90c 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -179,7 +179,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - tcph = (void *)skb_network_header(skb) + tcphoff; + tcph = (void *)skb->data + tcphoff; tcph->source = cp->vport; /* Adjust TCP checksums */ @@ -260,7 +260,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - tcph = (void *)skb_network_header(skb) + tcphoff; + tcph = (void *)skb->data + tcphoff; tcph->dest = cp->dport; /* diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index de3597347542..f32785682402 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -170,7 +170,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - udph = (void *)skb_network_header(skb) + udphoff; + udph = (void *)skb->data + udphoff; udph->source = cp->vport; /* @@ -254,7 +254,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - udph = (void *)skb_network_header(skb) + udphoff; + udph = (void *)skb->data + udphoff; udph->dest = cp->dport; /* diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 9fef4335da13..c23401c789de 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1502,8 +1502,9 @@ tx_error: */ int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, unsigned int hooknum, - struct ip_vs_iphdr *iph) + struct ip_vs_protocol *pp, unsigned int toff, + unsigned int wlen, unsigned int hooknum, + struct ip_vs_iphdr *ciph) { struct rtable *rt; /* Route to the other host */ int rc; @@ -1515,7 +1516,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, translate address/port back */ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { if (cp->packet_xmit) - rc = cp->packet_xmit(skb, cp, pp, iph); + rc = cp->packet_xmit(skb, cp, pp, ciph); else rc = NF_ACCEPT; /* do not touch skb anymore */ @@ -1533,7 +1534,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, - NULL, iph); + NULL, ciph); if (local < 0) goto tx_error; rt = skb_rtable(skb); @@ -1565,13 +1566,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, } /* copy-on-write the packet before mangling it */ - if (skb_ensure_writable(skb, offset)) + if (skb_ensure_writable(skb, wlen)) goto tx_error; if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp(skb, pp, cp, 0); + ip_vs_nat_icmp(skb, pp, cp, 0, toff); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; @@ -1587,8 +1588,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, #ifdef CONFIG_IP_VS_IPV6 int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, unsigned int hooknum, - struct ip_vs_iphdr *ipvsh) + struct ip_vs_protocol *pp, unsigned int toff, + unsigned int wlen, unsigned int hooknum, + struct ip_vs_iphdr *ciph) { struct rt6_info *rt; /* Route to the other host */ int rc; @@ -1600,7 +1602,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, translate address/port back */ if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { if (cp->packet_xmit) - rc = cp->packet_xmit(skb, cp, pp, ipvsh); + rc = cp->packet_xmit(skb, cp, pp, ciph); else rc = NF_ACCEPT; /* do not touch skb anymore */ @@ -1617,7 +1619,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, - &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); + &cp->daddr.in6, NULL, ciph, 0, rt_mode); if (local < 0) goto tx_error; rt = dst_rt6_info(skb_dst(skb)); @@ -1649,13 +1651,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, } /* copy-on-write the packet before mangling it */ - if (skb_ensure_writable(skb, offset)) + if (skb_ensure_writable(skb, wlen)) goto tx_error; if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp_v6(skb, pp, cp, 0); + ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; -- cgit v1.2.3 From ee7f9bb9320add61f7b367d7e6cd55e3a3a4d65d Mon Sep 17 00:00:00 2001 From: Sungmin Kang <726ksm@gmail.com> Date: Sat, 18 Jul 2026 16:36:30 +0900 Subject: net: slip: serialize receive against buffer reallocation sl_realloc_bufs() replaces rbuff and updates buffsize while holding sl->lock. slip_receive_buf() reads those fields and writes through rbuff without holding the lock. An MTU change can therefore race with receive processing. An MTU shrink can expose the new smaller rbuff with the old larger bound, causing an out-of-bounds write. A receive callback which already loaded the old rbuff can instead continue writing after that buffer has been freed. Serialize receive processing with sl_realloc_bufs() by holding sl->lock while consuming each receive batch. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Sungmin Kang <726ksm@gmail.com> Link: https://patch.msgid.link/20260718073631.1674-1-726ksm@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/slip/slip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index 820e1a8fc956..faae711cf793 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -693,6 +693,8 @@ static void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev)) return; + spin_lock_bh(&sl->lock); + /* Read the characters out of the buffer */ while (count--) { if (fp && *fp++) { @@ -708,6 +710,8 @@ static void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, #endif slip_unesc(sl, *cp++); } + + spin_unlock_bh(&sl->lock); } /************************************ -- cgit v1.2.3 From 342e24a339b90e8e339a0f8c151ca479b8565661 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 22 Jul 2026 13:15:17 +0300 Subject: ipvs: do not mangle ICMP replies for non-first fragments Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the payload for embedded non-first IPv4 fragments. The problem is in the very old inverted pp->dont_defrag check which should not continue when embedded is a non-first TCP/UDP/SCTP fragment. Check for embedded non-first fragment is also missing from ip_vs_out_icmp_v6(), it is needed before any connection lookups that expect ports after the network headers. Drop the blocking code from ip_vs_in_icmp_v6() which prevents ICMPv6 from local clients to use non-MASQ forwarding. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260720201122.79882-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 11 ++++---- net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++++--------------------------- net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++++++---- 3 files changed, 48 insertions(+), 52 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 4a10a01d6e2f..e6ca930a3507 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1975,8 +1975,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int wlen, unsigned int hooknum, - struct ip_vs_iphdr *ciph); + unsigned int hooknum, struct ip_vs_iphdr *ciph); void ip_vs_dest_dst_rcu_free(struct rcu_head *head); #ifdef CONFIG_IP_VS_IPV6 @@ -1990,8 +1989,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int wlen, unsigned int hooknum, - struct ip_vs_iphdr *ciph); + unsigned int hooknum, struct ip_vs_iphdr *ciph); #endif #ifdef CONFIG_SYSCTL @@ -2063,12 +2061,13 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) } void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int dir, unsigned int toff); + struct ip_vs_conn *cp, int dir, unsigned int toff, + bool has_ports); #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir, unsigned int toff, - struct ip_vs_iphdr *ciph); + bool has_ports, struct ip_vs_iphdr *ciph); #endif static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index cd5eb71543ec..7efa209a517b 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -924,7 +924,8 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, * - inout: 1=in->out, 0=out->in */ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int inout, unsigned int toff) + struct ip_vs_conn *cp, int inout, unsigned int toff, + bool has_ports) { struct iphdr *iph = ip_hdr(skb); struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); @@ -944,8 +945,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, } /* the TCP/UDP/SCTP port */ - if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol || - IPPROTO_SCTP == ciph->protocol) { + if (has_ports) { __be16 *ports = (void *)ciph + ciph->ihl*4; if (inout) @@ -970,18 +970,15 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int inout, unsigned int toff, - struct ip_vs_iphdr *ciph) + bool has_ports, struct ip_vs_iphdr *ciph) { struct ipv6hdr *iph = ipv6_hdr(skb); - int protocol; struct icmp6hdr *icmph; struct ipv6hdr *cih; icmph = (struct icmp6hdr *)(skb->data + toff); cih = (struct ipv6hdr *)(skb->data + ciph->off); - protocol = ciph->protocol; - if (inout) { iph->saddr = cp->vaddr.in6; cih->daddr = cp->vaddr.in6; @@ -991,9 +988,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, } /* the TCP/UDP/SCTP port */ - if (!ciph->fragoffs && - (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || - protocol == IPPROTO_SCTP)) { + if (has_ports) { __be16 *ports = (void *)(skb->data + ciph->len); IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__, @@ -1035,6 +1030,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP; unsigned int verdict = NF_DROP; unsigned int ctoff = ciph->len; + bool has_ports = false; if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) goto after_nat; @@ -1048,17 +1044,19 @@ static int handle_response_icmp(int af, struct sk_buff *skb, } if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || - ciph->protocol == IPPROTO_SCTP) + ciph->protocol == IPPROTO_SCTP) { ctoff += 2 * sizeof(__u16); + has_ports = true; + } if (skb_ensure_writable(skb, ctoff)) goto out; #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) - ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, ciph); + ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); else #endif - ip_vs_nat_icmp(skb, pp, cp, 1, toff); + ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) goto out; @@ -1142,8 +1140,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; /* Is the embedded protocol header present? */ - if (unlikely(cih->frag_off & htons(IP_OFFSET) && - pp->dont_defrag)) + if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) return NF_ACCEPT; IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, @@ -1207,6 +1204,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, if (!pp) return NF_ACCEPT; + /* Is the embedded protocol header present? */ + if (unlikely(ciph.fragoffs && !pp->dont_defrag)) + return NF_ACCEPT; + /* The embedded headers contain source and dest in reverse order */ cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, ipvs, AF_INET6, skb, &ciph); @@ -1865,8 +1866,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, pp = pd->pp; /* Is the embedded protocol header present? */ - if (unlikely(cih->frag_off & htons(IP_OFFSET) && - pp->dont_defrag)) + if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) return NF_ACCEPT; IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, @@ -1874,7 +1874,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, offset2 = offset; ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); - offset = ciph.len; /* The embedded headers contain source and dest in reverse order. * For IPIP/UDP/GRE tunnel this is error for request, not for reply. @@ -1968,11 +1967,7 @@ ignore_tunnel: /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); - if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol || - IPPROTO_SCTP == cih->protocol) - offset += 2 * sizeof(__u16); - verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, offset, hooknum, - &ciph); + verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph); out: if (likely(!new_cp)) @@ -2032,8 +2027,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; pp = pd->pp; - /* Cannot handle fragmented embedded protocol */ - if (ciph.fragoffs) + /* Is the embedded protocol header present? */ + if (ciph.fragoffs && !pp->dont_defrag) return NF_ACCEPT; IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset, @@ -2057,13 +2052,6 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, new_cp = true; } - /* VS/TUN, VS/DR and LOCALNODE just let it go */ - if ((hooknum == NF_INET_LOCAL_OUT) && - (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) { - verdict = NF_ACCEPT; - goto out; - } - verdict = NF_DROP; /* Ensure the checksum is correct */ @@ -2079,14 +2067,7 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, /* do the statistics and put it back */ ip_vs_in_stats(cp, skb); - /* Need to mangle contained IPv6 header in ICMPv6 packet */ - offset = ciph.len; - if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol || - IPPROTO_SCTP == ciph.protocol) - offset += 2 * sizeof(__u16); /* Also mangle ports */ - - verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, offset, hooknum, - &ciph); + verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph); out: if (likely(!new_cp)) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index c23401c789de..0b0c5304993a 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1503,13 +1503,14 @@ tx_error: int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int wlen, unsigned int hooknum, - struct ip_vs_iphdr *ciph) + unsigned int hooknum, struct ip_vs_iphdr *ciph) { struct rtable *rt; /* Route to the other host */ int rc; int local; int rt_mode, was_input; + bool has_ports = false; + unsigned int wlen; /* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be forwarded directly here, because there is no need to @@ -1565,6 +1566,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; } + wlen = ciph->len; + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || + ciph->protocol == IPPROTO_SCTP) { + wlen += 2 * sizeof(__u16); /* Also mangle ports */ + has_ports = true; + } + /* copy-on-write the packet before mangling it */ if (skb_ensure_writable(skb, wlen)) goto tx_error; @@ -1572,7 +1580,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp(skb, pp, cp, 0, toff); + ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; @@ -1589,10 +1597,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, unsigned int toff, - unsigned int wlen, unsigned int hooknum, - struct ip_vs_iphdr *ciph) + unsigned int hooknum, struct ip_vs_iphdr *ciph) { + bool has_ports = false; struct rt6_info *rt; /* Route to the other host */ + unsigned int wlen; int rc; int local; int rt_mode; @@ -1650,6 +1659,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; } + wlen = ciph->len; + if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP || + ciph->protocol == IPPROTO_SCTP) { + wlen += 2 * sizeof(__u16); /* Also mangle ports */ + has_ports = true; + } + /* copy-on-write the packet before mangling it */ if (skb_ensure_writable(skb, wlen)) goto tx_error; @@ -1657,7 +1673,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, ciph); + ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; -- cgit v1.2.3 From 313a123e1fca8827bb463db1f4bb211309764563 Mon Sep 17 00:00:00 2001 From: "Nikola Z. Ivanov" Date: Sun, 19 Jul 2026 13:57:59 +0300 Subject: ipv6: Change allocation flags to match rcu_read_lock section requirements Since the call to __ip6_del_rt_siblings has been converted under rcu read lock and it only has one call point we should no longer block or yield. Our stack trace from the syzbot reproducer looks as follows: __ip6_del_rt_siblings rtnl_notify (Here we pass gfp_any() -> GFP_KERNEL) nlmsg_notify nlmsg_multicast nlmsg_multicast_filtered netlink_broadcast_filtered (GFP_KERNEL passed from earlier) netlink_broadcast_filtered can yield if GFP_KERNEL is passed, which we do not want to happen. Fix this by changing the allocation flag of rtnl_notify. Also change the flag passed to nlmsg_new. Even though it is not related to the syzbot generated bug it still falls under the same requirements. Reported-by: syzbot+84d4a405ed798b40c96d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=84d4a405ed798b40c96d Fixes: bd11ff421d36 ("ipv6: Get rid of RTNL for SIOCDELRT and RTM_DELROUTE.") Signed-off-by: Nikola Z. Ivanov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260719105759.558050-1-zlatistiv@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/route.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index a1301334da48..fc42d67e5822 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -4022,7 +4022,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) struct fib6_node *fn; /* prefer to send a single notification with all hops */ - skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); + skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC); if (skb) { u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; @@ -4078,7 +4078,7 @@ out_put: if (skb) { rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, - info->nlh, gfp_any()); + info->nlh, GFP_ATOMIC); } return err; } -- cgit v1.2.3 From 167e54c703ccd4fa028feb568b0d1002020cff86 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Sun, 19 Jul 2026 17:03:57 -0400 Subject: rds: tcp: unregister sysctl before tearing down listen socket rds_tcp_exit_net() frees the per-netns RDS TCP listen socket via rds_tcp_kill_sock() before unregistering the per-netns sysctl table. Since rds_tcp_skbuf_handler() derives the netns from rtn->rds_tcp_listen_sock->sk, a concurrent sysctl write can race with netns teardown and dereference the freed socket/sk. KASAN reports the race as: BUG: KASAN: slab-use-after-free in rds_tcp_skbuf_handler+0x2aa/0x2e0 rds_tcp_skbuf_handler net/rds/tcp.c:721 proc_sys_call_handler fs/proc/proc_sysctl.c vfs_write fs/read_write.c __x64_sys_pwrite64 fs/read_write.c Fix this by unregistering the RDS TCP sysctl table before calling rds_tcp_kill_sock(). unregister_net_sysctl_table() prevents new sysctl handlers from starting and waits for in-flight handlers to finish, so the listen socket can then be released safely. The fix was tested against the linked reproducer. Fixes: 7f5611cbc487 ("rds: sysctl: rds_tcp_{rcv,snd}buf: avoid using current->nsproxy") Reported-by: AutonomousCodeSecurity@microsoft.com Link: https://lore.kernel.org/all/20260719203718.9680-1-blbllhy@gmail.com Reviewed-by: Allison Henderson Signed-off-by: Cen Zhang (Microsoft) Link: https://patch.msgid.link/20260719210357.10179-1-blbllhy@gmail.com Signed-off-by: Jakub Kicinski --- net/rds/tcp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 955d92277d5a..5de35d556f29 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -657,13 +657,13 @@ static void __net_exit rds_tcp_exit_net(struct net *net) { struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid); - rds_tcp_kill_sock(net); - if (rtn->rds_tcp_sysctl) unregister_net_sysctl_table(rtn->rds_tcp_sysctl); if (net != &init_net) kfree(rtn->ctl_table); + + rds_tcp_kill_sock(net); } static struct pernet_operations rds_tcp_net_ops = { -- cgit v1.2.3 From c3f2fc231a39e29fe9f0adc14a3ecc3c1260d3c5 Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Mon, 20 Jul 2026 09:25:08 +0800 Subject: ptp: netc: explicitly clear TMR_OFF during initialization The NETC timer does not support function level reset, so TMR_OFF_L/H registers are not cleared by pcie_flr(). If TMR_OFF was set to a non-zero value in a previous binding, it will persist across driver rebind and cause inaccurate PTP time. There is also a hardware issue: after a warm reset or soft reset, TMR_OFF_L/H registers appear to be cleared to zero, but the timer clock domain internally retains the stale value. When the timer is re-enabled, TMR_CUR_TIME continues to track the old offset until TMR_OFF is written explicitly. This can cause incorrect PTP timestamps and even PTP clock synchronization failures. Per the recommendation from the IP team, explicitly write 0 to TMR_OFF in netc_timer_init() to flush the internally cached value and ensure TMR_CUR_TIME follows the freshly initialized counter. Fixes: 87a201d59963 ("ptp: netc: add NETC V4 Timer PTP driver support") Signed-off-by: Clark Wang Signed-off-by: Wei Fang Reviewed-by: Vadim Fedorenko Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260720012508.23227-1-wei.fang@oss.nxp.com Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_netc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c index 94e952ee6990..5e381c354d74 100644 --- a/drivers/ptp/ptp_netc.c +++ b/drivers/ptp/ptp_netc.c @@ -779,6 +779,7 @@ static void netc_timer_init(struct netc_timer *priv) netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl); netc_timer_wr(priv, NETC_TMR_ECTRL, NETC_TMR_DEFAULT_ETTF_THR); + netc_timer_offset_write(priv, 0); ktime_get_real_ts64(&now); ns = timespec64_to_ns(&now); netc_timer_cnt_write(priv, ns); -- cgit v1.2.3 From da7d894c41d5910daae2b8ffa024c52ff0a4df6a Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Wed, 22 Jul 2026 13:25:39 +0300 Subject: ipvs: clear the nfct flag under lock Sashiko warns that cp->flags should be changed under cp->lock Fixes: 35dfb013149f ("ipvs: queue delayed work to expire no destination connections if expire_nodest_conn=1") Fixes: f0a5e4d7a594 ("ipvs: allow connection reuse for unconfirmed conntrack") Link: https://sashiko.dev/#/patchset/CALMqdkR704S2BG_QD_bgHTFp2%2B1QCi7n0T4zoZyTo8mDZevYSA%40mail.gmail.com Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 7efa209a517b..6b79e0c4d9e2 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -2194,8 +2194,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state } if (resched) { - if (!old_ct) + if (!old_ct) { + spin_lock_bh(&cp->lock); cp->flags &= ~IP_VS_CONN_F_NFCT; + spin_unlock_bh(&cp->lock); + } if (!atomic_read(&cp->n_control)) ip_vs_conn_expire_now(cp); __ip_vs_conn_put(cp); @@ -2211,8 +2214,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state if (sysctl_expire_nodest_conn(ipvs)) { bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb); - if (!old_ct) + if (!old_ct) { + spin_lock_bh(&cp->lock); cp->flags &= ~IP_VS_CONN_F_NFCT; + spin_unlock_bh(&cp->lock); + } ip_vs_conn_expire_now(cp); __ip_vs_conn_put(cp); -- cgit v1.2.3 From d9a33cadc70a94c1582f65e6042e81027cd200c6 Mon Sep 17 00:00:00 2001 From: Minhong He Date: Mon, 20 Jul 2026 15:25:18 +0800 Subject: mctp: check register_netdevice_notifier() error in mctp_device_init() mctp_device_init() handles errors from rtnl_af_register() and rtnl_register_many(), but ignores the return value of register_netdevice_notifier(). If notifier registration fails, init can still return success while the module is only partially initialized. Check the notifier registration error and fail module init early. Fixes: 583be982d934 ("mctp: Add device handling and netlink interface") Signed-off-by: Minhong He Link: https://patch.msgid.link/20260720072518.112614-1-heminhong@kylinos.cn Signed-off-by: Jakub Kicinski --- net/mctp/device.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mctp/device.c b/net/mctp/device.c index 2c84df674669..822120e860c8 100644 --- a/net/mctp/device.c +++ b/net/mctp/device.c @@ -536,7 +536,9 @@ int __init mctp_device_init(void) { int err; - register_netdevice_notifier(&mctp_dev_nb); + err = register_netdevice_notifier(&mctp_dev_nb); + if (err) + return err; err = rtnl_af_register(&mctp_af_ops); if (err) -- cgit v1.2.3 From 39e88f28fb32bf02bd4b525c24c842c9cff5663d Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Sun, 19 Jul 2026 22:15:23 +0000 Subject: netfilter: nft_payload: fix mask build for partial field offload nft_payload_offload_mask() builds the offload match mask for a payload expression that covers only part of a header field. For a partial IPv6 address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which is undefined on the 32-bit int operand. It also trims only one word, so the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 the trim is skipped entirely), leaving the mask covering more bytes than the rule matches. UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 shift exponent 120 is too large for 32-bit type 'int' ... The match is byte-granular and struct nft_data is zero-initialised, so the correct mask is simply the first priv_len bytes set to 0xff. Set those bytes directly and drop the word/shift trimming; this removes the undefined shift and no longer over-masks the trailing bytes. Fixes: a5d45bc0dc50 ("netfilter: nftables_offload: build mask based from the matching bytes") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_payload.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 391539a1ceaa..8a4472fd77d9 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -259,9 +259,7 @@ nla_put_failure: static bool nft_payload_offload_mask(struct nft_offload_reg *reg, u32 priv_len, u32 field_len) { - unsigned int remainder, delta, k; struct nft_data mask = {}; - __be32 remainder_mask; if (priv_len == field_len) { memset(®->mask, 0xff, priv_len); @@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg, return false; } - memset(&mask, 0xff, field_len); - remainder = priv_len % sizeof(u32); - if (remainder) { - k = priv_len / sizeof(u32); - delta = field_len - priv_len; - remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1)); - mask.data[k] = (__force u32)remainder_mask; - } - + memset(&mask, 0xff, priv_len); memcpy(®->mask, &mask, field_len); return true; -- cgit v1.2.3 From 655111f878a455f724e20122929cf2afa52b76e4 Mon Sep 17 00:00:00 2001 From: Jackie Liu Date: Wed, 15 Jul 2026 15:44:55 +0800 Subject: tracing: Propagate errors from remote event bulk updates remote_events_dir_enable_write() ignores the return value from trace_remote_enable_event(). If a remote rejects an event state change, the write therefore reports success even though the affected event remains in its previous state. Keep trying all events, but retain and return the first error. This matches __ftrace_set_clr_event_nolock(), which permits partial updates while notifying userspace when an operation fails. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260715074455.3897-1-liu.yun@linux.dev Fixes: 775cb093bc50 ("tracing: Add events/ root files to trace remotes") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Jackie Liu Reviewed-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/trace_remote.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index ef42d9c38b37..e6724f947170 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -1149,10 +1149,21 @@ static ssize_t remote_events_dir_enable_write(struct file *filp, const char __us for (i = 0; i < remote->nr_events; i++) { struct remote_event *evt = &remote->events[i]; + int eret; - trace_remote_enable_event(remote, evt, enable); + eret = trace_remote_enable_event(remote, evt, enable); + /* + * Save the first error and return that. Some events + * may still have been enabled, but let the user + * know that something went wrong. + */ + if (!ret && eret) + ret = eret; } + if (ret) + return ret; + return count; } -- cgit v1.2.3 From c1d87e724ae55e781b7cc7ccafb34d9e668582b2 Mon Sep 17 00:00:00 2001 From: deepakraog Date: Wed, 15 Jul 2026 20:06:04 +0530 Subject: tracing: Fix resource leak on mmiotrace trace_pipe close The mmiotrace tracer was added May 12th 2008. At that time, resources created in pipe_open() could not be freed because there was not pipe_close function pointer of the tracer. The pipe_close function pointer was added in December 7th, 2009, but the mmiotrace tracer was not updated. mmio_pipe_open() allocates a header_iter and takes a pci_dev reference when trace_pipe is opened. mmio_close() frees them, but it was only wired to the tracer's .close callback. tracing_release_pipe() invokes .pipe_close, not .close, when the trace_pipe file is released. As a result, closing trace_pipe with the mmiotrace tracer active leaked the header_iter allocation and left a stale pci_dev reference. Set .pipe_close to mmio_close, matching how function_graph wires both callbacks to the same handler. Note, if the trace_pipe is read to completion, it will clean up the resources, but if one were to run: # head -n 1 /sys/kernel/tracing/trace_pipe VERSION 20070824 Over and over again, it would trigger a massive leak. Cc: stable@vger.kernel.org Fixes: c521efd1700a8 ("tracing: Add pipe_close interface) Link: https://patch.msgid.link/20260715143604.14481-1-gaikwad.dcg@gmail.com Signed-off-by: deepakraog Signed-off-by: Steven Rostedt --- kernel/trace/trace_mmiotrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index 226cf66e0d68..20812e7f911f 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -109,7 +109,6 @@ static void mmio_pipe_open(struct trace_iterator *iter) iter->private = hiter; } -/* XXX: This is not called when the pipe is closed! */ static void mmio_close(struct trace_iterator *iter) { struct header_iter *hiter = iter->private; @@ -279,6 +278,7 @@ static struct tracer mmio_tracer __read_mostly = .start = mmio_trace_start, .pipe_open = mmio_pipe_open, .close = mmio_close, + .pipe_close = mmio_close, .read = mmio_read, .print_line = mmio_print_line, .noboot = true, -- cgit v1.2.3 From 649ea07fc25a17aa51bff710baac1ab161022a7c Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 20 Jul 2026 13:22:28 +0200 Subject: net: airoha: fix ETS channel derivation in airoha_tc_setup_qdisc_ets() Derive the hardware QoS channel from opt->parent instead of opt->handle in airoha_tc_setup_qdisc_ets(). The ETS qdisc handle is either user-specified or auto-allocated by qdisc_alloc_handle() and bears no relation to the HTB leaf classid that identifies the hardware channel. HTB derives the channel from TC_H_MIN(opt->classid), and ETS is always attached as a child of an HTB leaf, so its opt->parent matches that classid. Using opt->handle instead can cause two ETS qdiscs on different HTB leaves to collide on the same hardware channel, corrupting scheduler configuration and stats. Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support") Reviewed-by: Simon Horman Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260720-airoha-ets-handle-fix-v2-1-6f7129ddc06f@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 90aa8b0210bd..79418e682f71 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -2543,8 +2543,7 @@ static int airoha_tc_setup_qdisc_ets(struct net_device *dev, if (opt->parent == TC_H_ROOT) return -EINVAL; - channel = TC_H_MAJ(opt->handle) >> 16; - channel = channel % AIROHA_NUM_QOS_CHANNELS; + channel = TC_H_MIN(opt->parent) % AIROHA_NUM_QOS_CHANNELS; switch (opt->command) { case TC_ETS_REPLACE: -- cgit v1.2.3 From 47f42ff521b4eeb46e82f9a46a4783a99f7570d7 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Mon, 20 Jul 2026 17:41:03 -0400 Subject: tipc: fix integer overflow in tipc_recvmsg() and tipc_recvstream() In tipc_recvmsg(), the copy length is computed as: copy = min_t(int, dlen - offset, buflen); buflen is size_t but min_t(int, ...) casts it to int. When buflen exceeds INT_MAX (e.g. 0xFFFFFFFF via io_uring provided buffers), it wraps negative, wins the comparison, and the negative copy length propagates to simple_copy_to_iter() where int-to-size_t promotion makes it SIZE_MAX, triggering a WARN_ON. tipc_recvstream() has the same pattern. Kernel panic - not syncing: kernel: panic_on_warn set ... RIP: 0010:simple_copy_to_iter+0x9e/0xd0 (net/core/datagram.c:521) Call Trace: __skb_datagram_iter+0x123/0x8b0 (net/core/datagram.c:402) skb_copy_datagram_iter+0x77/0x1a0 (net/core/datagram.c:534) tipc_recvmsg+0x3d7/0xe80 (net/tipc/socket.c:1934) io_recvmsg+0x47e/0xda0 Fix by changing min_t(int, ...) to min_t(size_t, ...) in both functions. The result is always <= (dlen - offset), which is bounded by TIPC maximum message size (0x1ffff bytes), so the implicit narrowing on assignment to int copy is always safe. Fixes: e9f8b10101c6 ("tipc: refactor function tipc_sk_recvmsg()") Fixes: ec8a09fbbeff ("tipc: refactor function tipc_sk_recv_stream()") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Cen Zhang (Microsoft) Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260720214103.47732-1-blbllhy@gmail.com Signed-off-by: Jakub Kicinski --- net/tipc/socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 55e695748332..185c24003b82 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1936,7 +1936,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, if (likely(!err)) { int offset = skb_cb->bytes_read; - copy = min_t(int, dlen - offset, buflen); + copy = min_t(size_t, dlen - offset, buflen); rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy); if (unlikely(rc)) goto exit; @@ -2068,7 +2068,7 @@ static int tipc_recvstream(struct socket *sock, struct msghdr *m, /* Copy data if msg ok, otherwise return error/partial data */ if (likely(!err)) { offset = skb_cb->bytes_read; - copy = min_t(int, dlen - offset, buflen - copied); + copy = min_t(size_t, dlen - offset, buflen - copied); rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy); if (unlikely(rc)) break; -- cgit v1.2.3 From b9e558976bb968162c35ddccdb076a77fc906993 Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Tue, 21 Jul 2026 12:07:31 +0530 Subject: bnge/bng_re: fix ring ID widths Firmware requires more than 16 bits to address TX ring IDs for its internal QP management. Widen the associated HSI ring ID fields to 32 bits. The values firmware assigns remain within 24 bits, bounded by the hardware doorbell XID field. The fw_ring_id field belongs to bnge_ring_struct, a common struct shared by all ring types, so widening it to u32 applies uniformly across TX, RX, CP, and NQ rings but firmware assigns values within 16-bit range for all ring types except TX, which requires the wider field. Note that, Thor Ultra hardware has not yet been deployed and no firmware has been released to field, so backward compatibility is not a concern. Fixes: 42d1c54d6248 ("bnge/bng_re: Add a new HSI") Signed-off-by: Vikas Gupta Reviewed-by: Siva Reddy Kallam Reviewed-by: Dharmender Garg Reviewed-by: Yendapally Reddy Dhananjaya Reddy Link: https://patch.msgid.link/20260721063731.2622500-1-vikas.gupta@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/infiniband/hw/bng_re/bng_dev.c | 6 +-- drivers/net/ethernet/broadcom/bnge/bnge.h | 1 + drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c | 8 ++-- drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h | 2 +- drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 50 +++++++++++----------- drivers/net/ethernet/broadcom/bnge/bnge_netdev.h | 4 +- drivers/net/ethernet/broadcom/bnge/bnge_rmem.h | 2 +- include/linux/bnge/hsi.h | 7 ++- 8 files changed, 39 insertions(+), 41 deletions(-) diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c index 71a7ca2196ad..311c8bc93160 100644 --- a/drivers/infiniband/hw/bng_re/bng_dev.c +++ b/drivers/infiniband/hw/bng_re/bng_dev.c @@ -113,7 +113,7 @@ static void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg, } static int bng_re_net_ring_free(struct bng_re_dev *rdev, - u16 fw_ring_id, int type) + u32 fw_ring_id, int type) { struct bnge_auxr_dev *aux_dev = rdev->aux_dev; struct hwrm_ring_free_input req = {}; @@ -123,7 +123,7 @@ static int bng_re_net_ring_free(struct bng_re_dev *rdev, bng_re_init_hwrm_hdr((void *)&req, HWRM_RING_FREE); req.ring_type = type; - req.ring_id = cpu_to_le16(fw_ring_id); + req.ring_id = cpu_to_le32(fw_ring_id); bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT); rc = bnge_send_msg(aux_dev, &fw_msg); @@ -161,7 +161,7 @@ static int bng_re_net_ring_alloc(struct bng_re_dev *rdev, sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT); rc = bnge_send_msg(aux_dev, &fw_msg); if (!rc) - *fw_ring_id = le16_to_cpu(resp.ring_id); + *fw_ring_id = (u16)le32_to_cpu(resp.ring_id); return rc; } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h index f21cff651fd4..4479ccd071f5 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge.h @@ -36,6 +36,7 @@ struct bnge_pf_info { }; #define INVALID_HW_RING_ID ((u16)-1) +#define INVALID_HW_RING_ID_32BIT (U32_MAX) enum { BNGE_FW_CAP_SHORT_CMD = BIT_ULL(0), diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c index 1c9cfec1b633..651c5e783516 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c @@ -1283,7 +1283,7 @@ int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn) int hwrm_ring_free_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring, - u32 ring_type, int cmpl_ring_id) + u32 ring_type, u32 cmpl_ring_id) { struct hwrm_ring_free_input *req; struct bnge_dev *bd = bn->bd; @@ -1295,7 +1295,7 @@ int hwrm_ring_free_send_msg(struct bnge_net *bn, req->cmpl_ring = cpu_to_le16(cmpl_ring_id); req->ring_type = ring_type; - req->ring_id = cpu_to_le16(ring->fw_ring_id); + req->ring_id = cpu_to_le32(ring->fw_ring_id); bnge_hwrm_req_hold(bd, req); rc = bnge_hwrm_req_send(bd, req); @@ -1317,7 +1317,7 @@ int hwrm_ring_alloc_send_msg(struct bnge_net *bn, struct hwrm_ring_alloc_output *resp; struct hwrm_ring_alloc_input *req; struct bnge_dev *bd = bn->bd; - u16 ring_id, flags = 0; + u32 ring_id, flags = 0; int rc; rc = bnge_hwrm_req_init(bd, req, HWRM_RING_ALLOC); @@ -1401,7 +1401,7 @@ int hwrm_ring_alloc_send_msg(struct bnge_net *bn, resp = bnge_hwrm_req_hold(bd, req); rc = bnge_hwrm_req_send(bd, req); - ring_id = le16_to_cpu(resp->ring_id); + ring_id = le32_to_cpu(resp->ring_id); bnge_hwrm_req_drop(bd, req); exit: diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h index 3501de7a89b9..bf452e390d5b 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h @@ -50,7 +50,7 @@ int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd, void bnge_hwrm_stat_ctx_free(struct bnge_net *bn); int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn); int hwrm_ring_free_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring, - u32 ring_type, int cmpl_ring_id); + u32 ring_type, u32 cmpl_ring_id); int hwrm_ring_alloc_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring, u32 ring_type, u32 map_index); diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index 70768193004c..6f7ef506d4e1 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -1327,12 +1327,12 @@ err_free_core: return rc; } -u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr) +u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr) { return rxr->rx_cpr->ring_struct.fw_ring_id; } -u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr) +u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr) { return txr->tx_cpr->ring_struct.fw_ring_id; } @@ -1375,12 +1375,12 @@ static void bnge_init_nq_tree(struct bnge_net *bn) struct bnge_nq_ring_info *nqr = &bn->bnapi[i]->nq_ring; struct bnge_ring_struct *ring = &nqr->ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; for (j = 0; j < nqr->cp_ring_count; j++) { struct bnge_cp_ring_info *cpr = &nqr->cp_ring_arr[j]; ring = &cpr->ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } } } @@ -1637,7 +1637,7 @@ static void bnge_init_one_rx_ring_rxbd(struct bnge_net *bn, ring = &rxr->rx_ring_struct; bnge_init_rxbd_pages(ring, type); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn, @@ -1647,7 +1647,7 @@ static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn, u32 type; ring = &rxr->rx_agg_ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; if (bnge_is_agg_reqd(bn->bd)) { type = ((u32)BNGE_RX_PAGE_SIZE << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP; @@ -1708,7 +1708,7 @@ static void bnge_init_tx_rings(struct bnge_net *bn) struct bnge_tx_ring_info *txr = &bn->tx_ring[i]; struct bnge_ring_struct *ring = &txr->tx_ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; netif_queue_set_napi(bn->netdev, i, NETDEV_QUEUE_TYPE_TX, &txr->bnapi->napi); @@ -1867,7 +1867,7 @@ static int bnge_hwrm_rx_agg_ring_alloc(struct bnge_net *bn, ring->fw_ring_id); bnge_db_write(bn->bd, &rxr->rx_agg_db, rxr->rx_agg_prod); bnge_db_write(bn->bd, &rxr->rx_db, rxr->rx_prod); - bn->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id; + bn->grp_info[grp_idx].agg_fw_ring_id = (u16)ring->fw_ring_id; return 0; } @@ -1886,7 +1886,7 @@ static int bnge_hwrm_rx_ring_alloc(struct bnge_net *bn, return rc; bnge_set_db(bn, &rxr->rx_db, type, map_idx, ring->fw_ring_id); - bn->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id; + bn->grp_info[map_idx].rx_fw_ring_id = (u16)ring->fw_ring_id; return 0; } @@ -1916,7 +1916,7 @@ static int bnge_hwrm_ring_alloc(struct bnge_net *bn) bnge_set_db(bn, &nqr->nq_db, type, map_idx, ring->fw_ring_id); bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons); enable_irq(vector); - bn->grp_info[i].nq_fw_ring_id = ring->fw_ring_id; + bn->grp_info[i].nq_fw_ring_id = (u16)ring->fw_ring_id; if (!i) { rc = bnge_hwrm_set_async_event_cr(bd, ring->fw_ring_id); @@ -1986,15 +1986,13 @@ void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic) tbl_size = bnge_get_rxfh_indir_size(bd); for (i = 0; i < tbl_size; i++) { - u16 ring_id, j; + u32 j; j = bd->rss_indir_tbl[i]; rxr = &bn->rx_ring[j]; - ring_id = rxr->rx_ring_struct.fw_ring_id; - *ring_tbl++ = cpu_to_le16(ring_id); - ring_id = bnge_cp_ring_for_rx(rxr); - *ring_tbl++ = cpu_to_le16(ring_id); + *ring_tbl++ = cpu_to_le16(rxr->rx_ring_struct.fw_ring_id); + *ring_tbl++ = cpu_to_le16(bnge_cp_ring_for_rx(rxr)); } } @@ -2285,7 +2283,7 @@ static void bnge_disable_int(struct bnge_net *bn) nqr = &bnapi->nq_ring; ring = &nqr->ring_struct; - if (ring->fw_ring_id != INVALID_HW_RING_ID) + if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT) bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons); } } @@ -2401,7 +2399,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn, u32 grp_idx = rxr->bnapi->index; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = bnge_cp_ring_for_rx(rxr); @@ -2409,7 +2407,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn, RING_FREE_REQ_RING_TYPE_RX, close_path ? cmpl_ring_id : INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[grp_idx].rx_fw_ring_id = INVALID_HW_RING_ID; } @@ -2421,14 +2419,14 @@ static void bnge_hwrm_rx_agg_ring_free(struct bnge_net *bn, u32 grp_idx = rxr->bnapi->index; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = bnge_cp_ring_for_rx(rxr); hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_RX_AGG, close_path ? cmpl_ring_id : INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[grp_idx].agg_fw_ring_id = INVALID_HW_RING_ID; } @@ -2439,14 +2437,14 @@ static void bnge_hwrm_tx_ring_free(struct bnge_net *bn, struct bnge_ring_struct *ring = &txr->tx_ring_struct; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = close_path ? bnge_cp_ring_for_tx(txr) : INVALID_HW_RING_ID; hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_TX, cmpl_ring_id); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_hwrm_cp_ring_free(struct bnge_net *bn, @@ -2455,12 +2453,12 @@ static void bnge_hwrm_cp_ring_free(struct bnge_net *bn, struct bnge_ring_struct *ring; ring = &cpr->ring_struct; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_L2_CMPL, INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path) @@ -2496,11 +2494,11 @@ static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path) bnge_hwrm_cp_ring_free(bn, &nqr->cp_ring_arr[j]); ring = &nqr->ring_struct; - if (ring->fw_ring_id != INVALID_HW_RING_ID) { + if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT) { hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_NQ, INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[i].nq_fw_ring_id = INVALID_HW_RING_ID; } } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h index f4636b5b0cf3..d177919c2e11 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h @@ -630,8 +630,8 @@ struct bnge_l2_filter { refcount_t refcnt; }; -u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr); -u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr); +u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr); +u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr); void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic); int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr, u16 prod, gfp_t gfp); diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h index 341c7f81ed09..bb0c79a1ee60 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h @@ -184,7 +184,7 @@ struct bnge_ctx_mem_info { struct bnge_ring_struct { struct bnge_ring_mem_info ring_mem; - u16 fw_ring_id; + u32 fw_ring_id; union { u16 grp_idx; u16 map_idx; /* Used by NQs */ diff --git a/include/linux/bnge/hsi.h b/include/linux/bnge/hsi.h index 8ea13d5407ee..1f7bd96415a5 100644 --- a/include/linux/bnge/hsi.h +++ b/include/linux/bnge/hsi.h @@ -8317,8 +8317,7 @@ struct hwrm_ring_alloc_output { __le16 req_type; __le16 seq_id; __le16 resp_len; - __le16 ring_id; - __le16 logical_ring_id; + __le32 ring_id; u8 push_buffer_index; #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PING_BUFFER 0x0UL #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER 0x1UL @@ -8345,10 +8344,10 @@ struct hwrm_ring_free_input { u8 flags; #define RING_FREE_REQ_FLAGS_VIRTIO_RING_VALID 0x1UL #define RING_FREE_REQ_FLAGS_LAST RING_FREE_REQ_FLAGS_VIRTIO_RING_VALID - __le16 ring_id; + __le16 unused_1; __le32 prod_idx; __le32 opaque; - __le32 unused_1; + __le32 ring_id; }; /* hwrm_ring_free_output (size:128b/16B) */ -- cgit v1.2.3 From 0f71f852a96af9685858ce59fda34ecbf85c283d Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Tue, 21 Jul 2026 01:58:45 -0700 Subject: phonet: pep: fix use-after-free in pep_get_sb() pep_get_sb() doesn't consider that pskb_may_pull() might have relocated the skb data, and continue to access the older pointer, causing UAF. Reproduced under KASAN: BUG: KASAN: slab-use-after-free in pep_get_sb+0x234/0x3b0 Read of size 1 at addr ff11000105510f50 by task repro/157 pep_get_sb+0x234/0x3b0 pipe_handler_do_rcv+0x5f7/0xa10 pep_do_rcv+0x203/0x410 __sk_receive_skb+0x471/0x4a0 phonet_rcv+0x5b3/0x6c0 __netif_receive_skb+0xcc/0x1d0 Refetch the header with skb_header_pointer() after pskb_may_pull(), so the possibly stale pointer is no longer dereferenced. There are better ways to solve this, but, this is the less instrusive one. Fixes: 9641458d3ec4 ("Phonet: Pipe End Point for Phonet Pipes protocol") Cc: stable@vger.kernel.org Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20260721-phonet_get_sb_uaf-v1-1-95fd7881cc4e@debian.org Signed-off-by: Jakub Kicinski --- net/phonet/pep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 706927139393..31b29e3ca7bc 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -55,6 +55,8 @@ static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen, ph = skb_header_pointer(skb, 0, 2, &h); if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len)) return NULL; + /* pskb_may_pull() may have reallocated the head; refetch ph. */ + ph = skb_header_pointer(skb, 0, 2, &h); ph->sb_len -= 2; *ptype = ph->sb_type; *plen = ph->sb_len; -- cgit v1.2.3 From d1ff66b66151c14b084e88040512a064b1c1e493 Mon Sep 17 00:00:00 2001 From: Minhong He Date: Tue, 21 Jul 2026 17:39:56 +0800 Subject: phonet: check register_netdevice_notifier() error in phonet_device_init() phonet_device_init() registers a netdevice notifier before calling phonet_netlink_register(), but does not check whether notifier registration succeeded. On failure, netlink setup still proceeds and init may return success without the notifier in place. Also, the existing phonet_netlink_register() failure path called phonet_device_exit(), which runs rtnl_unregister_all() even though rtnl_register_many() already unwound any partial registration. Calling the full exit helper on a partial init is not correct. Check each registration error, including proc_create_net(), and unwind only the steps that have succeeded so far, in reverse order. Signed-off-by: Minhong He Link: https://patch.msgid.link/20260721093956.162617-1-heminhong@kylinos.cn Signed-off-by: Jakub Kicinski --- net/phonet/pn_dev.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index ad44831d6745..1272d49cd038 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -350,16 +350,34 @@ static struct pernet_operations phonet_net_ops = { /* Initialize Phonet devices list */ int __init phonet_device_init(void) { - int err = register_pernet_subsys(&phonet_net_ops); + int err; + + err = register_pernet_subsys(&phonet_net_ops); if (err) return err; - proc_create_net("pnresource", 0, init_net.proc_net, &pn_res_seq_ops, - sizeof(struct seq_net_private)); - register_netdevice_notifier(&phonet_device_notifier); + if (!proc_create_net("pnresource", 0, init_net.proc_net, + &pn_res_seq_ops, sizeof(struct seq_net_private))) { + err = -ENOMEM; + goto err_pernet; + } + + err = register_netdevice_notifier(&phonet_device_notifier); + if (err) + goto err_proc; + err = phonet_netlink_register(); if (err) - phonet_device_exit(); + goto err_notifier; + + return 0; + +err_notifier: + unregister_netdevice_notifier(&phonet_device_notifier); +err_proc: + remove_proc_entry("pnresource", init_net.proc_net); +err_pernet: + unregister_pernet_subsys(&phonet_net_ops); return err; } @@ -367,8 +385,8 @@ void phonet_device_exit(void) { rtnl_unregister_all(PF_PHONET); unregister_netdevice_notifier(&phonet_device_notifier); - unregister_pernet_subsys(&phonet_net_ops); remove_proc_entry("pnresource", init_net.proc_net); + unregister_pernet_subsys(&phonet_net_ops); } int phonet_route_add(struct net_device *dev, u8 daddr) -- cgit v1.2.3 From 9b2854f86f0b56e9027d68e7a3fc909d1a9b566f Mon Sep 17 00:00:00 2001 From: Jun Yang Date: Tue, 21 Jul 2026 21:14:05 +0800 Subject: sctp: don't free the ASCONF's own transport in DEL-IP processing sctp_process_asconf() caches the transport the ASCONF chunk is processed against in asconf->transport (== chunk->transport, set once in sctp_rcv()). For an ASCONF located through its Address Parameter by __sctp_rcv_asconf_lookup(), that cached transport corresponds to the Address Parameter, which need not be the packet's source address. sctp_process_asconf_param() rejects a DEL-IP for the packet source address (ADDIP D8, SCTP_ERROR_DEL_SRC_IP), but nothing protects asconf->transport. A single ASCONF can therefore carry, in order: [Address Parameter L] [DEL-IP L] [DEL-IP 0.0.0.0] where L differs from the source. The DEL-IP for L passes the D8 check and calls sctp_assoc_rm_peer() on the transport that asconf->transport still points at, freeing it (RCU-deferred). The following wildcard DEL-IP then reuses the now-dangling asconf->transport in sctp_assoc_set_primary() and sctp_assoc_del_nonprimary_peers(): set_primary() dereferences the freed transport (->ipaddr, ->state) and plants the dangling pointer into asoc->peer.primary_path / active_path, and del_nonprimary_peers(), keeping only the pointer that is no longer on the list, removes every real transport, leaving the association with a transport_count of 0 and primary_path/active_path pointing at freed memory. Reject a DEL-IP that targets the transport the ASCONF is being processed against, mirroring the existing source-address guard, so the wildcard branch can never reuse a freed transport. Fixes: 42e30bf3463c ("[SCTP]: Handle the wildcard ADD-IP Address parameter") Cc: stable@kernel.org Signed-off-by: Jun Yang Acked-by: Xin Long Link: https://patch.msgid.link/tencent_73762ED1DF08CC9D5F5F61954B01350CFE0A@qq.com Signed-off-by: Jakub Kicinski --- net/sctp/sm_make_chunk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 8adac9e0cd66..c02809264075 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3153,6 +3153,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, if (!peer) return SCTP_ERROR_DNS_FAILED; + /* Don't free asconf->transport; a later wildcard DEL-IP + * parameter reuses it. + */ + if (peer == asconf->transport) + return SCTP_ERROR_REQ_REFUSED; + sctp_assoc_rm_peer(asoc, peer); break; case SCTP_PARAM_SET_PRIMARY: -- cgit v1.2.3 From 234e5e898b713bc0b3a631b6f002897f43d046c8 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Tue, 21 Jul 2026 23:12:28 +0200 Subject: mac802154: hold an interface reference across the scan worker mac802154_scan_worker() captures the scanning sub-interface under RCU and then keeps dereferencing sdata->dev after rcu_read_unlock() and outside the rtnl -- in the failure traces, in mac802154_transmit_beacon_req() (skb->dev = sdata->dev), and in the end_scan cleanup. Nothing keeps that netdev alive across the worker iteration. A concurrent DEL_INTERFACE or PHY removal can unregister the interface once the worker drops the rtnl between its two drv_set_channel() sections. unregister_netdevice() frees the netdev asynchronously from netdev_run_todo() with the rtnl already dropped, so neither holding the rtnl nor the per-PHY IEEE802154_IS_SCANNING flag prevents a stale worker iteration from dereferencing the freed netdev -- a KASAN slab-use-after-free, reachable by racing TRIGGER_SCAN against DEL_INTERFACE (both CAP_NET_ADMIN). Pin the netdev with netdev_hold() while the RCU read lock is still held, and release it at every worker exit. Fixes: 57588c71177f ("mac802154: Handle passive scanning") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Link: https://patch.msgid.link/20260721211228.34578-1-security@auditcode.ai Signed-off-by: Jakub Kicinski --- net/mac802154/scan.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 300d4584533e..65089826ff59 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -179,6 +179,7 @@ void mac802154_scan_worker(struct work_struct *work) enum nl802154_scan_types scan_req_type; struct ieee802154_sub_if_data *sdata; unsigned int scan_duration = 0; + netdevice_tracker dev_tracker; struct wpan_phy *wpan_phy; u8 scan_req_duration; u8 page, channel; @@ -209,6 +210,14 @@ void mac802154_scan_worker(struct work_struct *work) return; } + /* + * sdata->dev is dereferenced below after rcu_read_unlock() and outside + * the rtnl, and a concurrent DEL_INTERFACE / PHY teardown can free it + * asynchronously from netdev_run_todo(). Pin it with a reference taken + * while the RCU read lock is still held, and drop it at every exit. + */ + netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC); + wpan_phy = scan_req->wpan_phy; scan_req_type = scan_req->type; scan_req_duration = scan_req->duration; @@ -262,12 +271,14 @@ void mac802154_scan_worker(struct work_struct *work) "Scan page %u channel %u for %ums\n", page, channel, jiffies_to_msecs(scan_duration)); queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration); + netdev_put(sdata->dev, &dev_tracker); return; end_scan: rtnl_lock(); mac802154_scan_cleanup_locked(local, sdata, false); rtnl_unlock(); + netdev_put(sdata->dev, &dev_tracker); } int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata, -- cgit v1.2.3 From ad1d940efbf13fba516821f3d00fbd34a26e289e Mon Sep 17 00:00:00 2001 From: Alexandre Hamamdjian Date: Thu, 23 Jul 2026 20:24:09 +0700 Subject: Input: edt-ft5x06 - ignore contacts with an out-of-range slot id The per-contact slot id is taken from the top nibble of the third report byte, so it can be any value from 0 to 15. The driver only allocates max_support_points MT slots (2 to 10 depending on the variant), so a report that carries an id at or above that count - be it a genuinely higher-numbered contact or a corrupted byte - is outside the range the input core was told about. input_mt_slot() silently ignores an ABS_MT_SLOT beyond num_slots and leaves the current slot unchanged, so the following input_mt_report_slot_state()/touchscreen_report_pos() pair is applied to whichever slot happened to be selected last, reporting the contact at the wrong position. Skip such entries instead. Signed-off-by: Alexandre Hamamdjian Link: https://patch.msgid.link/20260723-b4-ft5426-v2-1-cd2bed168051@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d3b1177185a3..d6c3d033b83d 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -331,6 +331,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) swap(x, y); id = (buf[2] >> 4) & 0x0f; + if (id >= tsdata->max_support_points) + continue; input_mt_slot(tsdata->input, id); if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, -- cgit v1.2.3 From 7d971337ebfad0b173cb46097c709db174ac3557 Mon Sep 17 00:00:00 2001 From: Kyohei Kadota Date: Thu, 23 Jul 2026 19:55:57 +0900 Subject: Input: xpad - add support for ZENAIM LEVERLESS Add the VID/PID for the ZENAIM LEVERLESS controller to xpad_device and the VID to xpad_table. Signed-off-by: KADOTA, Kyohei Link: https://patch.msgid.link/CAFMepckDUuOHiDDVVhUYc-UqJMeCqrWSfCuxbJ2x2sGgdDD4nw@mail.gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index feb8f368f834..2da0b7f1722a 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -394,6 +394,7 @@ static const struct xpad_device { { 0x3285, 0x0646, "Nacon Pro Compact", 0, XTYPE_XBOXONE }, { 0x3285, 0x0662, "Nacon Revolution5 Pro", 0, XTYPE_XBOX360 }, { 0x3285, 0x0663, "Nacon Evol-X", 0, XTYPE_XBOXONE }, + { 0x3507, 0x000b, "ZENAIM LEVERLESS", 0, XTYPE_XBOX360 }, { 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 }, { 0x3537, 0x100f, "GameSir Nova 2 Lite", 0, XTYPE_XBOX360 }, { 0x3537, 0x1010, "GameSir G7 SE", 0, XTYPE_XBOXONE }, @@ -557,6 +558,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */ XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */ XPAD_XBOXONE_VENDOR(0x3285), /* Nacon Evol-X */ + XPAD_XBOX360_VENDOR(0x3507), /* ZENAIM Controllers */ XPAD_XBOX360_VENDOR(0x3537), /* GameSir Controllers */ XPAD_XBOXONE_VENDOR(0x3537), /* GameSir Controllers */ XPAD_XBOX360_VENDOR(0x3651), /* CRKD Controllers */ -- cgit v1.2.3 From d4a00d61a5c2c24973175ace5368d1f6acf9bb0a Mon Sep 17 00:00:00 2001 From: Kuba Piecuch Date: Thu, 23 Jul 2026 09:59:55 +0000 Subject: selftests/sched_ext: Handle sleeping task affinity changes in numa test When a sleeping task's affinity is changed, task_cpu(p) can be outside of p->cpus_ptr until after select_task_rq() selects a new runqueue for the task during wakeup. Thus, the task's NUMA node determined by numa_select_cpu() can be completely outside of the task's cpumask, leading to scx_pick_{idle,any}_cpu_node() failing to find an eligible CPU and returning -EBUSY. This leads to the numa.bpf.c scheduler abnormally exiting with the following message in dmesg: sched_ext: numa: invalid CPU -16 scx_bpf_cpu_node+0x120/0x190 bpf_prog_0a34b8e0f515771f_numa_select_cpu+0x108/0x14e bpf__sched_ext_ops_select_cpu+0x4f/0xb4 select_task_rq_scx+0xb0/0x210 select_task_rq+0xa0/0xd0 __try_to_wake_up+0x196/0x650 complete_all+0x76/0x100 migration_cpu_stop+0x22b/0x300 cpu_stopper_thread+0xc1/0x180 smpboot_thread_fn+0x16b/0x230 kthread+0x2d7/0x350 ret_from_fork+0x1c2/0x350 ret_from_fork_asm+0x1a/0x30 Make numa_select_cpu() robust against this case by returning @prev_cpu if no CPU could be found in the selected NUMA node _and_ we have reason to believe that the task's affinity was changed while it was sleeping. Fixes: 5ae5161820e5 ("selftests/sched_ext: Add NUMA-aware scheduler test") Signed-off-by: Kuba Piecuch Signed-off-by: Tejun Heo --- tools/testing/selftests/sched_ext/numa.bpf.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/sched_ext/numa.bpf.c b/tools/testing/selftests/sched_ext/numa.bpf.c index 78cc49a7f9a6..6b4515c28aa0 100644 --- a/tools/testing/selftests/sched_ext/numa.bpf.c +++ b/tools/testing/selftests/sched_ext/numa.bpf.c @@ -34,7 +34,8 @@ static bool is_cpu_idle(s32 cpu, int node) s32 BPF_STRUCT_OPS(numa_select_cpu, struct task_struct *p, s32 prev_cpu, u64 wake_flags) { - int node = __COMPAT_scx_bpf_cpu_node(scx_bpf_task_cpu(p)); + s32 task_cpu = scx_bpf_task_cpu(p); + int node = __COMPAT_scx_bpf_cpu_node(task_cpu); s32 cpu; /* @@ -48,6 +49,16 @@ s32 BPF_STRUCT_OPS(numa_select_cpu, cpu = __COMPAT_scx_bpf_pick_any_cpu_node(p->cpus_ptr, node, __COMPAT_SCX_PICK_IDLE_IN_NODE); + /* + * @task_cpu may be outside of p->cpus_ptr if @p's affinity + * changed while it was sleeping. This means it's possible for + * p->cpus_ptr to not include any CPUs from @node. + * If we failed to find a cpu in @node, check if @task_cpu + * is outside of p->cpus_ptr and just return @prev_cpu if it is. + */ + if (cpu < 0 && !bpf_cpumask_test_cpu(task_cpu, p->cpus_ptr)) + return prev_cpu; + if (is_cpu_idle(cpu, node)) scx_bpf_error("CPU %d should be marked as busy", cpu); -- cgit v1.2.3 From f3ca0ee2cc308e33896536789cbc5f3a12ca7b30 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Wed, 22 Jul 2026 00:14:38 +0200 Subject: mptcp: decrement subflows counter on failed passive join mptcp_pm_allow_new_subflow() increments extra_subflows before __mptcp_finish_join() on the passive MP_JOIN path. In case of race conditions, the subflow is dropped without calling mptcp_close_ssk(), so the counter is not rolled back. Call mptcp_pm_close_subflow() when the join completion fails to decrement the subflows counter. Fixes: 10f6d46c943d ("mptcp: fix race between MP_JOIN and close") Cc: stable@vger.kernel.org Signed-off-by: Chenguang Zhao Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-1-6fb595bc86ef@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index cb9515f505aa..b32f0cd262a7 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3907,6 +3907,7 @@ bool mptcp_finish_join(struct sock *ssk) mptcp_data_unlock(parent); if (!ret) { + mptcp_pm_close_subflow(msk); err_prohibited: subflow->reset_reason = MPTCP_RST_EPROHIBIT; return false; -- cgit v1.2.3 From 9bc6d5e4ca9f3cbb41d43400b3a31cb0403796c9 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 22 Jul 2026 00:14:39 +0200 Subject: mptcp: pm: userspace: fix use-after-free in get_local_id In mptcp_pm_userspace_get_local_id(), the address entry is looked up under spinlock, but its id is read after dropping the lock. A concurrent deletion can free the entry between the unlock and the read, leading to UAF. The race window is narrow. It was reproduced only with a locally constructed stress test that repeatedly overlaps an MP_JOIN SYN with a MPTCP_PM_CMD_SUBFLOW_DESTROY request. However, the KASAN report below confirms that the race is reachable: [ 666.319376] BUG: KASAN: slab-use-after-free in mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319386] Read of size 1 at addr ffff888124845610 by task swapper/0/0 ... [ 666.319401] Call Trace: [ 666.319405] [ 666.319408] dump_stack_lvl+0x53/0x70 [ 666.319412] print_address_description.constprop.0+0x2c/0x3b0 [ 666.319418] print_report+0xbe/0x2b0 [ 666.319421] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319423] kasan_report+0xce/0x100 [ 666.319426] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319429] mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319433] mptcp_pm_get_local_id+0x371/0x440 ... [ 666.319821] Allocated by task 45539: [ 666.319844] kasan_save_stack+0x33/0x60 [ 666.319855] kasan_save_track+0x14/0x30 [ 666.319858] __kasan_kmalloc+0x8f/0xa0 [ 666.319863] __kmalloc_noprof+0x1e7/0x520 [ 666.319867] sock_kmalloc+0xdf/0x130 [ 666.319885] sock_kmemdup+0x1b/0x40 [ 666.319888] mptcp_userspace_pm_append_new_local_addr+0x261/0x500 [ 666.319910] mptcp_pm_nl_announce_doit+0x16a/0x610 ... [ 666.319967] Freed by task 45560: [ 666.319988] kasan_save_stack+0x33/0x60 [ 666.319991] kasan_save_track+0x14/0x30 [ 666.319994] kasan_save_free_info+0x3b/0x60 [ 666.319998] __kasan_slab_free+0x43/0x70 [ 666.320000] kfree+0x166/0x440 [ 666.320003] sock_kfree_s+0x1d/0x50 [ 666.320007] mptcp_userspace_pm_delete_local_addr.isra.0+0x157/0x200 [ 666.320011] mptcp_pm_nl_subflow_destroy_doit+0x51d/0xea0 Fix by copying the id into a local variable while still holding the lock, and use -1 as a "not found" sentinel. Fixes: f012d796a6de ("mptcp: check addrs list in userspace_pm_get_local_id") Cc: stable@vger.kernel.org Signed-off-by: Geliang Tang Tested-by: Xuanqiang Luo Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-2-6fb595bc86ef@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/pm_userspace.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index d100867e9202..945aa5afc2dd 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -132,12 +132,15 @@ int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, __be16 msk_sport = ((struct inet_sock *) inet_sk((struct sock *)msk))->inet_sport; struct mptcp_pm_addr_entry *entry; + int id; spin_lock_bh(&msk->pm.lock); entry = mptcp_userspace_pm_lookup_addr(msk, &skc->addr); + id = entry ? entry->addr.id : -1; spin_unlock_bh(&msk->pm.lock); - if (entry) - return entry->addr.id; + + if (id != -1) + return id; if (skc->addr.port == msk_sport) skc->addr.port = 0; -- cgit v1.2.3 From bd7aae448f6ee9d82599a4474664de1e6e91a535 Mon Sep 17 00:00:00 2001 From: Kalpan Jani Date: Wed, 22 Jul 2026 00:14:40 +0200 Subject: mptcp: fix stale skb->sk reference on subflow close The backlog list is updated by mptcp_data_ready() under mptcp_data_lock(). The cleanup of backlog references to a closing subflow, however, was performed in mptcp_close_ssk(), before __mptcp_close_ssk() acquires the ssk lock, and while holding neither the ssk lock nor mptcp_data_lock(). Because that traversal ran without mptcp_data_lock(), concurrent softirq RX processing on another CPU (subflow_data_ready() -> mptcp_data_ready() -> __mptcp_add_backlog(), under mptcp_data_lock()) could add a backlog entry referencing the ssk while the cleanup loop was in progress. Such an entry could be missed by the cleanup, or the concurrent list update could corrupt the traversal, leaving skb->sk pointing at the ssk after it is freed. A later mptcp_backlog_purge() then dereferences the stale pointer, triggering a warning in inet_sock_destruct() (ssk->sk_rmem_alloc != 0) followed by a use-after-free in mptcp_backlog_purge(). Fix this by moving the backlog cleanup into __mptcp_close_ssk(), after subflow->closing is set to 1 and while the ssk lock is still held, serialized under mptcp_data_lock(). The cleanup runs only on the push path (MPTCP_CF_PUSH), where backlog references accumulate; on other teardown paths the caller already handles cleanup. With subflow->closing set and mptcp_data_lock() held across the purge, any concurrent mptcp_data_ready() either completes its enqueue before the purge runs and is caught, or observes closing=1 and bails out. Once mptcp_data_unlock() is reached, no new skb referencing the ssk can be enqueued, so the cleanup is exhaustive. Remove the unprotected traversal from mptcp_close_ssk() entirely. Fixes: ee458a3f314e ("mptcp: introduce mptcp-level backlog") Cc: stable@vger.kernel.org Suggested-by: Paolo Abeni Reported-by: Matthieu Baerts (NGI0) Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/621 Signed-off-by: Kalpan Jani Acked-by: Paolo Abeni Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-3-6fb595bc86ef@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index b32f0cd262a7..ca644ec53eed 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2545,6 +2545,22 @@ static void __mptcp_subflow_disconnect(struct sock *ssk, } } +static void mptcp_cleanup_ssk_backlog(struct sock *sk, struct sock *ssk) +{ + struct mptcp_sock *msk = mptcp_sk(sk); + struct sk_buff *skb; + + mptcp_data_lock(sk); + list_for_each_entry(skb, &msk->backlog_list, list) { + if (skb->sk != ssk) + continue; + + atomic_sub(skb->truesize, &skb->sk->sk_rmem_alloc); + skb->sk = NULL; + } + mptcp_data_unlock(sk); +} + /* subflow sockets can be either outgoing (connect) or incoming * (accept). * @@ -2568,6 +2584,9 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); subflow->closing = 1; + if (flags & MPTCP_CF_PUSH) + mptcp_cleanup_ssk_backlog(sk, ssk); + /* Borrow the fwd allocated page left-over; fwd memory for the subflow * could be negative at this point, but will be reach zero soon - when * the data allocated using such fragment will be freed. @@ -2659,9 +2678,6 @@ out: void mptcp_close_ssk(struct sock *sk, struct sock *ssk, struct mptcp_subflow_context *subflow) { - struct mptcp_sock *msk = mptcp_sk(sk); - struct sk_buff *skb; - /* The first subflow can already be closed or disconnected */ if (subflow->close_event_done || READ_ONCE(subflow->local_id) < 0) return; @@ -2671,17 +2687,6 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk, if (sk->sk_state == TCP_ESTABLISHED) mptcp_event(MPTCP_EVENT_SUB_CLOSED, mptcp_sk(sk), ssk, GFP_KERNEL); - /* Remove any reference from the backlog to this ssk; backlog skbs consume - * space in the msk receive queue, no need to touch sk->sk_rmem_alloc - */ - list_for_each_entry(skb, &msk->backlog_list, list) { - if (skb->sk != ssk) - continue; - - atomic_sub(skb->truesize, &skb->sk->sk_rmem_alloc); - skb->sk = NULL; - } - /* subflow aborted before reaching the fully_established status * attempt the creation of the next subflow */ -- cgit v1.2.3 From e3213292c4fd69ba442c6ed4693f91a92b753140 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 22 Jul 2026 00:14:41 +0200 Subject: selftests: mptcp: userspace_pm: fix undefined variable port In make_connection(), the variable "port" is used but never defined. This leads to an empty argument being passed to wait_local_port_listen(), causing "printf: : invalid number" errors: # INFO: Init # 01 Created network namespaces ns1, ns2 [ OK ] # INFO: Make connections # ./../lib.sh: line 651: printf: : invalid number # 02 Established IPv4 MPTCP Connection ns2 => ns1 [ OK ] # INFO: Connection info: 10.0.1.2:59516 -> 10.0.1.1:50002 # ./../lib.sh: line 651: printf: : invalid number # 03 Established IPv6 MPTCP Connection ns2 => ns1 [ OK ] Fix it by using the correctly defined variable "app_port", which holds the appropriate port number for the connection. Fixes: 39348f5f2f13 ("selftests: mptcp: wait for port instead of sleep") Cc: stable@vger.kernel.org Signed-off-by: Geliang Tang Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-4-6fb595bc86ef@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/userspace_pm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index e9ae1806ab07..30a809752d1b 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -212,7 +212,7 @@ make_connection() ./mptcp_connect -s MPTCP -w 300 -p $app_port -l $listen_addr > /dev/null 2>&1 & local server_pid=$! - mptcp_lib_wait_local_port_listen "${ns1}" "${port}" + mptcp_lib_wait_local_port_listen "${ns1}" "${app_port}" # Run the client, transfer $file and stay connected to the server # to conduct tests -- cgit v1.2.3 From 133cca19d75b9264bc2bbcdf2c3b80e3da207649 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Wed, 22 Jul 2026 00:14:42 +0200 Subject: mptcp: fix BUILD_BUG_ON on legacy ARM config The 0-day bot managed to find kernel configs that cause build failures, e.g. when using the StrongARM SA1100 target (ARMv4). On such legacy ARM architecture, all structures are apparently aligned to 32 bits, causing build issue here. Indeed, on such architecture, 'flags' size is not equivalent to sizeof(u16) as expected, but to sizeof(u32). Instead, use memset(). It was not used before to ensure a simple clear operation was used by the compiler. But at the end, it shouldn't matter, and the compiler should optimise this to the same operation with or without memset() when -O above 0 is used. So let's switch to memset() to fix this issue, and reduce this complexity. Fixes: 5e939544f9d2 ("mptcp: fix uninit-value in mptcp_established_options") Cc: stable@vger.kernel.org Suggested-by: Frank Ranner Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605312026.Srgsz7Tp-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202607031100.upQfRZTM-lkp@intel.com/ Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-5-6fb595bc86ef@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 1b74ca5b6a59..c664023d37ba 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -571,10 +571,7 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, bool ret = false; /* Zero `use_ack` and `use_map` flags with one shot. */ - BUILD_BUG_ON(sizeof_field(struct mptcp_ext, flags) != sizeof(u16)); - BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct mptcp_ext, flags), - sizeof(u16))); - *(u16 *)&opts->ext_copy.flags = 0; + memset(&opts->ext_copy.flags, 0, sizeof(opts->ext_copy.flags)); opts->csum_reqd = READ_ONCE(msk->csum_enabled); mpext = skb ? mptcp_get_ext(skb) : NULL; -- cgit v1.2.3 From 5e9c8baee0329fbefe7c67aea945e2a07f15e98b Mon Sep 17 00:00:00 2001 From: Yehyeong Lee Date: Wed, 22 Jul 2026 21:28:17 +0900 Subject: net: drop_monitor: fix info leak in NET_DM_ATTR_PAYLOAD net_dm_packet_report_fill() and net_dm_hw_packet_report_fill() open code the NET_DM_ATTR_PAYLOAD attribute to avoid zeroing the packet payload before overwriting it with skb_copy_bits(). skb_put() reserves nla_total_size(payload_len), i.e. the header plus the NLA_ALIGN() padding, but only payload_len bytes are copied in. When payload_len is not a multiple of 4 the 1-3 padding bytes are never initialized and are leaked to user space inside the netlink message. KMSAN confirms the leak for the software path when the packet payload length is not 4-byte aligned: BUG: KMSAN: kernel-infoleak in _copy_to_iter _copy_to_iter __skb_datagram_iter skb_copy_datagram_iter netlink_recvmsg sock_recvmsg __sys_recvfrom Uninit was created at: kmem_cache_alloc_node_noprof __alloc_skb net_dm_packet_work Bytes 173-175 of 176 are uninitialized Use __nla_reserve(), which sets up the attribute header and zeroes the padding, instead of open coding the attribute construction. Fixes: ca30707dee2b ("drop_monitor: Add packet alert mode") Fixes: 5e58109b1ea4 ("drop_monitor: Add support for packet alert mode for hardware drops") Suggested-by: Eric Dumazet Signed-off-by: Yehyeong Lee Link: https://patch.msgid.link/20260722122817.5548-1-yhlee@isslab.korea.ac.kr Signed-off-by: Jakub Kicinski --- net/core/drop_monitor.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 2bf3cab5e557..b4d1ff2829b6 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -671,9 +671,7 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb, if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol))) goto nla_put_failure; - attr = skb_put(msg, nla_total_size(payload_len)); - attr->nla_type = NET_DM_ATTR_PAYLOAD; - attr->nla_len = nla_attr_size(payload_len); + attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len); if (skb_copy_bits(skb, 0, nla_data(attr), payload_len)) goto nla_put_failure; @@ -831,9 +829,7 @@ static int net_dm_hw_packet_report_fill(struct sk_buff *msg, if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol))) goto nla_put_failure; - attr = skb_put(msg, nla_total_size(payload_len)); - attr->nla_type = NET_DM_ATTR_PAYLOAD; - attr->nla_len = nla_attr_size(payload_len); + attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len); if (skb_copy_bits(skb, 0, nla_data(attr), payload_len)) goto nla_put_failure; -- cgit v1.2.3 From 7089f7ab99c89f443c92d8fcc585e63f2727f0b3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 Jul 2026 14:17:42 +0000 Subject: drop_monitor: fix size calculations for 64-bit attributes net_dm_packet_report_fill() and net_dm_hw_packet_report_fill() use nla_put_u64_64bit() to append 64-bit attributes (NET_DM_ATTR_PC and NET_DM_ATTR_TIMESTAMP). On 32-bit architectures without CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS, nla_put_u64_64bit() may append a 4-byte NET_DM_ATTR_PAD attribute for 64-bit alignment. However, net_dm_packet_report_size() and net_dm_hw_packet_report_size() used nla_total_size(sizeof(u64)) instead of nla_total_size_64bit(sizeof(u64)), budgeting 12 bytes instead of up to 16 bytes. This under-estimation of SKB size can lead to an skb_over_panic() when __nla_reserve() or skb_put() is subsequently called. Fix this by using nla_total_size_64bit(sizeof(u64)) in both size calculations. Fixes: ca30707dee2b ("drop_monitor: Add packet alert mode") Fixes: 5e58109b1ea4 ("drop_monitor: Add support for packet alert mode for hardware drops") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260722141743.3266924-2-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/core/drop_monitor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index b4d1ff2829b6..9abcdee8c76e 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -566,13 +566,13 @@ static size_t net_dm_packet_report_size(size_t payload_len) /* NET_DM_ATTR_ORIGIN */ nla_total_size(sizeof(u16)) + /* NET_DM_ATTR_PC */ - nla_total_size(sizeof(u64)) + + nla_total_size_64bit(sizeof(u64)) + /* NET_DM_ATTR_SYMBOL */ nla_total_size(NET_DM_MAX_SYMBOL_LEN + 1) + /* NET_DM_ATTR_IN_PORT */ net_dm_in_port_size() + /* NET_DM_ATTR_TIMESTAMP */ - nla_total_size(sizeof(u64)) + + nla_total_size_64bit(sizeof(u64)) + /* NET_DM_ATTR_ORIG_LEN */ nla_total_size(sizeof(u32)) + /* NET_DM_ATTR_PROTO */ @@ -766,7 +766,7 @@ net_dm_hw_packet_report_size(size_t payload_len, /* NET_DM_ATTR_FLOW_ACTION_COOKIE */ net_dm_flow_action_cookie_size(hw_metadata) + /* NET_DM_ATTR_TIMESTAMP */ - nla_total_size(sizeof(u64)) + + nla_total_size_64bit(sizeof(u64)) + /* NET_DM_ATTR_ORIG_LEN */ nla_total_size(sizeof(u32)) + /* NET_DM_ATTR_PROTO */ -- cgit v1.2.3 From fd098a23bf8fda7eae48db9b06e7c34fc4d228fa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 Jul 2026 14:17:43 +0000 Subject: drop_monitor: perform u64_stats updates under IRQ-disabled section In net_dm_packet_trace_kfree_skb_hit() and net_dm_hw_trap_packet_probe(), u64_stats_update_begin() / u64_stats_inc() / u64_stats_update_end() were called after spin_unlock_irqrestore(&...drop_queue.lock, flags), when local IRQs had already been re-enabled. Tracepoint probes can execute in IRQ or softirq context. On 32-bit architectures, u64_stats_update_begin() disables preemption but not interrupts, relying on seqcount writes. If a nested interrupt occurs on the same CPU during the 64-bit stats update, the reentrant seqcount update can corrupt the seqcount state or stats value. Fix this by performing the 64-bit per-CPU stats update before releasing drop_queue.lock via spin_unlock_irqrestore(), ensuring local interrupts remain disabled during the u64_stats update. Fixes: e9feb58020f9 ("drop_monitor: Expose tail drop counter") Fixes: 5e58109b1ea4 ("drop_monitor: Add support for packet alert mode for hardware drops") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260722141743.3266924-3-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/core/drop_monitor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 9abcdee8c76e..abaf108ac4db 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -530,10 +530,10 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore, return; unlock_free: - spin_unlock_irqrestore(&data->drop_queue.lock, flags); u64_stats_update_begin(&data->stats.syncp); u64_stats_inc(&data->stats.dropped); u64_stats_update_end(&data->stats.syncp); + spin_unlock_irqrestore(&data->drop_queue.lock, flags); consume_skb(nskb); } @@ -997,10 +997,10 @@ net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink, return; unlock_free: - spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags); u64_stats_update_begin(&hw_data->stats.syncp); u64_stats_inc(&hw_data->stats.dropped); u64_stats_update_end(&hw_data->stats.syncp); + spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags); net_dm_hw_metadata_free(n_hw_metadata); free: consume_skb(nskb); -- cgit v1.2.3 From 90ef2f2961c2dc55957dafe2f53b3efdb4675efc Mon Sep 17 00:00:00 2001 From: Vijaya Krishna Nivarthi Date: Wed, 22 Jul 2026 14:53:58 +0530 Subject: spi: qcom-qspi: Correct max DMA length to avoid 64K boundary failure The maximum size for a DMA data descriptor is 64KB-1 because the size field in HW is 16 bits wide. For this reason, transfers fail at 64KB and beyond. Lower max_dma_len to 60KB so larger transfers are split into multiple DMA blocks and do not hit the failing 64KB boundary. 60KB is chosen as a safe round number below the 64KB-1 hardware limit while satisfying alignment requirements. Tested on x1e80100 (Hamoa) with SPI-NOR flash (/dev/mtd0): Without patch: dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=32768 count=2 # works dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # fails With patch: dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # works Fixes: b5762d95607e ("spi: spi-qcom-qspi: Add DMA mode support") Cc: stable@vger.kernel.org Signed-off-by: Vijaya Krishna Nivarthi Link: https://patch.msgid.link/20260722092358.459943-1-vnivarth@qti.qualcomm.com Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index caf55a6f70b3..e9fdefa9d65d 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -775,7 +775,8 @@ static int qcom_qspi_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "could not set DMA mask\n"); host->max_speed_hz = 300000000; - host->max_dma_len = 65536; /* as per HPG */ + /* as per HPG, it is 64KB, limit to 60KB to avoid boundary condition failures */ + host->max_dma_len = 0xf000; host->dma_alignment = QSPI_ALIGN_REQ; host->num_chipselect = QSPI_NUM_CS; host->bus_num = -1; -- cgit v1.2.3 From 98917a499ec7064c14fc56d180a4fd636fc2784c Mon Sep 17 00:00:00 2001 From: Raphael Zimmer Date: Wed, 27 May 2026 16:06:17 +0200 Subject: libceph: Fix multiplication overflow in decode_new_up_state_weight() If a message of type CEPH_MSG_OSD_MAP contains a (maliciously) corrupted osdmap, out-of-bounds memory accesses may occur in decode_new_up_state_weight(). This happens because the bounds check for the new_state part is based on calculating its length depending on a len value read from the incoming message. This calculation may overflow leading to an incorrect bounds check. Subsequently, out-of-bounds reads may occur when decoding this part. This patch switches the multiplication to use check_mul_overflow() to abort processing the osdmap if an overflow occurred. Therefore, osdmaps/messages containing large values for len that result in a multiplication overflow are treated as invalid. [ idryomov: rename new_state_len -> new_state_item_size, formatting ] Cc: stable@vger.kernel.org Fixes: 930c53286977 ("libceph: apply new_state before new_up_client on incrementals") Signed-off-by: Raphael Zimmer Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- net/ceph/osdmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 8b5b0587a0cf..8e77096718c4 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1842,6 +1842,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, void *new_up_client; void *new_state; void *new_weight_end; + const u32 new_state_item_size = + sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8)); u32 len; int ret; int i; @@ -1862,7 +1864,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v, new_state = *p; ceph_decode_32_safe(p, end, len, e_inval); - len *= sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8)); + if (check_mul_overflow(len, new_state_item_size, &len)) + goto e_inval; ceph_decode_need(p, end, len, e_inval); *p += len; -- cgit v1.2.3 From 05f90284223381005d6bcddab3fda4a97f9c3401 Mon Sep 17 00:00:00 2001 From: Douya Le Date: Fri, 29 May 2026 16:11:44 +0800 Subject: libceph: reject zero bucket types in crush_decode CRUSH bucket type 0 is reserved for devices. The mapper relies on that invariant and uses type 0 to identify leaf devices. If crush_decode() accepts a bucket with type 0, a malformed CRUSH map can make the mapper treat a negative bucket ID as a device and pass it to is_out(), which then indexes the OSD weight array with a negative value. Reject zero bucket types while decoding the CRUSH map so the invalid state never reaches the mapper. Cc: stable@vger.kernel.org Fixes: f24e9980eb86 ("ceph: OSD client") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Douya Le Signed-off-by: Ren Wei Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- net/ceph/osdmap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 8e77096718c4..3c87f4b24e51 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -518,6 +518,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end) ceph_decode_need(p, end, 4*sizeof(u32), bad); b->id = ceph_decode_32(p); b->type = ceph_decode_16(p); + if (b->type == 0) + goto bad; b->alg = ceph_decode_8(p); if (b->alg != alg) { b->alg = 0; -- cgit v1.2.3 From 40480eee361ed9676b3f844d532ac28b47251634 Mon Sep 17 00:00:00 2001 From: Raphael Zimmer Date: Fri, 29 May 2026 09:42:57 +0200 Subject: libceph: Reject monmaps advertising zero monitors A message of type CEPH_MSG_MON_MAP contains a monmap that is sent from a monitor to the client. This monmap contains information about the existing monitors in the cluster. Currently, a monmap indicating that there are zero monitors in the cluster is treated as valid. However, it is impossible to have zero monitors in the cluster and still receive a valid monmap from a monitor. Therefore, such a monmap must be corrupted and should be treated as invalid. Furthermore, a monmap with a monitor count of zero can subsequently crash the client when attempting to open a session with a monitor in __open_session(). This happens because the "BUG_ON(monc->monmap->num_mon < 1)" assertion in pick_new_mon() is triggered. This patch extends a check in ceph_monmap_decode() to also reject arriving mon_maps with num_mon == 0 rather than only with num_mon > CEPH_MAX_MON. [ idryomov: drop "log output for unusual values of num_mon" part ] Cc: stable@vger.kernel.org Signed-off-by: Raphael Zimmer Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- net/ceph/mon_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index d2cdc8ee3155..24acdd580e79 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -114,7 +114,7 @@ static struct ceph_monmap *ceph_monmap_decode(void **p, void *end, bool msgr2) dout("%s fsid %pU epoch %u num_mon %u\n", __func__, &fsid, epoch, num_mon); - if (num_mon > CEPH_MAX_MON) + if (num_mon == 0 || num_mon > CEPH_MAX_MON) goto e_inval; monmap = kmalloc_flex(*monmap, mon_inst, num_mon, GFP_NOIO); -- cgit v1.2.3 From 4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 29 May 2026 00:37:24 +0000 Subject: ceph: fix pre-auth out-of-bounds read on snaptrace in ceph_handle_caps() ceph_handle_caps() reads snap_trace_len from the wire-format ceph_mds_caps header and uses it unconditionally to build a fake end pointer (snaptrace + snaptrace_len) that is later handed to ceph_update_snap_trace() in the CEPH_CAP_OP_IMPORT case: snaptrace = h + 1; snaptrace_len = le32_to_cpu(h->snap_trace_len); p = snaptrace + snaptrace_len; ... case CEPH_CAP_OP_IMPORT: if (snaptrace_len) { ... if (ceph_update_snap_trace(mdsc, snaptrace, snaptrace + snaptrace_len, false, &realm)) { ... } ceph_update_snap_trace() then decodes a struct ceph_mds_snap_realm from snaptrace using ceph_decode_need(&p, e, sizeof(*ri), bad) with the attacker-supplied fake end e == snaptrace + snaptrace_len. With snaptrace_len == 0xFFFFFFFF the bound check is trivially satisfied, ri = p reads sizeof(struct ceph_mds_snap_realm) past the legitimate msg->front buffer, and ri->num_snaps / ri->num_prior_parent_snaps then drive further out-of-bounds reads of the encoded snap arrays. The eleven msg_version >= 2 .. msg_version >= 12 decoder blocks above the op switch each catch this OOB through their ceph_decode_*_safe() / ceph_decode_need() helpers, but they sit behind a hdr.version-gated if, so a malicious or compromised MDS that sets msg->hdr.version = 1 reaches the IMPORT path with no version-gated decoder having validated snap_trace_len. The shape has been present since ceph_handle_caps() was introduced. Validate snap_trace_len against the message front buffer before consuming it, using the canonical ceph_decode_need() / ceph_has_room() helper. The helper bounds the length with subtraction (n <= end - p, guarded by end >= p) rather than pointer addition, so it is wrap-safe for the attacker-controlled u32 length on 32-bit builds where p + snap_trace_len could overflow the address space. This matches the rest of the ceph decode path (e.g. the pool_ns_len check a few lines below), and the existing goto bad cleanup already covers this exit path. Cc: stable@vger.kernel.org Fixes: a8599bd821d0 ("ceph: capability management") Signed-off-by: Bryam Vargas Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/caps.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 4b37d9ffdf7f..77b23fe51425 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -4375,6 +4375,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, snaptrace = h + 1; snaptrace_len = le32_to_cpu(h->snap_trace_len); + ceph_decode_need(&snaptrace, end, snaptrace_len, bad); p = snaptrace + snaptrace_len; if (msg_version >= 2) { -- cgit v1.2.3 From a109a556115271ca7896dcda7b4b7e45e156c227 Mon Sep 17 00:00:00 2001 From: Pavitra Jha Date: Tue, 2 Jun 2026 00:17:35 -0400 Subject: libceph: fix two unsafe bare decodes in decode_lockers() decode_lockers() in cls_lock_client.c contains two bare decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads: 1. ceph_decode_32(p) at the num_lockers field has no preceding bounds check. ceph_start_decoding() accepts struct_len=0 as valid -- the internal ceph_decode_need(p, end, 0, bad) always passes -- so when an OSD sends struct_len=0, ceph_start_decoding() returns success with p == end. The immediately following bare ceph_decode_32(p) then reads 4 bytes past the validated buffer boundary. The garbage value is passed directly to kzalloc_objs() as the locker count. The sibling function decode_watchers() in osd_client.c already uses ceph_decode_32_safe() after its own ceph_start_decoding() call. decode_lockers() was the only site using the bare variant. 2. ceph_decode_8(p) after the decode_locker() loop has no preceding bounds check. If an OSD crafts num_lockers such that the loop advances p exactly to end, the subsequent bare ceph_decode_8(p) reads one byte past the validated buffer boundary. The result is passed directly into *type, which is used as a lock type discriminator by callers, giving an OSD-controlled one-byte OOB read with direct influence over the lock type field. Fix both by replacing bare operations with their safe variants: ceph_decode_32(p) -> ceph_decode_32_safe(p, end, *num_lockers, err_inval) ceph_decode_8(p) -> ceph_decode_8_safe(p, end, *type, err_free_lockers) The goto targets differ intentionally: err_inval: is a new label returning -EINVAL directly. It is used for the pre-allocation failure path where *lockers is not yet allocated and must not be passed to ceph_free_lockers(). err_free_lockers: is the existing label. It is used for the post-allocation failure path where *lockers is allocated and must be freed. ret is set to -EINVAL before ceph_decode_8_safe() so that err_free_lockers returns the correct error code on bounds violation. Without this, err_free_lockers would return a stale ret value (0 from the successful decode_locker() loop), silently swallowing the error. -EINVAL is correct for both failure paths. The data received from the OSD is structurally malformed. -ENOMEM would misrepresent the failure class to callers and to stable@ backporters triaging error paths. Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment can trigger this against any kernel client that issues the lock.get_info class method (e.g. during RBD exclusive lock acquisition). [ idryomov: trim changelog, formatting ] Cc: stable@vger.kernel.org Fixes: d4ed4a530562 ("libceph: support for lock.lock_info") Signed-off-by: Pavitra Jha Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- net/ceph/cls_lock_client.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/ceph/cls_lock_client.c b/net/ceph/cls_lock_client.c index c6956f1df333..377336982f7d 100644 --- a/net/ceph/cls_lock_client.c +++ b/net/ceph/cls_lock_client.c @@ -299,7 +299,7 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, if (ret) return ret; - *num_lockers = ceph_decode_32(p); + ceph_decode_32_safe(p, end, *num_lockers, err_inval); *lockers = kzalloc_objs(**lockers, *num_lockers, GFP_NOIO); if (!*lockers) return -ENOMEM; @@ -310,7 +310,8 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, goto err_free_lockers; } - *type = ceph_decode_8(p); + ret = -EINVAL; + ceph_decode_8_safe(p, end, *type, err_free_lockers); s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO); if (IS_ERR(s)) { ret = PTR_ERR(s); @@ -320,6 +321,9 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, *tag = s; return 0; +err_inval: + return -EINVAL; + err_free_lockers: ceph_free_lockers(*lockers, *num_lockers); return ret; -- cgit v1.2.3 From cbf59617cd715219e84c50d106a3d0e1e8ba054e Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 4 Jun 2026 02:19:51 +0000 Subject: ceph: fix writeback_count leak in write_folio_nounlock() write_folio_nounlock() increments fsc->writeback_count to track in-flight writeback operations. On several error paths where the function returns early (folio lookup failure, snapshot context allocation failure, and writepages submission failure), the function returns without calling atomic_long_dec_return() to decrement the counter. Each leaked increment keeps the counter above zero, which can prevent the filesystem from cleanly unmounting or suspending writes. Add atomic_long_dec_return() calls on all error paths that currently return without decrementing the counter. Cc: stable@vger.kernel.org Fixes: d55207717ded ("ceph: add encryption support to writepage and writepages") Signed-off-by: Wentao Liang Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/addr.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 61bd6d92ff25..ecf33b66610c 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -790,6 +790,9 @@ static int write_folio_nounlock(struct folio *folio, ceph_wbc.truncate_size, true); if (IS_ERR(req)) { folio_redirty_for_writepage(wbc, folio); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return PTR_ERR(req); } @@ -809,6 +812,9 @@ static int write_folio_nounlock(struct folio *folio, folio_redirty_for_writepage(wbc, folio); folio_end_writeback(folio); ceph_osdc_put_request(req); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return PTR_ERR(bounce_page); } } @@ -847,6 +853,9 @@ static int write_folio_nounlock(struct folio *folio, ceph_vinop(inode), folio); folio_redirty_for_writepage(wbc, folio); folio_end_writeback(folio); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return err; } if (err == -EBLOCKLISTED) -- cgit v1.2.3 From d3c32939fa0e3ee9b883b9a0fd1972c5c444e3d0 Mon Sep 17 00:00:00 2001 From: Douya Le Date: Sun, 7 Jun 2026 17:35:49 +0800 Subject: libceph: bound get_version reply decode to front len handle_get_version_reply() uses msg->front_alloc_len as the decode boundary for MON_GET_VERSION_REPLY. That is the size of the reused reply buffer, not the number of bytes actually received. A truncated reply can therefore pass ceph_decode_need() and decode the second u64 from stale tail bytes left in the buffer by an earlier message, causing an uninitialized memory read. Use msg->front.iov_len as the receive-side decode boundary, matching other libceph reply handlers and limiting decoding to the bytes that were actually read from the wire. Cc: stable@vger.kernel.org Fixes: 513a8243d67f ("libceph: mon_get_version request infrastructure") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Douya Le Signed-off-by: Ren Wei Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- net/ceph/mon_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 24acdd580e79..c56457378d00 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -821,7 +821,7 @@ static void handle_get_version_reply(struct ceph_mon_client *monc, struct ceph_mon_generic_request *req; u64 tid = le64_to_cpu(msg->hdr.tid); void *p = msg->front.iov_base; - void *end = p + msg->front_alloc_len; + void *const end = p + msg->front.iov_len; u64 handle; dout("%s msg %p tid %llu\n", __func__, msg, tid); -- cgit v1.2.3 From e4c804726c4afce3ba648b982d564f6af2cfa328 Mon Sep 17 00:00:00 2001 From: Douya Le Date: Mon, 15 Jun 2026 14:31:06 +0800 Subject: libceph: remove debugfs files before client teardown ceph_destroy_client() tears down the monitor client before removing the per-client debugfs files. A concurrent read of the monmap debugfs file can enter monmap_show() after ceph_monc_stop() has freed monc->monmap, triggering a use-after-free. Remove the debugfs files before stopping the OSD and monitor clients. debugfs_remove() drains active handlers and prevents new accesses, so the debugfs callbacks can no longer race the rest of client teardown. Cc: stable@vger.kernel.org Fixes: 76aa844d5b2f ("ceph: debugfs") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Douya Le Signed-off-by: Ren Wei Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- net/ceph/ceph_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 952121849180..a797c7360e3c 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -762,13 +762,13 @@ void ceph_destroy_client(struct ceph_client *client) atomic_set(&client->msgr.stopping, 1); + ceph_debugfs_client_cleanup(client); + /* unmount */ ceph_osdc_stop(&client->osdc); ceph_monc_stop(&client->monc); ceph_messenger_fini(&client->msgr); - ceph_debugfs_client_cleanup(client); - ceph_destroy_options(client->options); kfree(client); -- cgit v1.2.3 From bbeae12fda3384a90fbebc8a19ba9d33f85b5361 Mon Sep 17 00:00:00 2001 From: Zhao Zhang Date: Fri, 19 Jun 2026 15:40:03 +0800 Subject: libceph: guard missing CRUSH type name lookup Localized read selection can walk a parent bucket whose name exists in the CRUSH map while its type has no matching entry in type_names. get_immediate_parent() then dereferences a NULL type_cn and passes an invalid pointer into strcmp(), causing a null-ptr-deref. Skip such malformed parent buckets unless both the bucket name and type name metadata are present. This keeps malformed hierarchy data from crashing locality lookup and safely falls back to "not local". [ idryomov: add WARN_ON_ONCE ] Cc: stable@vger.kernel.org Fixes: 117d96a04f00 ("libceph: support for balanced and localized reads") Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Zhao Zhang Signed-off-by: Ren Wei Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- net/ceph/osdmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 3c87f4b24e51..04036c047b8c 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -3060,8 +3060,11 @@ static int get_immediate_parent(struct crush_map *c, int id, if (b->items[j] != id) continue; - *parent_type_id = b->type; type_cn = lookup_crush_name(&c->type_names, b->type); + if (WARN_ON_ONCE(!type_cn)) + continue; + + *parent_type_id = b->type; parent_loc->cl_type_name = type_cn->cn_name; parent_loc->cl_name = cn->cn_name; return b->id; -- cgit v1.2.3 From c3e64079d8b9663e3998d0caac9aba915b6b93ae Mon Sep 17 00:00:00 2001 From: WenTao Liang Date: Thu, 11 Jun 2026 22:40:07 +0800 Subject: ceph: fix refcount leak in ceph_readdir() The ceph_readdir() function allocates a ceph_mds_request via ceph_mdsc_create_request() and stores it in dfi->last_readdir. In the directory entry processing loop, if the entry's offset is less than ctx->pos or if the inode pointer is unexpectedly NULL, the function returns -EIO without releasing the reference held by dfi->last_readdir, causing a refcount leak. Fix this by adding ceph_mdsc_put_request(dfi->last_readdir) before returning on these error paths. Also set dfi->last_readdir to NULL for safety, matching the cleanup done at the normal exit. Cc: stable@vger.kernel.org Fixes: af9ffa6df7e3 ("ceph: add support to readdir for encrypted names") Signed-off-by: WenTao Liang Reviewed-by: Viacheslav Dubeyko Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov --- fs/ceph/dir.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 27ce9e55e947..ef9e92e362d3 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -546,11 +546,16 @@ more: pr_warn_client(cl, "%p %llx.%llx rde->offset 0x%llx ctx->pos 0x%llx\n", inode, ceph_vinop(inode), rde->offset, ctx->pos); + ceph_mdsc_put_request(dfi->last_readdir); + dfi->last_readdir = NULL; return -EIO; } - if (WARN_ON_ONCE(!rde->inode.in)) + if (WARN_ON_ONCE(!rde->inode.in)) { + ceph_mdsc_put_request(dfi->last_readdir); + dfi->last_readdir = NULL; return -EIO; + } ctx->pos = rde->offset; doutc(cl, "%p %llx.%llx (%d/%d) -> %llx '%.*s' %p\n", inode, -- cgit v1.2.3 From 937d61f86d377a3aa578adae7a3dfcecdddf9d89 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Mon, 29 Jun 2026 13:14:22 -0400 Subject: libceph: refresh auth->authorizer_buf{,_len} after authorizer update ceph_x_create_authorizer() caches au->buf->vec.iov_base and au->buf->vec.iov_len in struct ceph_auth_handshake. These cached values are then used by the messenger connect code when sending the authorizer. ceph_x_update_authorizer() can rebuild the authorizer when a newer service ticket is available. If the rebuilt authorizer no longer fits in the existing buffer, ceph_x_build_authorizer() drops its reference to au->buf and allocates a new one. If this is the final reference, ceph_buffer_put() frees the old ceph_buffer and its vec.iov_base, but auth->authorizer_buf still points at that freed memory. A subsequent msgr1 reconnect can therefore queue the stale pointer and trigger a KASAN slab-use-after-free in _copy_from_iter() while tcp_sendmsg() copies the authorizer. Refresh auth->authorizer_buf and auth->authorizer_buf_len after a successful authorizer rebuild so the messenger sends the current buffer. Cc: stable@vger.kernel.org Fixes: 0bed9b5c523d ("libceph: add update_authorizer auth method") Closes: https://lore.kernel.org/all/E378850E-106C-427B-A241-970EB2D054D7@gmail.com/ Signed-off-by: Shuangpeng Bai Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov --- net/ceph/auth_x.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c index 9e64e82d0b63..50a79e8aa656 100644 --- a/net/ceph/auth_x.c +++ b/net/ceph/auth_x.c @@ -849,9 +849,16 @@ static int ceph_x_update_authorizer( au = (struct ceph_x_authorizer *)auth->authorizer; if (au->secret_id < th->secret_id) { + int ret; + dout("ceph_x_update_authorizer service %u secret %llu < %llu\n", au->service, au->secret_id, th->secret_id); - return ceph_x_build_authorizer(ac, th, au); + ret = ceph_x_build_authorizer(ac, th, au); + if (ret) + return ret; + + auth->authorizer_buf = au->buf->vec.iov_base; + auth->authorizer_buf_len = au->buf->vec.iov_len; } return 0; } -- cgit v1.2.3 From 9f00f9cf2be293efe899db67dc5272e3a9c62717 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Mon, 8 Jun 2026 21:40:09 -0700 Subject: libceph: bound pg_{temp,upmap,upmap_items} length to CEPH_PG_MAX_SIZE __decode_pg_temp() decodes an user-controlled length but only rejects values large enough to overflow the allocation; it does not bound it to CEPH_PG_MAX_SIZE. The helper backs both pg_temp and pg_upmap decoding, and apply_upmap()/get_temp_osds() later copy the decoded list into the fixed-size on-stack array struct ceph_osds.osds[CEPH_PG_MAX_SIZE]. A monitor that sends an OSDMap with a pg_temp/pg_upmap entry longer than 32 thus causes a stack out-of-bounds write. An OSD set for a single PG can never exceed CEPH_PG_MAX_SIZE, so reject longer entries at decode time. The bound is well below the old overflow threshold, so it also covers the allocation-size overflow the previous check guarded against. BUG: KASAN: stack-out-of-bounds in ceph_pg_to_up_acting_osds Write of size 4 ... by task exploit kasan_report (mm/kasan/report.c:595) ceph_pg_to_up_acting_osds (net/ceph/osdmap.c:2617 net/ceph/osdmap.c:2833) calc_target (net/ceph/osd_client.c:1638) __submit_request (net/ceph/osd_client.c:2394) ceph_osdc_start_request (net/ceph/osd_client.c:2490) ceph_osdc_call (net/ceph/osd_client.c:5164) rbd_dev_image_probe (drivers/block/rbd.c:6899) do_rbd_add (drivers/block/rbd.c:7138) ... kernel BUG at net/ceph/osdmap.c:2670! [ idryomov: do the same in __decode_pg_upmap_items() ] Cc: stable@vger.kernel.org Fixes: a303bb0e5834 ("libceph: introduce and switch to decode_pg_mapping()") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov --- net/ceph/osdmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 04036c047b8c..a4b0dd8672ec 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1438,7 +1438,7 @@ static struct ceph_pg_mapping *__decode_pg_temp(void **p, void *end, ceph_decode_32_safe(p, end, len, e_inval); if (len == 0 && incremental) return NULL; /* new_pg_temp: [] to remove */ - if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32)) + if (len > CEPH_PG_MAX_SIZE) return ERR_PTR(-EINVAL); ceph_decode_need(p, end, len * sizeof(u32), e_inval); @@ -1619,7 +1619,7 @@ static struct ceph_pg_mapping *__decode_pg_upmap_items(void **p, void *end, u32 len, i; ceph_decode_32_safe(p, end, len, e_inval); - if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32))) + if (len > CEPH_PG_MAX_SIZE) return ERR_PTR(-EINVAL); ceph_decode_need(p, end, 2 * len * sizeof(u32), e_inval); -- cgit v1.2.3 From a6c4250b81bd30beae94e1b7a4b26fa1193ad2e4 Mon Sep 17 00:00:00 2001 From: Raphael Zimmer Date: Thu, 9 Jul 2026 13:26:20 +0200 Subject: rbd: Reset positive result codes to zero in object map update path In a reply message to an RBD request, a positive result code indicates a data payload, which is not allowed for writes. While rbd_osd_req_callback() already resets a positive result code for writes to zero, rbd_object_map_callback() does not. This allows a corrupted reply to an object map update to trigger the rbd_assert(*result < 0) in __rbd_obj_handle_request(). This happens, because rbd_object_map_callback() calls rbd_obj_handle_request() -> __rbd_obj_handle_request() and passes this positive result code. From __rbd_obj_handle_request(), rbd_obj_advance_write() is called, which leaves the positive result code unchanged and returns true. Therefore, the if(done && *result) branch is executed in __rbd_obj_handle_request() and the assertion triggers. This patch fixes the issue by adjusting the logic in the rbd_object_map_callback() path. A positive result code for an object map update is now reset to zero (similar to rbd_osd_req_callback()), and the message is subsequently handled the same way as if the result code was zero from the beginning. Additionally, a WARN_ON_ONCE() is added for this case. Cc: stable@vger.kernel.org Fixes: 22e8bd51bb04 ("rbd: support for object-map and fast-diff") Signed-off-by: Raphael Zimmer Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- drivers/block/rbd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index ac90d81aa294..1f1c2810f6ee 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1950,9 +1950,14 @@ static int rbd_object_map_update_finish(struct rbd_obj_request *obj_req, bool has_current_state; void *p; - if (osd_req->r_result) + if (osd_req->r_result < 0) return osd_req->r_result; + /* + * Writes aren't allowed to return a data payload. + */ + WARN_ON_ONCE(osd_req->r_result > 0); + /* * Nothing to do for a snapshot object map. */ -- cgit v1.2.3 From 50958bb928bad3bdba9e5d1b7ff4bbadcf6951e6 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 6 Jul 2026 17:06:59 +0200 Subject: ceph: fix hanging __ceph_get_caps() with stale mds_wanted A reader can hang forever in __ceph_get_caps() when the client no longer holds `FILE_RD`, but local cap state still says that the capability is already wanted (via `mds_wanted`). One way to trigger this is through MDS cap revocation. If another client performs a conflicting operation, the MDS can revoke `FILE_RD` from the reader; the next read then has to reacquire `FILE_RD`. If the cap update that should request `FILE_RD` never reaches the MDS after `cap->mds_wanted` was raised, the reader is left holding only non-file caps while local `mds_wanted` still includes the file read caps. In that state, try_get_cap_refs() sees `need <= mds_wanted` and returns 0, so __ceph_get_caps() just waits on `i_cap_wq`. If the cap update that was supposed to request `FILE_RD never reaches the MDS after `cap->mds_wanted was` raised, no further request is sent and the waiter can sleep indefinitely until unrelated cap traffic happens to wake it up. The ordering issue is that `cap->mds_wanted` is updated in __prep_cap() before the `CEPH_MSG_CLIENT_CAPS message` is actually queued for send. That makes one field serve two different meanings at once: what this client wants, and what the client believes the MDS already knows it wants. A proper fix would be to split those states and track whether a cap update is actually in flight or has been observed by the MDS. However, simply moving the `cap->mds_wanted assignment` later would not be sufficient: queueing the message in the messenger does not guarantee that the MDS processed that specific wanted set, and reconnect or message loss can still invalidate that assumption. Fixing that properly would require a larger rework of the cap state machine. To allow simpler backports to stable kernels, this patch implements a simpler workaround: - stop waiting forever in __ceph_get_caps(); after a bounded wait, fall back to the renew path - make ceph_renew_caps() issue a synchronous `OPEN` request whenever the inode still does not actually hold the wanted caps, instead of only calling ceph_check_caps() The extra issued-vs-wanted check in ceph_renew_caps() is necessary because the previous test only checked whether the inode still had any real caps at all. That is not enough after revocation: the client can still hold something like `pLs` and yet be missing `FILE_RD` completely. In that case, falling back to ceph_check_caps() is not sufficient, because it still trusts `cap->mds_wanted` and may resend nothing. By requiring `(issued & wanted) == wanted` before taking the asynchronous path, the code only uses ceph_check_caps() when the `wanted caps` are already actually issued. Otherwise, it sends the synchronous `OPEN` renew. This preserves the existing asynchronous fast path when the wanted caps are already issued, avoids changing cap-state semantics, and fixes the hang by guaranteeing that a stalled waiter eventually retries through a path that does not rely on the stale `mds_wanted` state. [ idryomov: move CEPH_GET_CAPS_WAIT_TIMEOUT from libceph.h to mds_client.h, formatting ] Cc: stable@vger.kernel.org Fixes: 0a454bdd501a ("ceph: reorganize __send_cap for less spinlock abuse") Signed-off-by: Max Kellermann Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov --- fs/ceph/caps.c | 17 +++++++++++++++-- fs/ceph/file.c | 9 +++++---- fs/ceph/mds_client.h | 1 + 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 77b23fe51425..d7283fb54cec 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -3094,7 +3094,19 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need, ret = -ERESTARTSYS; break; } - wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); + + /* + * If a cap update is lost after + * mds_wanted was raised, waiting + * forever will never make progress. + * Retry the renew path periodically + * so we can resend synchronously. + */ + if (!wait_woken(&wait, TASK_INTERRUPTIBLE, + CEPH_GET_CAPS_WAIT_TIMEOUT)) { + ret = -EUCLEAN; + break; + } } remove_wait_queue(&ci->i_cap_wq, &wait); @@ -3128,7 +3140,8 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need, continue; } if (ret == -EUCLEAN) { - /* session was killed, try renew caps */ + /* session was killed or a waited cap + * request needs a retry */ ret = ceph_renew_caps(inode, flags); if (ret == 0) continue; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 71161f2b2151..a4a2a4b6a027 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -314,7 +314,7 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode) } /* - * try renew caps after session gets killed. + * Retry cap acquisition after a stale session or a lost cap update. */ int ceph_renew_caps(struct inode *inode, int fmode) { @@ -322,14 +322,15 @@ int ceph_renew_caps(struct inode *inode, int fmode) struct ceph_client *cl = mdsc->fsc->client; struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_mds_request *req; - int err, flags, wanted; + int err, flags, wanted, issued; spin_lock(&ci->i_ceph_lock); __ceph_touch_fmode(ci, mdsc, fmode); wanted = __ceph_caps_file_wanted(ci); + issued = __ceph_caps_issued(ci, NULL); if (__ceph_is_any_real_caps(ci) && - (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap)) { - int issued = __ceph_caps_issued(ci, NULL); + (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap) && + (issued & wanted) == wanted) { spin_unlock(&ci->i_ceph_lock); doutc(cl, "%p %llx.%llx want %s issued %s updating mds_wanted\n", inode, ceph_vinop(inode), ceph_cap_string(wanted), diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 731d6ad04956..0ece4c9e3529 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -77,6 +77,7 @@ struct ceph_fs_client; struct ceph_cap; #define MDS_AUTH_UID_ANY -1 +#define CEPH_GET_CAPS_WAIT_TIMEOUT (5 * HZ) #define CEPH_CAP_FLUSH_WAIT_TIMEOUT_SEC 60 #define CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES 5 #define CEPH_CAP_FLUSH_MAX_DUMP_ITERS 5 -- cgit v1.2.3 From cee38bbf5556a8e0a232ccae41649580827d7806 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 21 Jul 2026 08:20:46 +0200 Subject: ceph: add owner/capability checks for CEPH_IOC_SET_LAYOUT* These permission checks were already missing in the initial impementation of these ioctls. This Ceph allows any user who owns a file descriptor to manipulate the layout of any file, even if they don't have write permissions. It might be a good idea to guard other ioctls with permission checks as well or even disallow regular users (even if they own the file) to manipulate layout settings completely, as this may be abused to DoS the Ceph servers, but right now, I find it most urgent to have setter checks at all. Cc: stable@vger.kernel.org Fixes: 8f4e91dee2a2 ("ceph: ioctls") Signed-off-by: Max Kellermann Reviewed-by: Xiubo Li Signed-off-by: Ilya Dryomov --- fs/ceph/ioctl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index 15cde055f3da..de07f19b0caa 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -72,6 +72,9 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg) struct ceph_ioctl_layout nl; int err; + if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + return -EACCES; + if (copy_from_user(&l, arg, sizeof(l))) return -EFAULT; @@ -142,6 +145,9 @@ static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg) int err; struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + return -EACCES; + /* copy and validate */ if (copy_from_user(&l, arg, sizeof(l))) return -EFAULT; -- cgit v1.2.3 From 5b602344a49e039e792ce5a8923bcc61412ee134 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 22 Jul 2026 13:49:31 +0200 Subject: ceph: avoid fs reclaim while using current->journal_info handle_reply() stores a `ceph_mds_request` pointer in `current->journal_info` while filling the inode and dentry cache from an MDS reply. An allocation in this section can enter direct reclaim and prune dentries from another filesystem. If this dirties an ext4 inode, ext4 starts a JBD2 transaction. JBD2 interprets the Ceph request in `current->journal_info` as a journal handle and dereferences the request's `r_tid` as `h_transaction`, causing a kernel crash, e.g.: Unable to handle kernel paging request at virtual address 00000000077b4818 [...] Internal error: Oops: 0000000096000004 [#1] SMP Modules linked in: CPU: 6 UID: 0 PID: 2699135 Comm: kworker/6:3 Tainted: G W 6.18.38-i3 #1113 NONE [...] Workqueue: ceph-msgr ceph_con_workfn pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : jbd2__journal_start+0x2c/0x208 lr : __ext4_journal_start_sb+0x100/0x178 [...] Call trace: jbd2__journal_start+0x2c/0x208 (P) __ext4_journal_start_sb+0x100/0x178 ext4_dirty_inode+0x3c/0x90 __mark_inode_dirty+0x58/0x400 iput.part.0+0x2b0/0x370 iput+0x18/0x30 dentry_unlink_inode+0xc0/0x158 __dentry_kill+0x80/0x250 shrink_dentry_list+0x90/0x130 prune_dcache_sb+0x60/0x98 super_cache_scan+0xe8/0x190 do_shrink_slab+0x174/0x388 shrink_slab+0xd8/0x4c0 shrink_node+0x31c/0x908 do_try_to_free_pages+0xd0/0x508 try_to_free_pages+0x11c/0x238 __alloc_frozen_pages_noprof+0x4d0/0xdd0 __folio_alloc_noprof+0x18/0x70 __filemap_get_folio+0x248/0x440 ceph_readdir_prepopulate+0x570/0x9e8 mds_dispatch+0x1424/0x1ba0 ceph_con_process_message+0x74/0xa0 ceph_con_v1_try_read+0x3a0/0x1510 ceph_con_workfn+0x260/0x460 Enter a scoped NOFS allocation context and leave it after clearing `journal_info`. This prevents filesystem reclaim from recursing into another filesystem while the field contains Ceph-private data. Cc: stable@vger.kernel.org Fixes: 315f24088048 ("ceph: fix security xattr deadlock") Signed-off-by: Max Kellermann Reviewed-by: Viacheslav Dubeyko Reviewed-by: Xiubo Li Signed-off-by: Ilya Dryomov --- fs/ceph/mds_client.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 853bf698b356..3c692ad02c85 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -4015,6 +4016,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) struct ceph_mds_reply_head *head = msg->front.iov_base; struct ceph_mds_reply_info_parsed *rinfo; /* parsed reply info */ struct ceph_snap_realm *realm; + unsigned int nofs_flags; u64 tid; int err, result; int mds = session->s_mds; @@ -4158,6 +4160,14 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) /* insert trace into our cache */ mutex_lock(&req->r_fill_mutex); + + /* disable fs reclaim while we are using current->journal_info + * for our own purposes, or else shrinkers of other + * filesystems might dereference this pointer as a different + * type + */ + nofs_flags = memalloc_nofs_save(); + current->journal_info = req; err = ceph_fill_trace(mdsc->fsc->sb, req); if (err == 0) { @@ -4166,6 +4176,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) err = ceph_readdir_prepopulate(req, req->r_session); } current->journal_info = NULL; + memalloc_nofs_restore(nofs_flags); mutex_unlock(&req->r_fill_mutex); up_read(&mdsc->snap_rwsem); -- cgit v1.2.3 From af421e9aed3920c7ac88c24daa48606c7112feca Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 22 Jun 2026 08:47:22 -0400 Subject: um: vector: fix use-after-free in vector_mmsg_rx() When vector_mmsg_rx() discards a packet whose overlay header fails verify_header(), it frees the skb and continues the loop: if (header_check < 0) { dev_kfree_skb_irq(skb); vp->estats.rx_encaps_errors++; continue; } The normal and short-packet paths fall through to the bottom of the loop body, which clears the consumed slot and advances the cursors: (*skbuff_vector) = NULL; mmsg_vector++; skbuff_vector++; The verify_header() < 0 path skips that via continue, so the freed skb is left in skbuff_vector[] and the cursors do not advance. The next iteration reads the same slot, gets the freed skb, and frees it again, producing a refcount underflow / use-after-free in the RX path. Discard the slot the same way the other paths do before continuing. Only transports whose verify_header() can return negative are affected: GRE and L2TPv3 do so on a cookie/session-id mismatch (raw/tap do not), so any peer on such a transport can trigger it without authentication. Fixes: 49da7e64f33e ("High Performance UML Vector Network Driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Richard Weinberger --- arch/um/drivers/vector_kern.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c index 2cc90055499a..8a70b3a62553 100644 --- a/arch/um/drivers/vector_kern.c +++ b/arch/um/drivers/vector_kern.c @@ -995,6 +995,9 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget) */ dev_kfree_skb_irq(skb); vp->estats.rx_encaps_errors++; + (*skbuff_vector) = NULL; + mmsg_vector++; + skbuff_vector++; continue; } if (header_check > 0) { -- cgit v1.2.3 From 6a50332e194f58b562d396ab772c34e8c9890c0f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:34:57 +0100 Subject: ASoC: SDCA: Correct pointer passed to devm_acpi_table_put devm_acpi_table_put() takes a struct acpi_table_header * but the value passed in is struct acpi_table_header ** so the value passed to acpi_put_table() is actually the pointer not the table itself. Remove the extra reference to correct the passed value. Fixes: c4d096c3ca42 ("ASoC: SDCA: Add SDCA FDL data parsing") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c index 405e80b979de..4bcd8d1fdff8 100644 --- a/sound/soc/sdca/sdca_device.c +++ b/sound/soc/sdca/sdca_device.c @@ -43,7 +43,7 @@ void sdca_lookup_swft(struct sdw_slave *slave) dev_info(&slave->dev, "SWFT not available\n"); else devm_add_action_or_reset(&slave->dev, devm_acpi_table_put, - &slave->sdca_data.swft); + slave->sdca_data.swft); } EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA"); -- cgit v1.2.3 From 7f64ccc374b2fe1f6a169182e95ab8e104cae406 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:34:58 +0100 Subject: ASoC: SDCA: Always free firmware in FDL path In the case a disk firmware exists but is invalid and no SWFT firmware exists fdl_load_file() will return without calling release_firmware(). Update the code to call this to ensure the firmware is released on the error path. Fixes: 71f7990a34cd ("ASoC: SDCA: Add FDL library for XU entities") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_fdl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index 994821a6df61..60fdd406220d 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -258,7 +258,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, if (!swf) { dev_err(dev, "failed to locate SWF\n"); - return -ENOENT; + ret = -ENOENT; + goto error; } dev_info(dev, "loading SWF: %x-%x-%x\n", @@ -270,6 +271,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, swf->file_length - offsetof(struct acpi_sw_file, data)); + +error: release_firmware(firmware); return ret; } -- cgit v1.2.3 From 556d872e7c2a0b570c5b0974813847ef0d0cd637 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:34:59 +0100 Subject: ASoC: SDCA: Make UMP message size check more robust If message offset was larger than the buffer length the size check will pass incorrectly. Refactor the check such that it is more robust to invalid sizes. Fixes: daab108504be ("ASoC: SDCA: Add UMP buffer helper functions") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_ump.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c index a86bb28c6d0a..82a8bf75bbca 100644 --- a/sound/soc/sdca/sdca_ump.c +++ b/sound/soc/sdca/sdca_ump.c @@ -141,7 +141,7 @@ int sdca_ump_read_message(struct device *dev, return ret; } - if (msg_len > buf_len - msg_offset) { + if (msg_offset + msg_len > buf_len) { dev_err(dev, "%s: message too big for UMP buffer: %d\n", entity->label, msg_len); return -EINVAL; @@ -207,7 +207,7 @@ int sdca_ump_write_message(struct device *dev, buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0); ump_mode = sdca_range(range, SDCA_MESSAGEOFFSET_UMP_MODE, 0); - if (msg_len > buf_len - msg_offset) { + if (msg_offset + msg_len > buf_len) { dev_err(dev, "%s: message too big for UMP buffer: %d\n", entity->label, msg_len); return -EINVAL; -- cgit v1.2.3 From 951e921b039b793bef7050eaf5c5fb1a4a5341d1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:35:00 +0100 Subject: ASoC: SDCA: Ensure that Control Range is large enough for header When reading the Ranges structure from an SDCA Control, ensure that the read data is large enough to encompass the required header before accessing it. Fixes: 64fb5af1d1bb ("ASoC: SDCA: Add parsing for Control range structures") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_functions.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 77940bd6b33c..7a7a9f1a4938 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -855,6 +855,8 @@ static int find_sdca_control_range(struct device *dev, return 0; else if (num_range < 0) return num_range; + else if (num_range < 2 * sizeof(*limits)) + return -EINVAL; range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL); if (!range_list) -- cgit v1.2.3 From 78f75d632f74b8de0f081a128588f7c37d0d1164 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Wed, 22 Jul 2026 14:02:03 -0700 Subject: rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check() rds_tcp_laddr_check() looks up a scoped IPv6 interface with dev_get_by_index_rcu(), drops the RCU read-side lock, and only then passes the bare struct net_device * into ipv6_chk_addr(). dev_get_by_index_rcu() only keeps the device alive within the same RCU read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can free the net_device; ipv6_chk_addr() then dereferences the stale pointer in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading freed memory. Keep the RCU read-side lock held across the ipv6_chk_addr() call instead of dropping it right after the lookup, so the device cannot be freed while it is in use. BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) Read of size 8 at addr ffff8880106ec000 by task exploit/153 Call Trace: ... kasan_report (mm/kasan/report.c:595) __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) rds_tcp_laddr_check (net/rds/tcp.c:370) rds_bind (net/rds/bind.c:248) __sys_bind (net/socket.c:1920) __x64_sys_bind (net/socket.c:1956) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Allison Henderson Link: https://patch.msgid.link/20260722210203.565803-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- net/rds/tcp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 5de35d556f29..b263634ac750 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -355,23 +355,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr, /* If the scope_id is specified, check only those addresses * hosted on the specified interface. */ + rcu_read_lock(); if (scope_id != 0) { - rcu_read_lock(); dev = dev_get_by_index_rcu(net, scope_id); /* scope_id is not valid... */ if (!dev) { rcu_read_unlock(); return -EADDRNOTAVAIL; } - rcu_read_unlock(); } #if IS_ENABLED(CONFIG_IPV6) if (ipv6_mod_enabled()) { ret = ipv6_chk_addr(net, addr, dev, 0); - if (ret) + if (ret) { + rcu_read_unlock(); return 0; + } } #endif + rcu_read_unlock(); return -EADDRNOTAVAIL; } -- cgit v1.2.3 From 52a3a0cc28bf6fd8b38ef6c81b21f948bf13996a Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Thu, 23 Jul 2026 14:21:21 +0800 Subject: ASoC: Intel: soc-acpi: Add entry for sof_rt5682 in NVL match table. Adding rt5682 I2S codec support for NVL platforms and entry in match table. Signed-off-by: Balamurugan C Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260723062121.869857-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-nvl-match.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index 217272260803..a7f4097d426c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -8,6 +8,7 @@ #include #include +#include #include "soc-acpi-intel-sdw-mockup-match.h" static const struct snd_soc_acpi_codecs nvl_essx_83x6 = { @@ -15,6 +16,11 @@ static const struct snd_soc_acpi_codecs nvl_essx_83x6 = { .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, }; +static const struct snd_soc_acpi_codecs nvl_rt5682_rt5682s_hp = { + .num_codecs = 2, + .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID}, +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { { .comp_ids = &nvl_essx_83x6, @@ -24,6 +30,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, + { + .comp_ids = &nvl_rt5682_rt5682s_hp, + .drv_name = "sof_rt5682", + .sof_tplg_filename = "sof-nvl-rt5682", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_machines); -- cgit v1.2.3 From d8a0962d3506f7c9fdc3cc49c81ff0b8ded09f2f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 16 Jul 2026 15:21:03 +0200 Subject: drm/mediatek: mtk_hdmi_common: take i2c adapter module reference The i2c subsystem currently blocks during adapter deregistration whenever there are consumers holding a reference. Switch to using of_get_i2c_adapter_by_node() which also takes a reference to the adapter module so that an attempt to unload the module while in use fails gracefully instead of blocking uninterruptibly. Signed-off-by: Johan Hovold Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260716132103.1564995-1-johan@kernel.org/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_hdmi_common.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c index a049489daa2d..042b98e5a519 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c @@ -290,6 +290,13 @@ static int mtk_hdmi_get_cec_dev(struct mtk_hdmi *hdmi, struct device *dev, struc return 0; } +static void mtk_hdmi_put_adapter(void *_adap) +{ + struct i2c_adapter *adap = _adap; + + i2c_put_adapter(adap); +} + static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device *pdev, const char * const *clk_names, size_t num_clocks) { @@ -328,12 +335,12 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device if (!i2c_np) return dev_err_probe(dev, -EINVAL, "No ddc-i2c-bus in connector\n"); - hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np); + hdmi->ddc_adpt = of_get_i2c_adapter_by_node(i2c_np); of_node_put(i2c_np); if (!hdmi->ddc_adpt) return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get ddc i2c adapter by node\n"); - ret = devm_add_action_or_reset(dev, mtk_hdmi_put_device, &hdmi->ddc_adpt->dev); + ret = devm_add_action_or_reset(dev, mtk_hdmi_put_adapter, hdmi->ddc_adpt); if (ret) return ret; -- cgit v1.2.3 From 6273dd3ffb54ec581855b82ae77331b66028249c Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 20 Jul 2026 16:44:26 +0800 Subject: drm/vc4: Shut down BO cache timer before teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BO cache timer callback schedules time_work, and time_work can rearm the timer through vc4_bo_cache_free_old(). vc4_bo_cache_destroy() deletes the timer and then cancels the work, which does not break that cycle: the work being cancelled can rearm the timer, and the timer then queues work again after teardown. Use timer_shutdown_sync() instead, so the timer cannot be rearmed and the cycle ends with cancel_work_sync(). Fixes: c826a6e10644 ("drm/vc4: Add a BO cache.") Cc: stable@vger.kernel.org Signed-off-by: Linmao Li Link: https://patch.msgid.link/20260720084426.1632508-1-lilinmao@kylinos.cn Reviewed-by: Maíra Canal Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_bo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 5e7c46dd7823..49ea2ed0996b 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -1048,7 +1048,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused) struct vc4_dev *vc4 = to_vc4_dev(dev); int i; - timer_delete(&vc4->bo_cache.time_timer); + timer_shutdown_sync(&vc4->bo_cache.time_timer); cancel_work_sync(&vc4->bo_cache.time_work); vc4_bo_cache_purge(dev); -- cgit v1.2.3 From 289e680c89ae8a0bb629fa8308313f5c8c6c76a3 Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Thu, 23 Jul 2026 15:18:14 -0700 Subject: bpf: Reject passing scalar NULL to nonnull arg of a global subprog A global subprogram argument tagged __arg_nonnull is set up as a non-nullable PTR_TO_MEM. However the verifier does not check against a scalar NULL, leading to real NULL pointer dereference. Reject it as well. Fixes: 94e1c70a3452 ("bpf: support 'arg:xxx' btf_decl_tag-based hints for global subprog args") Signed-off-by: Amery Hung Acked-by: Eduard Zingerman Link: https://patch.msgid.link/20260723221815.367797-1-ameryhung@gmail.com Signed-off-by: Eduard Zingerman --- kernel/bpf/verifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 99444eae917e..7aa47342dc65 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9189,7 +9189,8 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, return ret; if (check_mem_reg(env, reg, argno, arg->mem_size)) return -EINVAL; - if (!(arg->arg_type & PTR_MAYBE_NULL) && (reg->type & PTR_MAYBE_NULL)) { + if (!(arg->arg_type & PTR_MAYBE_NULL) && + (type_may_be_null(reg->type) || bpf_register_is_null(reg))) { bpf_log(log, "%s is expected to be non-NULL\n", reg_arg_name(env, argno)); return -EINVAL; -- cgit v1.2.3 From 55c7bd2ddee50fd37b3b4c0b7a76bb0fd565f38b Mon Sep 17 00:00:00 2001 From: Amery Hung Date: Thu, 23 Jul 2026 15:18:15 -0700 Subject: selftests/bpf: Test passing scalar NULL to nonnull global subprog Make sure the verifier reject passing a hardcoded NULL to an __arg_nonnull argument. Signed-off-by: Amery Hung Link: https://patch.msgid.link/20260723221815.367797-2-ameryhung@gmail.com Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/progs/verifier_global_subprogs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c index 75a2e3f48d0f..67dc352addfd 100644 --- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c +++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c @@ -185,6 +185,16 @@ int arg_tag_nonnull_ptr_good(void *ctx) return subprog_nonnull_ptr_good(&x, &y); } +SEC("?raw_tp") +__failure __log_level(2) +__msg("R1 is expected to be non-NULL") +int arg_tag_nonnull_ptr_null_bad(void *ctx) +{ + int y = 74; + + return subprog_nonnull_ptr_good(NULL, &y); +} + /* this global subprog can be now called from many types of entry progs, each * with different context type */ -- cgit v1.2.3 From 1ff399c4cd132a24c73e5e237a11cb9d6b68dff1 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 24 Jul 2026 08:02:14 +0200 Subject: Revert "drm/pagemap: Guard HPAGE_PMD_ORDER use with CONFIG_ARCH_ENABLE_THP_MIGRATION" This reverts commit 04b177544a040cbafab760d6b766381c6b22e0a8. The original author requested it to be reverted, as it conflicts with changes in the -next branch for MM: "I'm not sure who is doing the drm-misc-fixes PR, but if you are can you omit this patch: https://patchwork.freedesktop.org/series/170865/ I guess this conflicts with MM changes in their next tree and it easy enough on our side to do this slightly differently to avoid a conflict so going to post revert + a different change. If this is already sent nbd." Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/drm_pagemap.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 4a794544b7dc..892b325fa99b 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -12,12 +12,6 @@ #include #include -#if IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION) -#define DRM_PAGEMAP_PMD_ORDER HPAGE_PMD_ORDER -#else -#define DRM_PAGEMAP_PMD_ORDER (-1) -#endif - /** * DOC: Overview * @@ -585,7 +579,7 @@ static int drm_pagemap_cpages(unsigned long *migrate_pfn, unsigned long npages) order = folio_order(folio); cpages += NR_PAGES(order); } else if (migrate_pfn[i] & MIGRATE_PFN_COMPOUND) { - order = DRM_PAGEMAP_PMD_ORDER; + order = HPAGE_PMD_ORDER; cpages += NR_PAGES(order); } @@ -773,11 +767,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, if (migrate.src[i] & MIGRATE_PFN_COMPOUND) { drm_WARN_ONCE(dpagemap->drm, src_page && - folio_order(page_folio(src_page)) != - DRM_PAGEMAP_PMD_ORDER, + folio_order(page_folio(src_page)) != HPAGE_PMD_ORDER, "Unexpected folio order\n"); - order = DRM_PAGEMAP_PMD_ORDER; + order = HPAGE_PMD_ORDER; migrate.dst[i] |= MIGRATE_PFN_COMPOUND; for (j = 1; j < NR_PAGES(order) && i + j < npages; j++) -- cgit v1.2.3 From 112badb7245059c176e3924f9cd9ce2933cb0082 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 20 Jul 2026 16:07:28 +0100 Subject: drm/imagination: Fix value of HWRT address in pvr_job_create tracepoint The pvr_job_create tracepoint was using pvr_fw_object::fw_addr_offset to display the firmware address of the render target structure attached to a job, but that's an offset into the firmware heap, not the expected full address, which is more useful in general e.g. for cross referencing against firmware logs. Use pvr_fw_object_get_fw_addr() to get the full firmware address. Fixes: c1079aebb4de ("drm/imagination: Add support for trace points") Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260720-fix-pvr-job-trace-hwrt-v1-1-b24551802efb@imgtec.com Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_trace.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_trace.h b/drivers/gpu/drm/imagination/pvr_trace.h index ffbbde3b2bcc..943d6b2418cb 100644 --- a/drivers/gpu/drm/imagination/pvr_trace.h +++ b/drivers/gpu/drm/imagination/pvr_trace.h @@ -41,6 +41,17 @@ TRACE_EVENT(pvr_job_submit_ioctl, __entry->count) ); +#define PVR_JOB_GET_HWRT_FW_ADDR(job) \ + ({ \ + struct pvr_job *_job = (job); \ + u32 _hwrt_fw_addr = 0; \ + \ + if (_job && _job->hwrt) \ + pvr_fw_object_get_fw_addr(_job->hwrt->fw_obj, &_hwrt_fw_addr); \ + \ + _hwrt_fw_addr; \ + }) + #define PVR_JOB_TYPE_TO_STR(val) \ __print_symbolic(val, \ { DRM_PVR_JOB_TYPE_GEOMETRY, "geometry" }, \ @@ -64,9 +75,7 @@ TRACE_EVENT(pvr_job_create, __entry->ctx = job->ctx; __entry->fw_obj = job->ctx->fw_obj; pvr_fw_object_get_fw_addr(job->ctx->fw_obj, &__entry->fw_addr); - __entry->hwrt_addr = job->hwrt ? - job->hwrt->fw_obj->fw_addr_offset : - 0; + __entry->hwrt_addr = PVR_JOB_GET_HWRT_FW_ADDR(job); __entry->job = job; __entry->job_type = job->type; __entry->sync_op_count = sync_op_count;), @@ -82,6 +91,7 @@ TRACE_EVENT(pvr_job_create, ); #undef PVR_JOB_TYPE_TO_STR +#undef PVR_JOB_GET_HWRT_FW_ADDR TRACE_EVENT(pvr_job_submit_fw, TP_PROTO(struct pvr_job *job), -- cgit v1.2.3 From c1d04c1bce98f9dd984a9c6657278a7761854c9c Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Thu, 23 Jul 2026 18:01:13 +0200 Subject: pidfs: make pidfs_ino_lock static Fixes: 87caaeef7995 ("pidfs: implement ino allocation without the pidmap lock") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607231547.ehCQxi0L-lkp@intel.com/ Signed-off-by: Mateusz Guzik Link: https://patch.msgid.link/20260723160114.291515-1-mjguzik@gmail.com Signed-off-by: Christian Brauner (Amutable) --- fs/pidfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index d7fe9abdd6f1..b57ecc96e967 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -108,7 +108,7 @@ struct pidfs_attr { #if BITS_PER_LONG == 32 -DEFINE_SPINLOCK(pidfs_ino_lock); +static DEFINE_SPINLOCK(pidfs_ino_lock); static u64 pidfs_ino_nr = 1; static inline unsigned long pidfs_ino(u64 ino) -- cgit v1.2.3 From 47e20d4b3da97ef3881d1e55e43545c22424f3fc Mon Sep 17 00:00:00 2001 From: Pu Lehui Date: Fri, 24 Jul 2026 16:33:08 +0800 Subject: LoongArch: BPF: Fix memory leak in bpf_jit_free() When bpf_int_jit_compile() is called for subprograms, it returns early during the first pass (!prog->is_func || extra_pass is false), keeping ctx->offset alive for the subsequent extra pass. If JIT compilation fails for a later subprogram, the BPF core aborts and calls bpf_jit_free() to clean up the first subprogram. However, bpf_jit_free() fails to free jit_data->ctx.offset, which causes a memory leak of the JIT context offsets array. So fix this by adding the missing kvfree(jit_data->ctx.offset) in bpf_jit_free(). Reported-by: Sashiko Fixes: 4ab17e762b34 ("LoongArch: BPF: Use BPF prog pack allocator") Acked-by: Tiezhu Yang Signed-off-by: Pu Lehui Signed-off-by: Huacai Chen --- arch/loongarch/net/bpf_jit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index c91d474faba7..29c281bef28e 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -2361,6 +2361,7 @@ void bpf_jit_free(struct bpf_prog *prog) */ if (jit_data) { bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header); + kvfree(jit_data->ctx.offset); kfree(jit_data); } hdr = bpf_jit_binary_pack_hdr(prog); -- cgit v1.2.3 From cf4dd800e49d35d48ebd63d511a6e200f39176f7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 24 Jul 2026 16:33:14 +0800 Subject: platform/loongarch: laptop: Stop setting acpi_device_class() The driver populates acpi_device_class() which is never read afterward, so make it stop doing that and drop the symbol defined specifically for this purpose. No intentional functional impact. This change will facilitate the removal of "device_class" from "struct acpi_device_pnp" in the future. Reviewed-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki Signed-off-by: Huacai Chen --- drivers/platform/loongarch/loongson-laptop.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/platform/loongarch/loongson-laptop.c b/drivers/platform/loongarch/loongson-laptop.c index 61b18ac206c9..f2dcc6d78051 100644 --- a/drivers/platform/loongarch/loongson-laptop.c +++ b/drivers/platform/loongarch/loongson-laptop.c @@ -30,7 +30,6 @@ #define LOONGSON_ACPI_HKEY_HID "LOON0000" #define ACPI_LAPTOP_NAME "loongson-laptop" -#define ACPI_LAPTOP_ACPI_EVENT_PREFIX "loongson" #define MAX_ACPI_ARGS 3 #define GENERIC_HOTKEY_MAP_MAX 64 @@ -167,8 +166,6 @@ static int __init setup_acpi_notify(struct generic_sub_driver *sub_driver) } sub_driver->device->driver_data = sub_driver; - sprintf(acpi_device_class(sub_driver->device), "%s/%s", - ACPI_LAPTOP_ACPI_EVENT_PREFIX, sub_driver->name); status = acpi_install_notify_handler(*sub_driver->handle, sub_driver->type, dispatch_acpi_notify, sub_driver); -- cgit v1.2.3 From 91a70492c03040d51b36f595530d6491d5d6c541 Mon Sep 17 00:00:00 2001 From: Zixing Liu Date: Fri, 24 Jul 2026 16:33:14 +0800 Subject: platform/loongarch: laptop: Explicitly reset bl_powered state when suspend On EAECIS NL60R with EC firmware version 1.11, resuming from S3 has a very high chance (>90%) of causing the EC to lose the previous backlight power state. When this happens, the laptop resumes normally from S3, but the backlight remains off (when shining on the screen with a flash light, we can see the screen contents are updating normally). Since there is no generic way to query the EC's backlight state on Loongson laptop platforms, assume the worst-case scenario and restart the backlight power inside the kernel each time the system resumes. Cc: stable@vger.kernel.org Fixes: 53c762b47f72 ("platform/loongarch: laptop: Add backlight power control support") Tested-by: Yao Zi Tested-by: Xi Ruoyao Signed-off-by: Zixing Liu Signed-off-by: Huacai Chen --- drivers/platform/loongarch/loongson-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/loongarch/loongson-laptop.c b/drivers/platform/loongarch/loongson-laptop.c index f2dcc6d78051..4ec56621db24 100644 --- a/drivers/platform/loongarch/loongson-laptop.c +++ b/drivers/platform/loongarch/loongson-laptop.c @@ -186,6 +186,7 @@ static int __init setup_acpi_notify(struct generic_sub_driver *sub_driver) static int loongson_hotkey_suspend(struct device *dev) { + bl_powered = false; return 0; } -- cgit v1.2.3 From 8fa01be5a6149404adb82c0979a78f6347edd3ef Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:04 -0700 Subject: KVM: s390: pci: Reject adapter interrupt forwarding if already enabled The MPCIFC instruction doesn't allow registering adapter interrupts without first unregistering. So reject any request to enable interrupt forwarding if its already enabled for the zPCI device. This also fixes overwriting and thus leaking resources when the ioctl is called multiple times for the same device. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- arch/s390/kvm/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 720bb58cabe2..d2a11cdf6941 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -237,6 +237,10 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, if (zdev->gisa == 0) return -EINVAL; + /* AIF already enabled for the device */ + if (zdev->kzdev->fib.fmt0.aibv != 0) + return -EINVAL; + kvm = zdev->kzdev->kvm; msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi); -- cgit v1.2.3 From 36f6999ecde3976731a8bfc0b8e667da6f593069 Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:05 -0700 Subject: KVM: s390: pci: Fix memory accounting for pinned/unpinned pages The account_mem() and unaccount_mem() functions call get_uid() which increments the reference count of struct user_struct on every invocation. But we don't decrement the count by calling free_uid(). It also accounted/unaccounted the pages against the current->mm. But its possible the unaccount_mem() can be called from a different process context than the one that originally pinned the pages. Let's fix this by storing the pinning process user_struct and mm_struct when accounting for pinned pages, and subsequently free these resources when the pages are unpinned. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato [borntraeger@linux.ibm.com: Fixed whitespace] Signed-off-by: Christian Borntraeger --- arch/s390/kvm/pci.c | 43 ++++++++++++++++++++++++++++++++----------- arch/s390/kvm/pci.h | 2 ++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index d2a11cdf6941..0741aed442bc 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -190,33 +190,54 @@ static int kvm_zpci_clear_airq(struct zpci_dev *zdev) return cc ? -EIO : 0; } -static inline void unaccount_mem(unsigned long nr_pages) +static inline void unaccount_mem(struct kvm_zdev *kzdev, unsigned long nr_pages) { - struct user_struct *user = get_uid(current_user()); + struct user_struct *user = kzdev->user_account; + struct mm_struct *mm_account = kzdev->mm_account; - if (user) + if (user) { atomic_long_sub(nr_pages, &user->locked_vm); - if (current->mm) - atomic64_sub(nr_pages, ¤t->mm->pinned_vm); + free_uid(user); + kzdev->user_account = NULL; + } + + if (mm_account) { + atomic64_sub(nr_pages, &mm_account->pinned_vm); + mmdrop(mm_account); + kzdev->mm_account = NULL; + } } -static inline int account_mem(unsigned long nr_pages) +static inline int account_mem(struct kvm_zdev *kzdev, unsigned long nr_pages) { struct user_struct *user = get_uid(current_user()); unsigned long page_limit, cur_pages, new_pages; + int rc = 0; page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; cur_pages = atomic_long_read(&user->locked_vm); do { new_pages = cur_pages + nr_pages; - if (new_pages > page_limit) - return -ENOMEM; + if (new_pages > page_limit) { + rc = -ENOMEM; + goto out; + } } while (!atomic_long_try_cmpxchg(&user->locked_vm, &cur_pages, new_pages)); - atomic64_add(nr_pages, ¤t->mm->pinned_vm); + if (current->mm) { + mmgrab(current->mm); + atomic64_add(nr_pages, ¤t->mm->pinned_vm); + } + + kzdev->user_account = user; + kzdev->mm_account = current->mm; return 0; + +out: + free_uid(user); + return rc; } static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, @@ -279,7 +300,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, } /* Account for pinned pages, roll back on failure */ - if (account_mem(pcount)) + if (account_mem(zdev->kzdev, pcount)) goto unpin2; /* AISB must be allocated before we can fill in GAITE */ @@ -400,7 +421,7 @@ static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force) pcount++; } if (pcount > 0) - unaccount_mem(pcount); + unaccount_mem(kzdev, pcount); out: mutex_unlock(&aift->aift_lock); diff --git a/arch/s390/kvm/pci.h b/arch/s390/kvm/pci.h index ff0972dd5e71..fdf8c7bf4ed0 100644 --- a/arch/s390/kvm/pci.h +++ b/arch/s390/kvm/pci.h @@ -22,6 +22,8 @@ struct kvm_zdev { struct kvm *kvm; struct zpci_fib fib; struct list_head entry; + struct user_struct *user_account; + struct mm_struct *mm_account; }; struct zpci_gaite { -- cgit v1.2.3 From f86842e4d6c482300f4567f492d512c9ccf5bc4f Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:06 -0700 Subject: KVM: s390: pci: Fix missing error codes and memory unaccounting In kvm_s390_pci_aif_enable() two error paths failed to set an error code, causing the function to return 0 on failure. It also failed to rollback memory accounting on failure. Fix both by propagating an error code on failure and calling unaccount_mem() in the cleanup path. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- arch/s390/kvm/pci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 0741aed442bc..36eb30953bb5 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -300,14 +300,17 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, } /* Account for pinned pages, roll back on failure */ - if (account_mem(zdev->kzdev, pcount)) + rc = account_mem(zdev->kzdev, pcount); + if (rc) goto unpin2; /* AISB must be allocated before we can fill in GAITE */ mutex_lock(&aift->aift_lock); bit = airq_iv_alloc_bit(aift->sbv); - if (bit == -1UL) + if (bit == -1UL) { + rc = -ENOMEM; goto unlock; + } zdev->aisb = bit; /* store the summary bit number */ zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK | @@ -351,6 +354,8 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, return rc; unlock: + if (pcount > 0) + unaccount_mem(zdev->kzdev, pcount); mutex_unlock(&aift->aift_lock); unpin2: if (fib->fmt0.sum == 1) -- cgit v1.2.3 From 8bf09b9b7d3232806df95f409581f8a9fd99a3fa Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:07 -0700 Subject: KVM: s390: pci: Fix NULL dereference on AIBV allocation failure The airq_iv_create() can return NULL on failure, but the return value was never checked. If it fails, zdev->aibv will be NULL and fail when dereferenced in kvm_zpci_set_airq(). Add a NULL check and free the previously allocated AISB bit and zdev->aisb on failure. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- arch/s390/kvm/pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 36eb30953bb5..1eb127fc9f89 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -317,6 +317,11 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, AIRQ_IV_GUESTVEC, phys_to_virt(fib->fmt0.aibv)); + if (!zdev->aibv) { + rc = -ENOMEM; + goto free_aisb; + } + spin_lock_irq(&aift->gait_lock); gaite = aift->gait + zdev->aisb; @@ -353,6 +358,9 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, rc = kvm_zpci_set_airq(zdev); return rc; +free_aisb: + airq_iv_free_bit(aift->sbv, zdev->aisb); + zdev->aisb = 0; unlock: if (pcount > 0) unaccount_mem(zdev->kzdev, pcount); -- cgit v1.2.3 From 5580c9858f1e00f60191eb09c3add359836d60b6 Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:08 -0700 Subject: KVM: s390: pci: Fix resource leak on IRQ registration failure Currently if kvm_zpci_set_airq() fails, kvm_s390_pci_aif_enable() returns the error code but doesn't do any resource cleanup thus leaking resources. Fix this by cleaning up all the resources such as the GAITE, AIBV, AISB and unpinning any pinned pages. While at it, remove dead code that stored FIB values that were never referenced. As part of the cleanup, we are also holding the aift_lock a bit longer, as we hold the lock while executing the MPCIFC instruction. Though this is not strictly necessary, it means we don't have to drop and re-acquire in the error case. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato Reviewed-by: Christian Borntraeger Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- arch/s390/kvm/pci.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 1eb127fc9f89..50f495bc8303 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -344,19 +344,32 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, aift->kzdev[zdev->aisb] = zdev->kzdev; spin_unlock_irq(&aift->gait_lock); - /* Update guest FIB for re-issue */ - fib->fmt0.aisbo = zdev->aisb & 63; - fib->fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8; - fib->fmt0.isc = gisc; - /* Save some guest fib values in the host for later use */ - zdev->kzdev->fib.fmt0.isc = fib->fmt0.isc; + zdev->kzdev->fib.fmt0.isc = gisc; zdev->kzdev->fib.fmt0.aibv = fib->fmt0.aibv; - mutex_unlock(&aift->aift_lock); /* Issue the clp to setup the irq now */ rc = kvm_zpci_set_airq(zdev); - return rc; + if (!rc) { + mutex_unlock(&aift->aift_lock); + return rc; + } + + /* Start cleanup */ + zdev->kzdev->fib.fmt0.isc = 0; + zdev->kzdev->fib.fmt0.aibv = 0; + + spin_lock_irq(&aift->gait_lock); + gaite->count--; + gaite->aisb = 0; + gaite->gisc = 0; + gaite->aisbo = 0; + gaite->gisa = 0; + aift->kzdev[zdev->aisb] = NULL; + spin_unlock_irq(&aift->gait_lock); + + airq_iv_release(zdev->aibv); + zdev->aibv = NULL; free_aisb: airq_iv_free_bit(aift->sbv, zdev->aisb); -- cgit v1.2.3 From 868d32ac72cba21c5c6d8a66a814b7c25a3a5c01 Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Thu, 23 Jul 2026 15:14:09 -0700 Subject: KVM: s390: pci: Validate AIBV and AISB before pinning guest pages The AIBV holds one bit per MSI-X vector for a given function. The size of the bit vector is derived from the NOI and the AIBVO. If the size of the AIBV exceeds a single page boundary, then reject the request as we cannot safely pin the guest AIBV. Similarly reject the request if the AISB address is not 8-byte aligned as the architecture requires doubleword alignment for the summary bit address. Since the AISBO can address up to 64 bits, the size of the AISB can only be 8 bytes for the function. This also ensures the AISB doesn't exceed a single page boundary. Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding") Cc: stable@vger.kernel.org Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Signed-off-by: Farhan Ali Tested-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- arch/s390/kvm/pci.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c index 50f495bc8303..50f5ec79600e 100644 --- a/arch/s390/kvm/pci.c +++ b/arch/s390/kvm/pci.c @@ -244,7 +244,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, bool assist) { struct page *pages[1], *aibv_page, *aisb_page = NULL; - unsigned int msi_vecs, idx; + unsigned int msi_vecs, idx, size; struct zpci_gaite *gaite; unsigned long hva, bit; struct kvm *kvm; @@ -271,6 +271,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, return gisc; /* Replace AIBV address */ + size = BITS_TO_LONGS(msi_vecs + fib->fmt0.aibvo) * sizeof(unsigned long); + npages = DIV_ROUND_UP((fib->fmt0.aibv & ~PAGE_MASK) + size, PAGE_SIZE); + /* AIBV cannot span more than 1 page */ + if (npages > 1) { + rc = -EINVAL; + goto out; + } + idx = srcu_read_lock(&kvm->srcu); hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aibv)); npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, pages); @@ -286,6 +294,12 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, /* Pin the guest AISB if one was specified */ if (fib->fmt0.sum == 1) { + /* AISB must be dword aligned */ + if (fib->fmt0.aisb & 0x7) { + rc = -EINVAL; + goto unpin1; + } + idx = srcu_read_lock(&kvm->srcu); hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aisb)); npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, -- cgit v1.2.3 From acd8aa3c4b91a38c8521000790890bc9d1083f1d Mon Sep 17 00:00:00 2001 From: Markus Lindner Date: Wed, 22 Jul 2026 03:09:18 +0200 Subject: ALSA: usb-audio: Add dB map quirk for Razer Barracuda X 2.4 The Razer Barracuda X 2.4 GHz USB headset dongle (0x1532:0x0552) reports a minimum volume register value of cval->min = -16800. In UAC 1/256 dB units, -16800 corresponds to -65.625 dB. However, stock ALSA misinterprets this raw integer as 1/100 dB units (-168.00 dB), causing user-space audio servers (PipeWire / PulseAudio) to map their volume curves against an incorrectly wide range. Add an explicit usbmix_dB_map entry overriding Unit 2 to -6562 (-65.62 dB) to accurately report the physical hardware attenuation bounds. Signed-off-by: Markus Lindner Link: https://patch.msgid.link/AS8P195MB2142F4EFF83980BD02BA6566E1C12@AS8P195MB2142.EURP195.PROD.OUTLOOK.COM Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index faac7df1fbcf..4d8dca04f87d 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -344,6 +344,16 @@ static const struct usbmix_name_map bose_soundlink_map[] = { { 0 } /* terminator */ }; +/* + * Razer Barracuda X 2.4: Firmware reports cval->min = -16800 in 1/256 dB units + * (-65.62 dB), which stock ALSA misinterprets as a -168 dB floor + */ +static const struct usbmix_dB_map razer_barracuda_x_2_4_dB = {-6562, 0}; +static const struct usbmix_name_map razer_barracuda_x_2_4_map[] = { + { 2, NULL, .dB = &razer_barracuda_x_2_4_dB }, + { 0 } /* terminator */ +}; + /* Sennheiser Communications Headset [PC 8], the dB value is reported as -6 negative maximum */ static const struct usbmix_dB_map sennheiser_pc8_dB = {-9500, 0}; static const struct usbmix_name_map sennheiser_pc8_map[] = { @@ -689,6 +699,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x17aa, 0x1046), .map = lenovo_p620_rear_map, }, + { + /* Razer Barracuda X 2.4 */ + .id = USB_ID(0x1532, 0x0552), + .map = razer_barracuda_x_2_4_map, + }, { /* Sennheiser Communications Headset [PC 8] */ .id = USB_ID(0x1395, 0x0025), -- cgit v1.2.3 From 82730dba0cf9d9524af0ceeb7eb6b5c3ab1bdb87 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 23 Jul 2026 14:01:59 -0400 Subject: drm/amd/display: Fix flip-done timeouts on mode1 reset The vblank on/off callbacks mixed use of amdgpu_irq_get/put() and amdgpu_dm_crtc_set_vupdate_irq() to enable and disable IRQs. With get/put, base driver will callback into DC to disable IRQs when refcount == 0. With set_vupdate_irq(), DC is called directly to disable IRQs, bypassing base driver's refcount tracking. During gpu reset, base driver can restore IRQs via amdgpu_irq_gpu_reset_resume_helper() > amdgpu_irq_update(). So if get/put() is not used (i.e. refcount == 0), then vupdate_irq will be disabled. This is problematic if DRM requests vblank on before amdgpu_irq_update() is called: drm_vblank_on() > set_vupdate_irq() enables vupdate_irq, but the refcount is still 0. gpu_reset_resume_helper() > irq_update() then immediately disables it, thus leading to flip done timeouts. This is made worse on DCN since VUPDATE_NO_LOCK is the only IRQ enabled. Prior to 8382cd234981, a combination of GRPH_FLIP and VSTARTUP IRQs were used, and they used get/put(). This explains why 8382cd234981 exposed this issue. Fix by using get/put() instead of set_vupdate_irq(). DCE is unchanged, since it relies on unbalanced enable/disable calls based on VRR status, and hence requires direct set_vupdate_irq(). Plus, it also uses GRPH_FLIP and VLINE IRQs, which are properly tracked by get/put(). Cc: stable@vger.kernel.org Fixes: 8382cd234981 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock") Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260723180159.52121-1-sunpeng.li@amd.com Signed-off-by: Mario Limonciello --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index b43cd68cde67..95d3da3c4199 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -280,10 +280,19 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) * is enabled. On DCE, vupdate is only needed in VRR mode. */ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { - rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable); + if (enable) { + rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc); + } } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) { if (enable) { - /* vblank irq on -> Only need vupdate irq in vrr mode */ + /* vblank irq on -> Only need vupdate irq in vrr mode + * Not ref-counted since we need explicit enable/disable + * for DCE VRR handling + */ if (amdgpu_dm_crtc_vrr_active(acrtc_state)) rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true); } else { -- cgit v1.2.3 From fbbaca9e208733652828ea98d00f09f260a7770e Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 23 Jul 2026 09:44:50 -0400 Subject: drm/amd/display: Fix missing DCE check in dm_gpureset_toggle_interrupts() This line was lost when cping from amd-staging-drm-next to drm-fixes. So add it back. Cc: stable@vger.kernel.org Fixes: 8382cd234981 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock") Reported-by: Lu Yao Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260723134450.13838-1-sunpeng.li@amd.com Signed-off-by: Mario Limonciello --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 9c564cd5edee..eaf19ec843e8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3324,7 +3324,8 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, acrtc = get_crtc_by_otg_inst( adev, state->stream_status[i].primary_otg_inst); - if (acrtc && state->stream_status[i].plane_count != 0) { + if (acrtc && state->stream_status[i].plane_count != 0 && + amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) { irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst; rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; if (rc) -- cgit v1.2.3 From cbb0140379de69fa46526672f3003f128f0b4e2e Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 16 Jul 2026 13:42:47 +0200 Subject: pmdomains: mediatek: Avoid setting RTFF's CLK_DIS before NRESTORE For the PCIE_PHY (also called PEXTP_PHY) type of RTFF hardware, there is special handling setting CLK_DIS before performing the NRESTORE sequence for resetting the RTFF and start sequencing from a clean state. That special handling, though, poses an issue in case the machine specific bootchain (bootloader in particular) ends up booting the kernel with both PCIe MAC and PHY enabled (not just power domains) as doing so will partially corrupt the PCIe MAC/PHY registers in an unpredictable manner, producing either an initialization fail in the PCI-Express drivers, or even a hard lockup! Resolve this by simply removing the special handling: in this case the bootchain, or remote processors, setting is getting honored by the later check for PWR_RTFF_SAVE_FLAG presence in the RTFF ctl register. Fixes: 9d02c94342b3 ("pmdomain: mediatek: Add support for RTFF Hardware in MT8196/MT6991") Signed-off-by: AngeloGioacchino Del Regno Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index f0a6339affd7..881a1a3b3632 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -486,16 +486,8 @@ static int scpsys_ctl_pwrseq_on(struct scpsys_domain *pd) if (ret < 0) return ret; - if (pd->data->rtff_type == SCPSYS_RTFF_TYPE_PCIE_PHY) - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RTFF_CLK_DIS); - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT); regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT); - - /* Wait for RTFF HW to sync buck isolation state if this is PCIe PHY RTFF */ - if (pd->data->rtff_type == SCPSYS_RTFF_TYPE_PCIE_PHY) - udelay(5); - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); /* -- cgit v1.2.3 From a3caaa06809248b996254be5b47e10804a3494e2 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Thu, 16 Jul 2026 16:39:38 +0200 Subject: drm/panthor: reject firmware sections with oversized data In panthor_fw_load_section_entry(), the data size to copy is calculated without validating it against the allocated section_size: section->data.size = hdr.data.end - hdr.data.start; If a crafted firmware sets data.size larger than the allocated memory, this could cause a heap buffer overflow in panthor_fw_init_section_mem() memcpy(section->mem->kmap, section->data.buf, section->data.size); Additionally, if the section->data.size exceeds the BO size, could this memset underflow the size calculation, leading to a massive out-of-bounds zeroing of kernel memory? memset(section->mem->kmap + section->data.size, 0, panthor_kernel_bo_size(section->mem) - section->data.size); Reject section entries whose initial data is larger than the section size. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260716143939.21903-1-osama.abdelkader@gmail.com Signed-off-by: Steven Price --- drivers/gpu/drm/panthor/panthor_fw.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 90f59d782a80..0623d37ac617 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -545,6 +545,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, struct panthor_fw_binary_section_entry_hdr hdr; struct panthor_fw_section *section; u32 section_size; + u32 data_size; u32 name_len; int ret; @@ -595,6 +596,13 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, return -EINVAL; } + section_size = hdr.va.end - hdr.va.start; + data_size = hdr.data.end - hdr.data.start; + if (data_size > section_size) { + drm_err(&ptdev->base, "Firmware corrupted, section data exceeds section size\n"); + return -EINVAL; + } + name_len = iter->size - iter->offset; section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL); @@ -603,7 +611,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, list_add_tail(§ion->node, &ptdev->fw->sections); section->flags = hdr.flags; - section->data.size = hdr.data.end - hdr.data.start; + section->data.size = data_size; if (section->data.size > 0) { void *data = drmm_kmalloc(&ptdev->base, section->data.size, GFP_KERNEL); @@ -626,7 +634,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, section->name = name; } - section_size = hdr.va.end - hdr.va.start; if (section_size) { u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK; struct panthor_gem_object *bo; -- cgit v1.2.3 From 39490ec6063d9dc3d995b7b48fc5106cd361a547 Mon Sep 17 00:00:00 2001 From: Surendra Singh Chouhan Date: Fri, 24 Jul 2026 18:25:33 +0530 Subject: platform/x86: dell-dw5826e: fix ACPI _DSM function index and bitmask usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PALC_DSM_FN_TRIGGER_PLDR was defined as BIT(1) (value 2). acpi_evaluate_dsm() expects a 0-based function index integer (0, 1, 2, ...), whereas acpi_check_dsm() expects a bitmask of supported function indices (BIT(1), BIT(2), ...). Because PALC_DSM_FN_TRIGGER_PLDR was defined as BIT(1), acpi_evaluate_dsm() was evaluating Function Index 2 instead of Function Index 1, while acpi_check_dsm() was checking for Function Index 1 support. Fix this by setting PALC_DSM_FN_TRIGGER_PLDR to 1 (the function index) and passing BIT(PALC_DSM_FN_TRIGGER_PLDR) to acpi_check_dsm(). Fixes: 1ab843135a77 ("platform/x86: dell-dw5826e: Add reset driver for DW5826e") Signed-off-by: Surendra Singh Chouhan Link: https://patch.msgid.link/20260724125533.74751-1-kr494167@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-dw5826e-reset.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/dell/dell-dw5826e-reset.c b/drivers/platform/x86/dell/dell-dw5826e-reset.c index 1ca7c3421bb5..64e6e9b1d7a0 100644 --- a/drivers/platform/x86/dell/dell-dw5826e-reset.c +++ b/drivers/platform/x86/dell/dell-dw5826e-reset.c @@ -13,7 +13,7 @@ #include #include -#define PALC_DSM_FN_TRIGGER_PLDR BIT(1) +#define PALC_DSM_FN_TRIGGER_PLDR 1 static guid_t palc_dsm_guid = GUID_INIT(0x5a1a4bba, 0x8006, 0x487e, 0xbe, 0x0a, 0xac, 0xf5, 0xd8, 0xfd, 0xfe, 0x59); @@ -66,7 +66,7 @@ static int palc_probe(struct platform_device *pdev) if (!handle) return -ENODEV; - if (!acpi_check_dsm(handle, &palc_dsm_guid, 1, PALC_DSM_FN_TRIGGER_PLDR)) + if (!acpi_check_dsm(handle, &palc_dsm_guid, 1, BIT(PALC_DSM_FN_TRIGGER_PLDR))) return -ENODEV; return 0; -- cgit v1.2.3 From 144f29e85702234b23d2a62abf723e6a17eb5427 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 21 Jul 2026 21:11:43 -0400 Subject: tracing: Fix mmiotrace possible NULL dereferencing of hiter->dev If the mmio_pipe_open() fails to find a PCI device, the hiter->dev will be assigned to NULL. The mmiotrace read() function dereferences the hiter->dev if hiter exists. Change the test of the read to not only check hiter being NULL, but also the hiter->dev before dereferencing it. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260721211143.36dbd559@gandalf.local.home Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin") Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260715143604.14481-1-gaikwad.dcg%40gmail.com Signed-off-by: Steven Rostedt --- kernel/trace/trace_mmiotrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index 20812e7f911f..b88b8d9923ad 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -145,7 +145,7 @@ static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp, goto print_out; } - if (!hiter) + if (!hiter || !hiter->dev) return 0; mmio_print_pcidev(s, hiter->dev); -- cgit v1.2.3 From b4eb07bde606c2096b24252be589e735eff6d413 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 17 Jul 2026 11:51:49 +0900 Subject: tracing: Fix union collision of module and refcnt for dynamic events In 'struct trace_event_call', the 'module' pointer and the 'refcnt' atomic variable share the same memory space in a union. For dynamic events, the union member is 'refcnt', which acts as an active reference counter. When a dynamic event (such as kprobe, uprobe, fprobe, eprobe, or wprobe) has a non-zero reference count (e.g. due to active event triggers or perf attachments), its 'call->module' evaluates to a small non-zero integer instead of NULL. When filtering or setting events for a specific module (e.g., writing ':mod:' to 'set_event'), the code in '__ftrace_set_clr_event_nolock()' and 'update_event_fields()' reads 'call->module' directly without checking whether the event is dynamic. This causes the kernel to treat the small integer (refcnt) as a 'struct module' pointer, leading to a NULL/invalid pointer dereference (Oops) when dereferencing the module name. Fix this by ensuring that the 'TRACE_EVENT_FL_DYNAMIC' flag is checked before treating 'call->module' as a valid pointer in these code paths. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178425670947.84440.11344393611899824907.stgit@devnote2 Fixes: 4c86bc531e60 ("tracing: Add :mod: command to enabled module events") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index c46e623e7e0d..956692856fa8 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1350,7 +1350,9 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match, call = file->event_call; /* If a module is specified, skip events that are not that module */ - if (module && (!call->module || strcmp(module_name(call->module), module))) + if (module && + ((call->flags & TRACE_EVENT_FL_DYNAMIC) || + !call->module || strcmp(module_name(call->module), module))) continue; name = trace_event_name(call); -- cgit v1.2.3 From 1a087033a6bad73b4140020b40e819b0933aafc3 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 17 Jul 2026 11:51:59 +0900 Subject: selftests/ftrace: Reset triggers at top level before instance loop When running instance tests, 'ftracetest' creates a new ftrace instance and runs the tests inside it. Before starting each test, it executes 'initialize_system()' to reset the ftrace state to initial-state. However, since 'initialize_system()' is executed in the context of the instance directory, it only cleans up triggers and filters of that instance. Any triggers or dynamic events left behind in the top-level instance by previous failed top-level tests, are left completely untouched. These top-level leftovers can cause subsequent instance-based tests to fail or even crash the kernel. Fix this by executing 'initialize_system()' in the top-level tracing directory once before entering the instance loop. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178425671889.84440.9477850701738666404.stgit@devnote2 Fixes: b5b77be812de ("selftests: ftrace: Allow some tests to be run in a tracing instance") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- tools/testing/selftests/ftrace/ftracetest | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index 0a56bf209f6c..8ad2c385407e 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -503,6 +503,7 @@ for t in $TEST_CASES; do done # Test on instance loop +(cd $TRACING_DIR; initialize_system) INSTANCE=" (instance) " for t in $TEST_CASES; do test_on_instance $t || continue -- cgit v1.2.3 From 8f76afb9b114bee1c1251e0e52553e9dc7c59f20 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Fri, 17 Jul 2026 10:32:52 -0700 Subject: tracing: Fix context switch counter truncation trace_user_fault_read() samples nr_context_switches_cpu() before enabling preemption and retries the user copy if the counter changes. The helper returns unsigned long long because rq->nr_switches is u64, but the saved value is unsigned int. Once a CPU has performed 2^32 context switches, assigning the counter to cnt discards its upper bits. The comparison after the copy promotes cnt back to unsigned long long, but the lost bits remain zero, so it reports a change even when the task was never scheduled out. Every retry then fails the same way until the 100-try guard warns and the user copy is abandoned. This affects long-running systems and workloads with high context-switch rates. A CPU switching 1,000 times per second takes about 50 days. Store the sampled count in unsigned long long so the full value is preserved. Cc: stable@vger.kernel.org Fixes: 64cf7d058a00 ("tracing: Have trace_marker use per-cpu data to read user space") Link: https://patch.msgid.link/20260717173252.3431565-1-usama.arif@linux.dev Reported-by: Breno Leitao Signed-off-by: Usama Arif Acked-by: Masami Hiramatsu (Google) Reviewed-by: Breno Leitao 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 18710c190c92..01a5e87af299 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6187,7 +6187,7 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo, { int cpu = smp_processor_id(); char *buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf; - unsigned int cnt; + unsigned long long cnt; int trys = 0; int ret; -- cgit v1.2.3 From 645effc0984ba8cd83cdf31e5a29945cd40973e1 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 30 Jun 2026 16:16:18 -0400 Subject: MAINTAINERS: ARM/FREESCALE: merge Layerscape entry into i.MX entry Both platforms are maintained by the same people, and the two entries contain largely identical information. Maintaining separate entries adds unnecessary duplication. Additionally, the standalone Layerscape entry does not include the imx@lists.linux.dev mailing list, causing Layerscape patches to miss NXP's Patchwork instance. Consolidating the entries ensures patches for both platforms are routed consistently and reduces future maintenance overhead. Signed-off-by: Frank Li --- MAINTAINERS | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..31188163a859 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2881,7 +2881,7 @@ W: http://www.armlinux.org.uk/ F: arch/arm/include/asm/hardware/dec21285.h F: arch/arm/mach-footbridge/ -ARM/FREESCALE IMX / MXC ARM ARCHITECTURE +ARM/FREESCALE IMX / MXC / LAYERSCAPE ARM ARCHITECTURE M: Frank Li M: Sascha Hauer R: Pengutronix Kernel Team @@ -2895,22 +2895,11 @@ F: Documentation/devicetree/bindings/firmware/nxp* F: arch/arm/boot/dts/nxp/ F: arch/arm64/boot/dts/freescale/ X: Documentation/devicetree/bindings/media/i2c/ -X: arch/arm64/boot/dts/freescale/fsl-* -X: arch/arm64/boot/dts/freescale/qoriq-* X: drivers/media/i2c/ N: imx N: mxs N: \bmxc[^\d] -ARM/FREESCALE LAYERSCAPE ARM ARCHITECTURE -M: Frank Li -L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/frank.li/linux.git -F: arch/arm/boot/dts/nxp/ls/ -F: arch/arm64/boot/dts/freescale/fsl-* -F: arch/arm64/boot/dts/freescale/qoriq-* - ARM/FREESCALE VYBRID ARM ARCHITECTURE M: Frank Li M: Sascha Hauer -- cgit v1.2.3 From 9736d2efc99670054359e153a9f312ef4646ec7b Mon Sep 17 00:00:00 2001 From: Wenjia Zhang Date: Fri, 24 Jul 2026 07:37:52 +0200 Subject: MAINTAINERS: Update SHARED MEMORY COMMUNICATIONS (SMC) maintainer entries Due to a change in responsibilities, I can no longer serve as an SMC maintainer and need to be removed from the MAINTAINERS list. To reflect these organizational changes, promote Mahanta Jambigi from reviewer to maintainer. Acked-by: Mahanta Jambigi Signed-off-by: Wenjia Zhang Reviewed-by: Sidraya Jayagond Reviewed-by: Dust Li Link: https://patch.msgid.link/20260724053752.3084-1-wenjia@linux.ibm.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index b72d2bd07f08..61126d170e4a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24632,8 +24632,7 @@ SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS M: D. Wythe M: Dust Li M: Sidraya Jayagond -M: Wenjia Zhang -R: Mahanta Jambigi +M: Mahanta Jambigi R: Tony Lu R: Wen Gu L: linux-rdma@vger.kernel.org -- cgit v1.2.3 From 072cd1f21819dedd2252e704d255de3b0cfc61a7 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Wed, 22 Jul 2026 00:29:50 +0000 Subject: nexthop: take nh->lock for f6i_list walks in replace check and notify fib6_check_nh_list() and __nexthop_replace_notify() walk nh->f6i_list during an RTNL-serialized nexthop replace without holding nh->lock. IPv6 RTM_NEWROUTE/RTM_DELROUTE run without RTNL and mutate that list under nh->lock (fib6_add_rt2node_nh(), fib6_purge_rt()), so both walks race a concurrent route delete that unlinks and frees a fib6_info: BUG: KASAN: slab-use-after-free in rt6_fill_node.isra.0 (net/ipv6/route.c:5799) Read of size 4 at addr ffff888014607e64 by task exploit/143 rt6_fill_node.isra.0 (net/ipv6/route.c:5799) fib6_rt_update (net/ipv6/route.c:6412) __nexthop_replace_notify (net/ipv4/nexthop.c:2542) rtm_new_nexthop (net/ipv4/nexthop.c:2554) rtnetlink_rcv_msg (net/core/rtnetlink.c:7076) BUG: KASAN: slab-use-after-free in fib6_check_nh_list (net/ipv4/nexthop.c:1605) Read of size 8 at addr ffff888014a7d068 by task exploit/142 fib6_check_nh_list (net/ipv4/nexthop.c:1605) rtm_new_nexthop (net/ipv4/nexthop.c:2575) rtnetlink_rcv_msg (net/core/rtnetlink.c:7076) Both walks only read the entries and take no tb6_lock, so protect them with nh->lock; fib6_rt_update() uses gfp_any(), which returns GFP_ATOMIC under the lock. Fixes: 081efd18326e ("ipv6: Protect nh->f6i_list with spinlock and flag.") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260722002951.2614721-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- net/ipv4/nexthop.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index 44fe75004cac..eb5c76ac807b 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -1597,14 +1597,21 @@ static int fib6_check_nh_list(struct nexthop *old, struct nexthop *new, struct netlink_ext_ack *extack) { struct fib6_info *f6i; + int err = 0; if (list_empty(&old->f6i_list)) return 0; + spin_lock_bh(&old->lock); list_for_each_entry(f6i, &old->f6i_list, nh_list) { - if (check_src_addr(&f6i->fib6_src.addr, extack) < 0) - return -EINVAL; + err = check_src_addr(&f6i->fib6_src.addr, extack); + if (err) + break; } + spin_unlock_bh(&old->lock); + + if (err) + return err; return fib6_check_nexthop(new, NULL, extack); } @@ -2538,8 +2545,10 @@ static void __nexthop_replace_notify(struct net *net, struct nexthop *nh, fi->nh_updated = false; } + spin_lock_bh(&nh->lock); list_for_each_entry(f6i, &nh->f6i_list, nh_list) fib6_rt_update(net, f6i, info); + spin_unlock_bh(&nh->lock); } /* send RTM_NEWROUTE with REPLACE flag set for all FIB entries -- cgit v1.2.3 From 4787a6d2629b4e8c0b6bacab1f75c1660eca44d9 Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Wed, 22 Jul 2026 00:29:51 +0000 Subject: nexthop: avoid unlocked f6i_list walk in nh_rt_cache_flush nh_rt_cache_flush() walks nh->f6i_list during an RTNL-serialized nexthop replace without holding nh->lock, racing the unlocked IPv6 route add/delete that mutate the list under nh->lock and free fib6_info entries (nh_rt_cache_flush() is inlined into rtm_new_nexthop()): BUG: KASAN: slab-use-after-free in nh_rt_cache_flush (net/ipv4/nexthop.c:2243) Read of size 8 at addr ffff888012953e18 by task exploit/146 nh_rt_cache_flush (net/ipv4/nexthop.c:2243) replace_nexthop (net/ipv4/nexthop.c:2610) rtm_new_nexthop (net/ipv4/nexthop.c:3323) rtnetlink_rcv_msg (net/core/rtnetlink.c:7076) Unlike the other f6i_list walks, this one bumps each route's sernum via fib6_update_sernum_upto_root(), which needs tb6_lock; taking nh->lock around it would invert the established tb6_lock -> nh->lock order and deadlock. As the only purpose is to invalidate cached dsts, bump the IPv6 sernum for the whole netns with rt_genid_bump_ipv6() instead, mirroring the rt_cache_flush() already done for IPv4 just above. Fixes: 081efd18326e ("ipv6: Protect nh->f6i_list with spinlock and flag.") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260722002951.2614721-2-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- net/ipv4/nexthop.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index eb5c76ac807b..0f1e21a5c812 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -2240,18 +2240,18 @@ static void remove_one_nexthop(struct net *net, struct nexthop *nh, static void nh_rt_cache_flush(struct net *net, struct nexthop *nh, struct nexthop *replaced_nh) { - struct fib6_info *f6i; struct nh_group *nhg; + bool have_f6i; int i; if (!list_empty(&nh->fi_list)) rt_cache_flush(net); - list_for_each_entry(f6i, &nh->f6i_list, nh_list) { - spin_lock_bh(&f6i->fib6_table->tb6_lock); - fib6_update_sernum_upto_root(net, f6i); - spin_unlock_bh(&f6i->fib6_table->tb6_lock); - } + spin_lock_bh(&nh->lock); + have_f6i = !list_empty(&nh->f6i_list); + spin_unlock_bh(&nh->lock); + if (have_f6i) + rt_genid_bump_ipv6(net); /* if an IPv6 group was replaced, we have to release all old * dsts to make sure all refcounts are released -- cgit v1.2.3 From 2d66a033864e27ab8d5e44cb36f31d9d2413bee4 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Fri, 24 Jul 2026 18:38:56 +0800 Subject: bpf, sockmap: Fix cork use-after-free in tcp_bpf_sendmsg() tcp_bpf_sendmsg() keeps msg_tx across sk_stream_wait_memory(), which drops and reacquires the socket lock. Its error path tries to decide whether msg_tx names the local temporary message by comparing it with the current value of psock->cork. This comparison is unsafe when two threads send on the same socket: Thread A Thread B msg_tx = psock->cork sk_msg_alloc() fails sk_stream_wait_memory() releases the socket lock acquires the socket lock completes the cork psock->cork = NULL frees the cork reacquires the socket lock msg_tx != psock->cork sk_msg_free(msg_tx) The stale cork is therefore mistaken for the local temporary message and freed again. KASAN reported: BUG: KASAN: slab-use-after-free in sk_msg_free+0x49/0x50 Read of size 4 at addr ffff88810c908800 by task poc/90 Call Trace: sk_msg_free+0x49/0x50 tcp_bpf_sendmsg+0x14f5/0x1cc0 __sys_sendto+0x32c/0x3a0 __x64_sys_sendto+0xdb/0x1b0 Allocated by task 89: __kasan_kmalloc+0x8f/0xa0 tcp_bpf_sendmsg+0x16b3/0x1cc0 Freed by task 91: __kasan_slab_free+0x43/0x70 kfree+0x131/0x3c0 tcp_bpf_sendmsg+0xec3/0x1cc0 msg_tx can only name the stack-local tmp or the shared cork. Check for tmp directly so a changed psock->cork cannot turn a shared message into an apparent local one. Fixes: 604326b41a6f ("bpf, sockmap: convert to generic sk_msg interface") Signed-off-by: Chengfeng Ye Reviewed-by: Emil Tsalapatis Reviewed-by: Jakub Sitnicki Link: https://lore.kernel.org/bpf/87fr18lmzo.fsf%40cloudflare.com/ Link: https://lore.kernel.org/netdev/20260719161630.2901208-1-nicoyip.dev%40gmail.com/ [v1] Link: https://patch.msgid.link/20260724103856.3399001-1-nicoyip.dev@gmail.com Signed-off-by: Eduard Zingerman --- net/ipv4/tcp_bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 8e905b50dead..a30475afb6f8 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -604,7 +604,7 @@ wait_for_sndbuf: wait_for_memory: err = sk_stream_wait_memory(sk, &timeo); if (err) { - if (msg_tx && msg_tx != psock->cork) + if (msg_tx == &tmp) sk_msg_free(sk, msg_tx); goto out_err; } -- cgit v1.2.3 From f0d9c3ffc2b5fc2ffacb56b3036155ce7a940a12 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 18 Jul 2026 14:29:01 -0400 Subject: af_unix: fix listen() succeeding on sockets in the wrong state Commit fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") inserted a prepare_peercred() call between err = -EINVAL and the socket-state check in unix_listen(). Since prepare_peercred() leaves err at 0 on success, listen() on an AF_UNIX socket that is not in TCP_CLOSE or TCP_LISTEN state (e.g. one that is already connected) now silently returns success without doing anything, instead of failing with EINVAL as it did before. Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") Signed-off-by: John Ericson Link: https://patch.msgid.link/20260718182903.2295560-1-John.Ericson@Obsidian.Systems Signed-off-by: Jakub Kicinski --- net/unix/af_unix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index f7a9d55eee8a..10ed9421e43a 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -823,6 +823,7 @@ static int unix_listen(struct socket *sock, int backlog) if (err) goto out; unix_state_lock(sk); + err = -EINVAL; if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN) goto out_unlock; if (backlog > sk->sk_max_ack_backlog) -- cgit v1.2.3 From 7f57c650d08b8793bb551bdb33ad876535ef9fe8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 18 Jul 2026 14:29:02 -0400 Subject: selftests/net/af_unix: test listen() rejects wrong socket states Add a regression test for the unix_listen() state check. The key case is listen() on a bound socket that has already been connected: it is no longer in TCP_CLOSE or TCP_LISTEN, so it must fail with EINVAL. A prepare_peercred() call slipped in ahead of that check once left err at 0 and made listen() silently succeed there instead; this guards against a repeat. The neighbouring outcomes are covered too so they cannot regress the same way: a bound socket in TCP_CLOSE listens fine, calling listen() again on a socket already in TCP_LISTEN is allowed, and an unbound socket fails with EINVAL. Each case runs for both listenable socket types (SOCK_STREAM and SOCK_SEQPACKET) and both pathname and abstract addresses. Fixes: fd0a109a0f6b ("net, pidfs: prepare for handing out pidfds for reaped sk->sk_peer_pid") Signed-off-by: John Ericson Link: https://patch.msgid.link/20260718182903.2295560-2-John.Ericson@Obsidian.Systems Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/af_unix/.gitignore | 1 + tools/testing/selftests/net/af_unix/Makefile | 1 + tools/testing/selftests/net/af_unix/unix_listen.c | 187 ++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 tools/testing/selftests/net/af_unix/unix_listen.c diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore index 240b26740c9e..973176644103 100644 --- a/tools/testing/selftests/net/af_unix/.gitignore +++ b/tools/testing/selftests/net/af_unix/.gitignore @@ -6,3 +6,4 @@ scm_rights so_peek_off unix_connect unix_connreset +unix_listen diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile index 4c0375e28bbe..57d159803a3a 100644 --- a/tools/testing/selftests/net/af_unix/Makefile +++ b/tools/testing/selftests/net/af_unix/Makefile @@ -14,6 +14,7 @@ TEST_GEN_PROGS := \ so_peek_off \ unix_connect \ unix_connreset \ + unix_listen \ # end of TEST_GEN_PROGS include ../../lib.mk diff --git a/tools/testing/selftests/net/af_unix/unix_listen.c b/tools/testing/selftests/net/af_unix/unix_listen.c new file mode 100644 index 000000000000..416fa3e5bfe9 --- /dev/null +++ b/tools/testing/selftests/net/af_unix/unix_listen.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tests for the state checks in AF_UNIX listen(). + * + * The central case is a regression test: listen() on a bound socket that + * is already connected (i.e. not in TCP_CLOSE or TCP_LISTEN state) must + * fail with EINVAL. A prior change accidentally let it return success + * without doing anything, because a helper called in between reset the + * error code to 0. The neighbouring checks (unbound, already listening) + * are tested too so they cannot silently regress the same way. + * + * Every case runs for both listenable socket types (SOCK_STREAM and + * SOCK_SEQPACKET) and both pathname and abstract addresses. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include +#include + +#include "kselftest_harness.h" + +#define SK_NAME "unix_listen_sk" +#define SRV_NAME "unix_listen_srv" + +FIXTURE(unix_listen) +{ + int sk; /* socket under test */ + int server; /* a listening peer, when a test needs one */ + struct sockaddr_un addr, srv_addr; + socklen_t addrlen, srv_addrlen; +}; + +FIXTURE_VARIANT(unix_listen) +{ + int type; + int abstract; +}; + +FIXTURE_VARIANT_ADD(unix_listen, stream_pathname) +{ + .type = SOCK_STREAM, + .abstract = 0, +}; + +FIXTURE_VARIANT_ADD(unix_listen, stream_abstract) +{ + .type = SOCK_STREAM, + .abstract = 1, +}; + +FIXTURE_VARIANT_ADD(unix_listen, seqpacket_pathname) +{ + .type = SOCK_SEQPACKET, + .abstract = 0, +}; + +FIXTURE_VARIANT_ADD(unix_listen, seqpacket_abstract) +{ + .type = SOCK_SEQPACKET, + .abstract = 1, +}; + +/* Fill @addr with a pathname or abstract address named @name. */ +static socklen_t unix_set_addr(struct sockaddr_un *addr, const char *name, + int abstract) +{ + size_t len = strlen(name); + + memset(addr, 0, sizeof(*addr)); + addr->sun_family = AF_UNIX; + /* An abstract address leads with a NUL and has no filesystem entry. */ + memcpy(addr->sun_path + (abstract ? 1 : 0), name, len); + + return offsetof(struct sockaddr_un, sun_path) + len + 1; +} + +FIXTURE_SETUP(unix_listen) +{ + self->sk = -1; + self->server = -1; + self->addrlen = unix_set_addr(&self->addr, SK_NAME, variant->abstract); + self->srv_addrlen = unix_set_addr(&self->srv_addr, SRV_NAME, + variant->abstract); +} + +FIXTURE_TEARDOWN(unix_listen) +{ + if (self->sk >= 0) + close(self->sk); + if (self->server >= 0) + close(self->server); + + /* Pathname sockets leave a filesystem entry behind; abstract ones do not. */ + if (!variant->abstract) { + remove(SK_NAME); + remove(SRV_NAME); + } +} + +/* A bound socket in TCP_CLOSE is the normal, allowed case. */ +TEST_F(unix_listen, bound_is_ok) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + EXPECT_EQ(0, err); +} + +/* Listening again on an already-listening socket (TCP_LISTEN) is allowed. */ +TEST_F(unix_listen, relisten_is_ok) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + ASSERT_EQ(0, err); + + err = listen(self->sk, 16); + EXPECT_EQ(0, err); +} + +/* listen() on an unbound socket fails: there is nothing to listen on. */ +TEST_F(unix_listen, unbound_is_einval) +{ + int err; + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + err = listen(self->sk, 8); + EXPECT_EQ(-1, err); + EXPECT_EQ(EINVAL, errno); +} + +/* + * The regression: a bound socket that has already been connected is not in + * TCP_CLOSE or TCP_LISTEN, so listen() must reject it with EINVAL rather + * than quietly succeeding. + */ +TEST_F(unix_listen, connected_is_einval) +{ + int err; + + self->server = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->server); + + err = bind(self->server, (struct sockaddr *)&self->srv_addr, + self->srv_addrlen); + ASSERT_EQ(0, err); + + err = listen(self->server, 8); + ASSERT_EQ(0, err); + + self->sk = socket(AF_UNIX, variant->type, 0); + ASSERT_LE(0, self->sk); + + /* Bind first so the unbound check does not mask the state check. */ + err = bind(self->sk, (struct sockaddr *)&self->addr, self->addrlen); + ASSERT_EQ(0, err); + + err = connect(self->sk, (struct sockaddr *)&self->srv_addr, + self->srv_addrlen); + ASSERT_EQ(0, err); + + err = listen(self->sk, 8); + EXPECT_EQ(-1, err); + EXPECT_EQ(EINVAL, errno); +} + +TEST_HARNESS_MAIN -- cgit v1.2.3 From a3c8382ebce4780c6b3ace2c09bc342313ac0186 Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Sun, 19 Jul 2026 15:56:04 +0200 Subject: xsk: fix buffer leak in xsk_drop_skb() for AF_XDP multi-buffer Tx This patch is inspired by the check[1] from sashiko. It says when overflow happens, the address of cq to be published is invalid. Actually the severer thing is the whole process of publishing the address of cq in this particular case is not right: it should truely publish the address and advance the cached_prod in cq as long as it reads descriptors from txq. The following is the full analysis. xsk_drop_skb() is called in three places, which all discard a partially built multi-buffer skb: 1) xsk_build_skb() -EOVERFLOW error path: packet exceeds MAX_SKB_FRAGS 2) __xsk_generic_xmit() post-loop cleanup: an invalid descriptor in the TX ring prevents the partial packet from completing 3) xsk_release(): socket close while xs->skb holds an incomplete packet In all three cases, the TX descriptors for the already-processed frags have been consumed from the TX ring (xskq_cons_release), and CQ slots have been reserved. However, xsk_drop_skb() calls xsk_consume_skb() which cancels the CQ reservations via xsk_cq_cancel_locked(). Since the buffer addresses never appear in the completion queue, userspace permanently loses track of these buffers. Fix this by letting consume_skb() trigger the existing xsk_destruct_skb destructor, which already submits buffer addresses to the CQ via xsk_cq_submit_addr_locked(). Note that cancelling the descriptors back to the TX ring (via xskq_cons_cancel_n) is not a appropriate option because an oversized packet that always exceeds MAX_SKB_FRAGS would be retried indefinitely, which is an obviously deadlock bug in the TX path. Also move the desc->addr assignment in xsk_build_skb() above the overflow check so that the current descriptor's address is recorded before a potential -EOVERFLOW jump to free_err, consistent with the zerocopy path in xsk_build_skb_zerocopy(). [1]: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/ Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") Acked-by: Maciej Fijalkowski Signed-off-by: Jason Xing Acked-by: Stanislav Fomichev Link: https://patch.msgid.link/20260719135609.147823-2-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- net/xdp/xsk.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index b970f30ea9b9..a7a83dc4546a 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -794,8 +794,11 @@ static void xsk_consume_skb(struct sk_buff *skb) static void xsk_drop_skb(struct sk_buff *skb) { - xdp_sk(skb->sk)->tx->invalid_descs += xsk_get_num_desc(skb); - xsk_consume_skb(skb); + struct xdp_sock *xs = xdp_sk(skb->sk); + + xs->tx->invalid_descs += xsk_get_num_desc(skb); + consume_skb(skb); + xs->skb = NULL; } static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, @@ -877,7 +880,7 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, return ERR_PTR(-ENOMEM); /* in case of -EOVERFLOW that could happen below, - * xsk_consume_skb() will release this node as whole skb + * xsk_drop_skb() will release this node as whole skb * would be dropped, which implies freeing all list elements */ xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; @@ -969,6 +972,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, goto free_err; } + xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; + if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) { err = -EOVERFLOW; goto free_err; @@ -986,8 +991,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, skb_add_rx_frag(skb, nr_frags, page, 0, len, PAGE_SIZE); refcount_add(PAGE_SIZE, &xs->sk.sk_wmem_alloc); - - xsk_addr->addrs[xsk_addr->num_descs] = desc->addr; } } -- cgit v1.2.3 From bd44a6dcd4248883de90f5dad53ae80066e27096 Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Sun, 19 Jul 2026 15:56:05 +0200 Subject: xsk: drain continuation descs after overflow in xsk_build_skb() Fix generic xmit path multi-buffer logic when packets are either too big (count of descriptors exceed MAX_SKB_FRAGS) or an invalid descriptor is included in fragmented packet. Introduce xdp_sock::drain_cont and act upon this flag - when it is set, keep on consuming descriptors from AF_XDP Tx ring and put them directly onto Cq. Previously these descriptors were silently lost and could never be reached again. Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") Closes: https://lore.kernel.org/all/20260425041726.85FB3C2BCB2@smtp.kernel.org/ Reviewed-by: Jason Xing Co-developed-by: Maciej Fijalkowski # wrapped cq addr submission onto routine Signed-off-by: Maciej Fijalkowski Signed-off-by: Jason Xing Acked-by: Stanislav Fomichev Link: https://patch.msgid.link/20260719135609.147823-3-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- include/net/xdp_sock.h | 1 + net/xdp/xsk.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index ebac60a3d8a1..8b51876efbed 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -80,6 +80,7 @@ struct xdp_sock { * call of __xsk_generic_xmit(). */ struct sk_buff *skb; + bool drain_cont; struct list_head map_list; /* Protects map_list */ diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index a7a83dc4546a..12a845d012f6 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -737,6 +737,19 @@ static void xsk_cq_submit_addr_locked(struct xsk_buff_pool *pool, spin_unlock_irqrestore(&pool->cq_prod_lock, flags); } +static void xsk_cq_submit_addr_single_locked(struct xsk_buff_pool *pool, + struct xdp_desc *desc) +{ + unsigned long flags; + u32 idx; + + spin_lock_irqsave(&pool->cq_prod_lock, flags); + idx = xskq_get_prod(pool->cq); + xskq_prod_write_addr(pool->cq, idx, desc->addr); + xskq_prod_submit_n(pool->cq, 1); + spin_unlock_irqrestore(&pool->cq_prod_lock, flags); +} + static void xsk_cq_cancel_locked(struct xsk_buff_pool *pool, u32 n) { spin_lock(&pool->cq->cq_cached_prod_lock); @@ -1028,13 +1041,14 @@ free_err: static int __xsk_generic_xmit(struct sock *sk) { struct xdp_sock *xs = xdp_sk(sk); - bool sent_frame = false; struct xdp_desc desc; struct sk_buff *skb; + u32 cached_cons; u32 max_batch; int err = 0; mutex_lock(&xs->mutex); + cached_cons = xs->tx->cached_cons; /* Since we dropped the RCU read lock, the socket state might have changed. */ if (unlikely(!xsk_is_bound(xs))) { @@ -1063,11 +1077,21 @@ static int __xsk_generic_xmit(struct sock *sk) goto out; } + if (unlikely(xs->drain_cont)) { + xsk_cq_submit_addr_single_locked(xs->pool, &desc); + xs->tx->invalid_descs++; + xskq_cons_release(xs->tx); + xs->drain_cont = xp_mb_desc(&desc); + continue; + } + skb = xsk_build_skb(xs, &desc); if (IS_ERR(skb)) { err = PTR_ERR(skb); if (err != -EOVERFLOW) goto out; + if (xp_mb_desc(&desc)) + xs->drain_cont = true; err = 0; continue; } @@ -1096,18 +1120,33 @@ static int __xsk_generic_xmit(struct sock *sk) goto out; } - sent_frame = true; xs->skb = NULL; } if (xskq_has_descs(xs->tx)) { + bool drain = xs->skb || xs->drain_cont || xp_mb_desc(&desc); + + err = xsk_cq_reserve_locked(xs->pool); + if (err) { + xs->tx->invalid_descs--; + if (xs->skb) + xsk_drop_skb(xs->skb); + xs->drain_cont = drain; + err = -EAGAIN; + goto out; + } + if (xs->skb) xsk_drop_skb(xs->skb); + + xsk_cq_submit_addr_single_locked(xs->pool, &desc); + xskq_cons_release(xs->tx); + xs->drain_cont = xp_mb_desc(&desc); } out: - if (sent_frame) + if (xs->tx->cached_cons != cached_cons) __xsk_tx_release(xs); mutex_unlock(&xs->mutex); -- cgit v1.2.3 From 08c9a8e794b4694c100dafcb80e069e29ad81b64 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Sun, 19 Jul 2026 15:56:06 +0200 Subject: xsk: provide sufficient space in pool->tx_descs The temporary Tx descriptor array in an XSK buffer pool is currently sized from the Tx ring of the socket that creates the pool. This is insufficient for shared-UMEM Tx. A later socket may have a larger Tx ring and submit a valid multi-buffer packet containing more descriptors than the first socket's ring, while still remaining within the device's xdp_zc_max_segs limit. A packet-framed batch parser bounded by the temporary array cannot reach the end-of-packet descriptor in that case. It leaves the packet on the Tx ring and encounters the same packet on every subsequent attempt, stalling Tx processing for that socket. Size the temporary descriptor array to the larger of the first Tx ring and the device's xdp_zc_max_segs capability. This keeps the array large enough to inspect one maximum-sized valid packet. Larger shared Tx rings do not require further resizing, as they can be processed over multiple batches. Following commit will actually address the data path side. Fixes: d5581966040f ("xsk: support ZC Tx multi-buffer in batch API") Reviewed-by: Jason Xing Signed-off-by: Maciej Fijalkowski Acked-by: Stanislav Fomichev Link: https://patch.msgid.link/20260719135609.147823-4-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- include/net/xsk_buff_pool.h | 6 ++++-- net/xdp/xsk.c | 10 +++++++--- net/xdp/xsk_buff_pool.c | 12 ++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h index ccb3b350001f..f5e737a83055 100644 --- a/include/net/xsk_buff_pool.h +++ b/include/net/xsk_buff_pool.h @@ -102,12 +102,14 @@ struct xsk_buff_pool { /* AF_XDP core. */ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, - struct xdp_umem *umem); + struct xdp_umem *umem, + u32 max_segs); int xp_assign_dev(struct xsk_buff_pool *pool, struct net_device *dev, u16 queue_id, u16 flags); int xp_assign_dev_shared(struct xsk_buff_pool *pool, struct xdp_sock *umem_xs, struct net_device *dev, u16 queue_id); -int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs); +int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs, + u32 max_segs); void xp_destroy(struct xsk_buff_pool *pool); void xp_get_pool(struct xsk_buff_pool *pool); bool xp_put_pool(struct xsk_buff_pool *pool); diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 12a845d012f6..091792d1d82d 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -1525,7 +1525,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr * and/or device. */ xs->pool = xp_create_and_assign_umem(xs, - umem_xs->umem); + umem_xs->umem, + dev->xdp_zc_max_segs); if (!xs->pool) { err = -ENOMEM; sockfd_put(sock); @@ -1557,7 +1558,8 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr * utilizes */ if (xs->tx && !xs->pool->tx_descs) { - err = xp_alloc_tx_descs(xs->pool, xs); + err = xp_alloc_tx_descs(xs->pool, xs, + dev->xdp_zc_max_segs); if (err) { xp_put_pool(xs->pool); xs->pool = NULL; @@ -1575,7 +1577,9 @@ static int xsk_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr goto out_unlock; } else { /* This xsk has its own umem. */ - xs->pool = xp_create_and_assign_umem(xs, xs->umem); + xs->pool = xp_create_and_assign_umem(xs, xs->umem, + dev->xdp_zc_max_segs); + if (!xs->pool) { err = -ENOMEM; goto out_unlock; diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index 1f28a9641571..12c9fb29af05 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -42,9 +42,12 @@ void xp_destroy(struct xsk_buff_pool *pool) kvfree(pool); } -int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs) +int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs, + u32 max_segs) { - pool->tx_descs = kvzalloc_objs(*pool->tx_descs, xs->tx->nentries); + u32 nentries = max(xs->tx->nentries, max_segs); + + pool->tx_descs = kvzalloc_objs(*pool->tx_descs, nentries); if (!pool->tx_descs) return -ENOMEM; @@ -52,7 +55,8 @@ int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs) } struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, - struct xdp_umem *umem) + struct xdp_umem *umem, + u32 max_segs) { bool unaligned = umem->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG; struct xsk_buff_pool *pool; @@ -69,7 +73,7 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs, goto out; if (xs->tx) - if (xp_alloc_tx_descs(pool, xs)) + if (xp_alloc_tx_descs(pool, xs, max_segs)) goto out; pool->chunk_mask = ~((u64)umem->chunk_size - 1); -- cgit v1.2.3 From 72f2b4516faf55d4dfac2414649d3cffa5fd2c5e Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Sun, 19 Jul 2026 15:56:07 +0200 Subject: xsk: reclaim invalid Tx descriptors in ZC batch path The zero-copy Tx batch parser stops when it encounters an invalid descriptor. If this happens after one or more continuation descriptors, the Tx consumer can be advanced past fragments that are neither submitted to the driver nor returned to userspace through the completion ring. A similar problem occurs when a packet exceeds xdp_zc_max_segs. The descriptors consumed up to the limit are released without completion, and the remaining continuation descriptors can subsequently be interpreted as the beginning of another packet. Parse Tx batches in packet units and distinguish descriptors belonging to complete valid packets from descriptors consumed while draining an invalid or oversized packet. Return the former to the driver and append the latter to the CQ address area so userspace can reclaim their UMEM frames. Treat a standalone invalid descriptor as a one-descriptor reclaim-only packet. Advancing the Tx-ring consumer releases the ring slot, but does not by itself return ownership of the referenced UMEM frame to userspace. Once draining starts, continue until the packet's end-of-packet descriptor is consumed. Preserve the drain state on the socket when EOP has not yet been supplied, so draining can continue during a later call. Leave incomplete but otherwise valid packets on the Tx ring. Shared-UMEM pools using multi-buffer Tx also need packet-framed parsing. Walk their Tx sockets one packet at a time, preserving the existing per-socket fairness scheme, instead of using the legacy one-descriptor fallback. Keep that fallback for shared pools that do not use multi-buffer Tx. Since the drain state is maintained per socket and both the singular and shared paths can resume an interrupted drain, changing the socket list from singular to shared requires no special bind-time transition. CQ entries are positional, and drivers may complete only part of the Tx work returned by xsk_tx_peek_release_desc_batch(). Therefore, reclaim-only entries cannot be published immediately when earlier driver-visible descriptors are still outstanding. Track the number of driver-visible CQ entries preceding the reclaim entries. Let xsk_tx_completed() publish partial hardware Tx completions, and publish the reclaim entries only after every earlier Tx descriptor has completed. Complete a reclaim-only batch immediately when there is no driver-visible work in front of it, and prevent another Tx batch from being appended while reclaim entries remain pending. Also cap batch processing by the size of the pool's temporary descriptor array, as Tx rings belonging to sockets sharing a UMEM may have different sizes. This ensures that every invalid Tx descriptor consumed by the ZC batch path is either submitted to the driver as part of a valid packet or returned to userspace without violating CQ completion ordering. Reviewed-by: Jason Xing Signed-off-by: Maciej Fijalkowski Acked-by: Stanislav Fomichev Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path") Link: https://patch.msgid.link/20260719135609.147823-5-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- Documentation/networking/af_xdp.rst | 54 ++++++----- include/net/xsk_buff_pool.h | 3 + net/xdp/xsk.c | 187 ++++++++++++++++++++++++++++++++---- net/xdp/xsk_buff_pool.c | 1 + net/xdp/xsk_queue.h | 65 +++++++++---- 5 files changed, 248 insertions(+), 62 deletions(-) diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst index 50d92084a49c..cc3f0d16b28f 100644 --- a/Documentation/networking/af_xdp.rst +++ b/Documentation/networking/af_xdp.rst @@ -43,12 +43,13 @@ UMEM also has two rings: the FILL ring and the COMPLETION ring. The FILL ring is used by the application to send down addr for the kernel to fill in with RX packet data. References to these frames will then appear in the RX ring once each packet has been received. The -COMPLETION ring, on the other hand, contains frame addr that the -kernel has transmitted completely and can now be used again by user -space, for either TX or RX. Thus, the frame addrs appearing in the -COMPLETION ring are addrs that were previously transmitted using the -TX ring. In summary, the RX and FILL rings are used for the RX path -and the TX and COMPLETION rings are used for the TX path. +COMPLETION ring, on the other hand, contains frame addresses from Tx +descriptors that the kernel has finished processing and that can now be +used again by user space, for either Tx or Rx. This includes frames whose +transmission has completed as well as frames referenced by invalid Tx +descriptors rejected by the kernel. A completion therefore returns +ownership of a frame to user space, but does not by itself guarantee that +the packet was successfully transmitted. The socket is then finally bound with a bind() call to a device and a specific queue id on that device, and it is not until bind is @@ -169,14 +170,15 @@ chunks mode, then the incoming addr will be left untouched. UMEM Completion Ring ~~~~~~~~~~~~~~~~~~~~ -The COMPLETION Ring is used transfer ownership of UMEM frames from +The COMPLETION Ring is used to transfer ownership of UMEM frames from kernel-space to user-space. Just like the FILL ring, UMEM indices are -used. - -Frames passed from the kernel to user-space are frames that has been -sent (TX ring) and can be used by user-space again. - -The user application consumes UMEM addrs from this ring. +used. Frames passed from the kernel to user-space are frames referenced +by Tx descriptors that the kernel has finished processing and can be +used by user-space again. This includes both frames whose transmission +has completed and frames referenced by invalid Tx descriptors that were +rejected and reclaimed by the kernel. A completion entry does not +guarantee successful packet transmission. The user application consumes +UMEM addrs from this ring. RX Ring @@ -504,21 +506,25 @@ will be treated as an invalid descriptor. These are the semantics for producing packets onto AF_XDP Tx ring consisting of multiple frames: -* When an invalid descriptor is found, all the other - descriptors/frames of this packet are marked as invalid and not - completed. The next descriptor is treated as the start of a new - packet, even if this was not the intent (because we cannot guess - the intent). As before, if your program is producing invalid - descriptors you have a bug that must be fixed. +* When an invalid descriptor is found, the complete packet is treated as + invalid. The kernel consumes descriptors through the descriptor marking + the end of the packet and returns all their frame addresses through the + COMPLETION ring. A standalone invalid descriptor is treated as a + one-descriptor invalid packet. The descriptor following the end of the + invalid packet is treated as the start of a new packet. As before, if + your program is producing invalid descriptors you have a bug that must + be fixed. Rejected descriptors are reported in the ``tx_invalid_descs`` + statistic. * Zero length descriptors are treated as invalid descriptors. * For copy mode, the maximum supported number of frames in a packet is - equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all - descriptors accumulated so far are dropped and treated as - invalid. To produce an application that will work on any system - regardless of this config setting, limit the number of frags to 18, - as the minimum value of the config is 17. + equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all descriptors + through the end of the oversized packet are consumed, treated as invalid, + and their frame addresses are returned through the COMPLETION ring. To + produce an application that will work on any system regardless of this + config setting, limit the number of frags to 18, as the minimum value of + the config is 17. * For zero-copy mode, the limit is up to what the NIC HW supports. Usually at least five on the NICs we have checked. We diff --git a/include/net/xsk_buff_pool.h b/include/net/xsk_buff_pool.h index f5e737a83055..2bb1d122b1bc 100644 --- a/include/net/xsk_buff_pool.h +++ b/include/net/xsk_buff_pool.h @@ -78,6 +78,9 @@ struct xsk_buff_pool { u32 chunk_size; u32 chunk_shift; u32 frame_len; + u32 tx_descs_nentries; + u32 reclaim_descs; + u32 tx_zc_pending_descs; u32 xdp_zc_max_segs; u8 tx_metadata_len; /* inherited from umem */ u8 cached_need_wakeup; diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 091792d1d82d..f906d51b6699 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -499,6 +499,23 @@ void __xsk_map_flush(struct list_head *flush_list) void xsk_tx_completed(struct xsk_buff_pool *pool, u32 nb_entries) { + u32 reclaim_descs = READ_ONCE(pool->reclaim_descs); + + if (unlikely(reclaim_descs)) { + u32 pending_descs = READ_ONCE(pool->tx_zc_pending_descs); + + if (nb_entries < pending_descs) { + WRITE_ONCE(pool->tx_zc_pending_descs, + pending_descs - nb_entries); + xskq_prod_submit_n(pool->cq, nb_entries); + return; + } + + WRITE_ONCE(pool->tx_zc_pending_descs, 0); + nb_entries += reclaim_descs; + WRITE_ONCE(pool->reclaim_descs, 0); + } + xskq_prod_submit_n(pool->cq, nb_entries); } EXPORT_SYMBOL(xsk_tx_completed); @@ -574,24 +591,157 @@ static u32 xsk_tx_peek_release_fallback(struct xsk_buff_pool *pool, u32 max_entr return nb_pkts; } +static void xsk_tx_commit_batch(struct xsk_buff_pool *pool, + struct xsk_tx_batch *batch) +{ + u32 nb_descs = xsk_tx_batch_cq_descs(batch); + u32 cq_cached_prod; + + if (!nb_descs) + return; + + cq_cached_prod = pool->cq->cached_prod; + xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_descs); + + if (unlikely(batch->reclaim_descs)) { + u32 cq_pending_descs; + + /* CQ is positional. Descriptors already written but not + * submitted must complete before any reclaim-only descriptors + * appended below. + */ + cq_pending_descs = cq_cached_prod - xskq_get_prod(pool->cq); + + WRITE_ONCE(pool->tx_zc_pending_descs, + batch->tx_descs + cq_pending_descs); + WRITE_ONCE(pool->reclaim_descs, batch->reclaim_descs); + if (unlikely(!pool->tx_zc_pending_descs)) + xsk_tx_completed(pool, 0); + } +} + +static struct xsk_tx_batch +__xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, struct xdp_sock *xs, + struct xdp_desc *descs, u32 max_descs) +{ + struct xsk_tx_batch batch = {}; + u32 entries; + + entries = xskq_cons_nb_entries(xs->tx, max_descs); + if (!entries) + return batch; + + batch = xskq_cons_read_desc_batch(xs, pool, descs, max_descs); + if (!xsk_tx_batch_cq_descs(&batch)) { + xs->tx->queue_empty_descs++; + } else { + __xskq_cons_release(xs->tx); + xs->sk.sk_write_space(&xs->sk); + } + return batch; +} + +static struct xsk_tx_batch +xsk_tx_peek_release_shared_desc_batch(struct xsk_buff_pool *pool, u32 max_descs) +{ + u32 cq_descs_before, cq_descs_after; + struct xsk_tx_batch sum_batch = {}; + bool budget_exhausted; + u32 per_socket_budget; + struct xdp_sock *xs; + + /* The fairness quota must allow one maximum-sized valid packet. */ + per_socket_budget = max_t(u32, MAX_PER_SOCKET_BUDGET, + pool->xdp_zc_max_segs); + +again: + budget_exhausted = false; + cq_descs_before = xsk_tx_batch_cq_descs(&sum_batch); + list_for_each_entry_rcu(xs, &pool->xsk_tx_list, tx_list) { + u32 budget, budget_left, offset, remaining, used; + struct xsk_tx_batch curr_batch; + + /* Once reclaim-only descriptors have been appended to the CQ + * address area, do not append driver-visible Tx descriptors + * from another socket after them. xsk_tx_completed() relies on + * all driver-visible descriptors preceding all reclaim-only + * descriptors in CQ order. + */ + if (sum_batch.reclaim_descs) + break; + + /* be gentle when playing with pool->tx_descs */ + offset = xsk_tx_batch_cq_descs(&sum_batch); + if (offset >= max_descs) + break; + + if (xs->tx_budget_spent >= per_socket_budget) { + if (xskq_cons_nb_entries(xs->tx, 1)) + budget_exhausted = true; + continue; + } + + budget_left = per_socket_budget - xs->tx_budget_spent; + remaining = max_descs - offset; + budget = min(remaining, budget_left); + + curr_batch = __xsk_tx_peek_release_desc_batch(pool, xs, + pool->tx_descs + offset, + budget); + used = xsk_tx_batch_cq_descs(&curr_batch); + if (!used) { + if (curr_batch.budget_limited && budget_left < remaining) + budget_exhausted = true; + continue; + } + + xs->tx_budget_spent += used; + sum_batch.tx_descs += curr_batch.tx_descs; + sum_batch.reclaim_descs = curr_batch.reclaim_descs; + } + + cq_descs_after = xsk_tx_batch_cq_descs(&sum_batch); + + if (sum_batch.reclaim_descs || cq_descs_after >= max_descs) + return sum_batch; + + /* Continue filling the batch while this pass made progress */ + if (cq_descs_before != cq_descs_after) + goto again; + + if (!budget_exhausted) + return sum_batch; + + list_for_each_entry_rcu(xs, &pool->xsk_tx_list, tx_list) + xs->tx_budget_spent = 0; + goto again; +} + u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts) { + struct xsk_tx_batch batch = {}; struct xdp_sock *xs; + bool umem_shared; rcu_read_lock(); - if (!list_is_singular(&pool->xsk_tx_list)) { - /* Fallback to the non-batched version */ - rcu_read_unlock(); - return xsk_tx_peek_release_fallback(pool, nb_pkts); - } + if (unlikely(READ_ONCE(pool->reclaim_descs))) + goto out; - xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock, tx_list); - if (!xs) { - nb_pkts = 0; + xs = list_first_or_null_rcu(&pool->xsk_tx_list, struct xdp_sock, + tx_list); + if (!xs) goto out; - } - nb_pkts = xskq_cons_nb_entries(xs->tx, nb_pkts); + nb_pkts = min(nb_pkts, pool->tx_descs_nentries); + if (!nb_pkts) + goto out; + + umem_shared = !list_is_singular(&pool->xsk_tx_list); + + if (umem_shared && !(pool->umem->flags & XDP_UMEM_SG_FLAG)) { + rcu_read_unlock(); + return xsk_tx_peek_release_fallback(pool, nb_pkts); + } /* This is the backpressure mechanism for the Tx path. Try to * reserve space in the completion queue for all packets, but @@ -603,19 +753,16 @@ u32 xsk_tx_peek_release_desc_batch(struct xsk_buff_pool *pool, u32 nb_pkts) if (!nb_pkts) goto out; - nb_pkts = xskq_cons_read_desc_batch(xs->tx, pool, nb_pkts); - if (!nb_pkts) { - xs->tx->queue_empty_descs++; - goto out; - } - - __xskq_cons_release(xs->tx); - xskq_prod_write_addr_batch(pool->cq, pool->tx_descs, nb_pkts); - xs->sk.sk_write_space(&xs->sk); + batch = umem_shared ? + xsk_tx_peek_release_shared_desc_batch(pool, nb_pkts) : + __xsk_tx_peek_release_desc_batch(pool, xs, + pool->tx_descs, + nb_pkts); + xsk_tx_commit_batch(pool, &batch); out: rcu_read_unlock(); - return nb_pkts; + return batch.tx_descs; } EXPORT_SYMBOL(xsk_tx_peek_release_desc_batch); diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index 12c9fb29af05..a4089480b22b 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -51,6 +51,7 @@ int xp_alloc_tx_descs(struct xsk_buff_pool *pool, struct xdp_sock *xs, if (!pool->tx_descs) return -ENOMEM; + pool->tx_descs_nentries = nentries; return 0; } diff --git a/net/xdp/xsk_queue.h b/net/xdp/xsk_queue.h index 3e3fbb73d23e..1bc42c8902f4 100644 --- a/net/xdp/xsk_queue.h +++ b/net/xdp/xsk_queue.h @@ -58,6 +58,17 @@ struct parsed_desc { u32 valid; }; +struct xsk_tx_batch { + u32 tx_descs; + u32 reclaim_descs; + bool budget_limited; +}; + +static inline u32 xsk_tx_batch_cq_descs(const struct xsk_tx_batch *batch) +{ + return batch->tx_descs + batch->reclaim_descs; +} + /* The structure of the shared state of the rings are a simple * circular buffer, as outlined in * Documentation/core-api/circular-buffers.rst. For the Rx and @@ -263,17 +274,18 @@ static inline void parse_desc(struct xsk_queue *q, struct xsk_buff_pool *pool, parsed->mb = xp_mb_desc(desc); } -static inline -u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool, - u32 max) +static inline struct xsk_tx_batch +xskq_cons_read_desc_batch(struct xdp_sock *xs, struct xsk_buff_pool *pool, + struct xdp_desc *descs, u32 max) { - u32 cached_cons = q->cached_cons, nb_entries = 0; - struct xdp_desc *descs = pool->tx_descs; - u32 total_descs = 0, nr_frags = 0; + bool drain = READ_ONCE(xs->drain_cont); + u32 cached_cons, nb_entries = 0; + struct xsk_tx_batch batch = {}; + struct xsk_queue *q = xs->tx; + u32 nr_frags = 0; + + cached_cons = q->cached_cons; - /* track first entry, if stumble upon *any* invalid descriptor, rewind - * current packet that consists of frags and stop the processing - */ while (cached_cons != q->cached_prod && nb_entries < max) { struct xdp_rxtx_ring *ring = (struct xdp_rxtx_ring *)q->ring; u32 idx = cached_cons & q->ring_mask; @@ -283,25 +295,42 @@ u32 xskq_cons_read_desc_batch(struct xsk_queue *q, struct xsk_buff_pool *pool, cached_cons++; parse_desc(q, pool, &descs[nb_entries], &parsed); if (unlikely(!parsed.valid)) - break; + drain = true; + + nr_frags++; + nb_entries++; if (likely(!parsed.mb)) { - total_descs += (nr_frags + 1); - nr_frags = 0; - } else { - nr_frags++; - if (nr_frags == pool->xdp_zc_max_segs) { + if (unlikely(drain)) { + batch.reclaim_descs = nr_frags; + WRITE_ONCE(xs->drain_cont, false); nr_frags = 0; break; } + + batch.tx_descs += nr_frags; + nr_frags = 0; + continue; + } + + if (nr_frags == pool->xdp_zc_max_segs) + drain = true; + } + + if (nr_frags) { + if (drain) { + batch.reclaim_descs = nr_frags; + WRITE_ONCE(xs->drain_cont, true); + } else { + if (nb_entries == max) + batch.budget_limited = true; + cached_cons -= nr_frags; } - nb_entries++; } - cached_cons -= nr_frags; /* Release valid plus any invalid entries */ xskq_cons_release_n(q, cached_cons - q->cached_cons); - return total_descs; + return batch; } /* Functions for consumers */ -- cgit v1.2.3 From c5b1ca6a02886f00170ed91b757e244f23259e91 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Sun, 19 Jul 2026 15:56:08 +0200 Subject: selftests/xsk: fix too-many-frags multi-buffer Tx test The too-many-frags test describes a packet that is valid from the Tx ring ownership point of view, but invalid for transmission because it exceeds the supported number of fragments. Keep the generated Tx descriptors valid so that __send_pkts() accounts them as outstanding descriptors that must be reclaimed through the CQ. Then mark the corresponding Rx packet invalid so the test still does not expect the oversized packet to appear on the receive side. Add a valid synchronization packet after the oversized packet so the test can verify that the Tx path drains the bad packet and resumes at the next packet boundary. Reviewed-by: Jason Xing Signed-off-by: Maciej Fijalkowski Acked-by: Stanislav Fomichev Link: https://patch.msgid.link/20260719135609.147823-6-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/bpf/prog_tests/test_xsk.c | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c index 6eb9096d084c..de17dd48f176 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c @@ -2270,7 +2270,7 @@ int testapp_too_many_frags(struct test_spec *test) max_frags += 1; } - pkts = calloc(2 * max_frags + 2, sizeof(struct pkt)); + pkts = calloc(2 * max_frags + 3, sizeof(struct pkt)); if (!pkts) return TEST_FAILURE; @@ -2288,24 +2288,30 @@ int testapp_too_many_frags(struct test_spec *test) } pkts[max_frags].options = 0; - /* An invalid packet with the max amount of frags but signals packet - * continues on the last frag - */ - for (i = max_frags + 1; i < 2 * max_frags + 1; i++) { + /* An invalid packet with the max + 1 amount of frags */ + for (i = max_frags + 1; i < 2 * max_frags + 2; i++) { pkts[i].len = MIN_PKT_SIZE; pkts[i].options = XDP_PKT_CONTD; - pkts[i].valid = false; + pkts[i].valid = true; } + pkts[2 * max_frags + 1].options = 0; /* Valid packet for synch */ - pkts[2 * max_frags + 1].len = MIN_PKT_SIZE; - pkts[2 * max_frags + 1].valid = true; + pkts[2 * max_frags + 2].len = MIN_PKT_SIZE; + pkts[2 * max_frags + 2].valid = true; - if (pkt_stream_generate_custom(test, pkts, 2 * max_frags + 2)) { + if (pkt_stream_generate_custom(test, pkts, 2 * max_frags + 3)) { free(pkts); return TEST_FAILURE; } + /* The generated Tx stream must keep the too-big packet valid so that + * __send_pkts() accounts its descriptors in outstanding_tx. The Rx + * stream, however, must not expect this packet on the wire. + */ + test->ifobj_rx->xsk->pkt_stream->pkts[2].valid = false; + test->ifobj_rx->xsk->pkt_stream->nb_valid_entries--; + ret = testapp_validate_traffic(test); free(pkts); return ret; -- cgit v1.2.3 From f49d99eaee7c32badc7ddfaecbb01ce4d037d695 Mon Sep 17 00:00:00 2001 From: Maciej Fijalkowski Date: Sun, 19 Jul 2026 15:56:09 +0200 Subject: selftests/xsk: account reclaimed invalid Tx descriptors Invalid Tx descriptors are now returned through the completion ring, regardless of whether they form a standalone packet or belong to an invalid multi-buffer packet. The selftests previously counted only descriptors belonging to valid packets, with a special exception for some invalid multi-buffer packets in verbatim streams. This undercounts completion entries when a standalone invalid descriptor or another invalid packet is reclaimed by the kernel. Keep valid_pkts as the number of packets expected on the Rx side, but count every descriptor submitted to the Tx ring in valid_frags, as every such descriptor is now expected to be returned through the completion ring. Make fragment counting in verbatim mode follow the packet boundary instead of stopping at the first invalid fragment. Update custom stream generation so an invalid middle fragment terminates the generated Rx packet while Tx completion accounting still covers the complete invalid packet. Also add explicit end fragments after invalid middle descriptors. This exercises the kernel drain logic and verifies that subsequent valid packets are not interpreted as continuations of the invalid packet. Reviewed-by: Jason Xing Signed-off-by: Maciej Fijalkowski Acked-by: Stanislav Fomichev Link: https://patch.msgid.link/20260719135609.147823-7-maciej.fijalkowski@intel.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/bpf/prog_tests/test_xsk.c | 26 ++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c index de17dd48f176..38ce6060b8fa 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c +++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c @@ -427,14 +427,14 @@ static u32 pkt_nb_frags(u32 frame_size, struct pkt_stream *pkt_stream, struct pk } /* Search for the end of the packet in verbatim mode */ - if (!pkt_continues(pkt->options) || !pkt->valid) + if (!pkt_continues(pkt->options)) return nb_frags; next_frag = pkt_stream->current_pkt_nb; pkt++; while (next_frag++ < pkt_stream->nb_pkts) { nb_frags++; - if (!pkt_continues(pkt->options) || !pkt->valid) + if (!pkt_continues(pkt->options)) break; pkt++; } @@ -665,11 +665,11 @@ static struct pkt_stream *__pkt_stream_generate_custom(struct ifobject *ifobj, s if (!frame->valid || !pkt_continues(frame->options)) payload++; } else { - if (frame->valid) + if (frame->valid) { len += frame->len; - if (frame->valid && pkt_continues(frame->options)) - continue; - + if (pkt_continues(frame->options)) + continue; + } pkt->pkt_nb = pkt_nb; pkt->len = len; pkt->valid = frame->valid; @@ -1250,10 +1250,9 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, } } - if (pkt && pkt->valid) { + if (pkt && pkt->valid) valid_pkts++; - valid_frags += nb_frags; - } + valid_frags += nb_frags; } pthread_mutex_lock(&pacing_mutex); @@ -2099,13 +2098,16 @@ int testapp_invalid_desc_mb(struct test_spec *test) {0, 0, 0, false, 0}, /* Invalid address in the second frame */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, - {umem_sz, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {umem_sz * 2, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {0, MIN_PKT_SIZE, 0, false, 0}, /* Invalid len in the middle */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, {0, XSK_UMEM__INVALID_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, + {0, MIN_PKT_SIZE, 0, false, 0}, /* Invalid options in the middle */ {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD}, {0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XSK_DESC__INVALID_OPTION}, + {0, MIN_PKT_SIZE, 0, false, 0}, /* Transmit 2 frags, receive 3 */ {0, XSK_UMEM__MAX_FRAME_SIZE, 0, true, XDP_PKT_CONTD}, {0, XSK_UMEM__MAX_FRAME_SIZE, 0, true, 0}, @@ -2117,8 +2119,8 @@ int testapp_invalid_desc_mb(struct test_spec *test) if (umem->unaligned_mode) { /* Crossing a chunk boundary allowed */ - pkts[12].valid = true; - pkts[13].valid = true; + pkts[15].valid = true; + pkts[16].valid = true; } test->mtu = MAX_ETH_JUMBO_SIZE; -- cgit v1.2.3 From 39d6e68f50f4a444a6ba23b9ea2eaee806601c6d Mon Sep 17 00:00:00 2001 From: Nicolas Schier Date: Thu, 23 Jul 2026 12:58:45 +0200 Subject: kbuild: Stop modifying $(objtree)/Makefile when building oot-kmods oos Update output Makefile in the corresponding tree only: for out-of-source in-tree builds update $(objtree)/Makefile, for out-of-source out-of-tree module builds update $(KBUILD_EXTMOD_OUTPUT)/Makefile instead. In-source builds are not affected. Since commit c9bb03ac2c66 ("kbuild: reduce output spam when building out of tree"), building out-of-tree kernel modules out-of-source (make M=... MO=...) causes a rewrite of $(objtree)/Makefile with KBUILD_EXTMOD and KBUILD_EXTMOD_OUTPUT being set. That is problematic: * $(objtree)/ must not be changed in any way when building out-of-tree modules as it breaks other uses of $(objtree). * Setting KBUILD_EXTMOD and KBUILD_EXTMOD_OUTPUT in $(objtree)/Makefile kills the tree for incremental builds that start right there ('make -C $(objtree)'); builds starting in $(srctree) reset $(objtree)/Makefile to its original content. Further, $(KBUILD_EXTMOD_OUTPUT)/Makefile was not generated any more at all. This commit restores the previous kbuild behaviour prior to commit c9bb03ac2c66 ("kbuild: reduce output spam when building out of tree") but leaves in-place the use of filechk for output spam reduction. Fixes: c9bb03ac2c66 ("kbuild: reduce output spam when building out of tree") Reported-by: Anish Rashinkar Closes: https://lore.kernel.org/r/CAOESE2Q2-0KUDaM0mUo+c_F-tMaUsBZ-gpnhdoe0rmYdgnnuJQ@mail.gmail.com Cc: stable@vger.kernel.org Tested-by: Philipp Hahn Reviewed-by: Philipp Hahn Signed-off-by: Nicolas Schier Signed-off-by: Nicolas Schier Link: https://patch.msgid.link/20260723105845.1704689-2-nsc@kernel.org Signed-off-by: Nathan Chancellor --- Makefile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 11539c3fd405..17d34968b7f0 100644 --- a/Makefile +++ b/Makefile @@ -695,13 +695,11 @@ filechk_makefile = { \ echo "include $(abs_srctree)/Makefile"; \ } -$(objtree)/Makefile: FORCE +PHONY += $(CURDIR)/Makefile +$(CURDIR)/Makefile: FORCE $(call filechk,makefile) -# Prevent $(srcroot)/Makefile from inhibiting the rule to run. -PHONY += $(objtree)/Makefile - -outputmakefile: $(objtree)/Makefile +outputmakefile: $(CURDIR)/Makefile ifeq ($(KBUILD_EXTMOD),) @if [ -f $(srctree)/.config -o \ -d $(srctree)/include/config -o \ -- cgit v1.2.3 From 79097812153b826fc156a2930ec8a90ed9edf4a2 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 24 Jul 2026 04:05:17 +0100 Subject: tracing: Fix use-after-free freeing trigger private data Commit 61d445af0a7c ("tracing: Add bulk garbage collection of freeing event_trigger_data") moved the kfree() of event_trigger_data to a kthread that runs tracepoint_synchronize_unregister() before freeing. That removed the synchronization the trigger .free callbacks used to get implicitly and inline from trigger_data_free(). event_hist_trigger_free(), event_hist_trigger_named_free() and event_enable_trigger_free() free their satellite data (hist_data, cmd_ops, enable_data) right after trigger_data_free() returns. With the synchronization now deferred to the kthread, a concurrent tracepoint handler can still reach that data through the list_del_rcu()'d trigger, causing a use-after-free. The histogram teardown must stay synchronous: remove_hist_vars() and unregister_field_var_hists() have to detach a synthetic event from the histogram before the trigger-removal write returns, otherwise a following command races in and the synthetic-event removal fails with -EBUSY, as the trigger-synthetic-eprobe.tc selftest catches. Make those callbacks wait with the correct barrier - tracepoint_synchronize_unregister(), matching the free kthread - before freeing. The enable trigger has no such synchronous requirement, and a blocking synchronize there would re-serialize the path that commit deliberately deferred. Give it an optional private_data_free() callback that the free kthread runs after its grace period, and free enable_data from there. Link: https://patch.msgid.link/20260724030523.19081-1-devnexen@gmail.com Suggested-by: Masami Hiramatsu (Google) Suggested-by: Steven Rostedt Fixes: 61d445af0a7c ("tracing: Add bulk garbage collection of freeing event_trigger_data") Signed-off-by: David Carlier Signed-off-by: Steven Rostedt --- kernel/trace/trace.h | 1 + kernel/trace/trace_events_hist.c | 2 ++ kernel/trace/trace_events_trigger.c | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 80fe152af1dd..bf77331f56a4 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1941,6 +1941,7 @@ struct event_trigger_data { struct list_head named_list; struct event_trigger_data *named_data; struct llist_node llist; + void (*private_data_free)(struct event_trigger_data *data); }; /* Avoid typos */ diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 82ce492ab268..58d28cd1afa3 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -6349,6 +6349,7 @@ static void event_hist_trigger_free(struct event_trigger_data *data) trigger_data_free(data); + tracepoint_synchronize_unregister(); remove_hist_vars(hist_data); unregister_field_var_hists(hist_data); @@ -6388,6 +6389,7 @@ static void event_hist_trigger_named_free(struct event_trigger_data *data) del_named_trigger(data); trigger_data_free(data); + tracepoint_synchronize_unregister(); kfree(cmd_ops); } } diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index 655db2e82513..46e60b70a4bb 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -38,6 +38,13 @@ static void trigger_create_kthread_locked(void) } } +static void trigger_data_free_one(struct event_trigger_data *data) +{ + if (data->private_data_free) + data->private_data_free(data); + kfree(data); +} + static void trigger_data_free_queued_locked(void) { struct event_trigger_data *data, *tmp; @@ -52,7 +59,7 @@ static void trigger_data_free_queued_locked(void) tracepoint_synchronize_unregister(); llist_for_each_entry_safe(data, tmp, llnodes, llist) - kfree(data); + trigger_data_free_one(data); } /* Bulk garbage collection of event_trigger_data elements */ @@ -75,7 +82,7 @@ static int trigger_kthread_fn(void *ignore) tracepoint_synchronize_unregister(); llist_for_each_entry_safe(data, tmp, llnodes, llist) - kfree(data); + trigger_data_free_one(data); } return 0; @@ -1717,6 +1724,11 @@ int event_enable_trigger_print(struct seq_file *m, return 0; } +static void enable_trigger_private_data_free(struct event_trigger_data *data) +{ + kfree(data->private_data); +} + void event_enable_trigger_free(struct event_trigger_data *data) { struct enable_trigger_data *enable_data = data->private_data; @@ -1728,9 +1740,9 @@ void event_enable_trigger_free(struct event_trigger_data *data) if (!data->ref) { /* Remove the SOFT_MODE flag */ trace_event_enable_disable(enable_data->file, 0, 1); + data->private_data_free = enable_trigger_private_data_free; trace_event_put_ref(enable_data->file->event_call); trigger_data_free(data); - kfree(enable_data); } } -- cgit v1.2.3 From e091351b38818ef620d27f44f4bfd625f13afbff Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 24 Jul 2026 13:24:15 -0400 Subject: tracing: Delay module ref count for "enable_event" trigger Triggers are now delayed from freeing, but can still be triggered until after the RCU grace period has ended. The freeing of the enable_event data is put into the private_data_free() callback, but the put of the module refcount is done immediately. It is possible that if a module is removed that has an event that would enable (or disable) it is still active, it can read the data of the module after it is removed causing a use-after-free bug. Move the trace_event_put_ref() that releases the module into the delayed callback so that the module can not be removed until any reference to its events are finished. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260724132415.1b5005db@gandalf.local.home Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260724030523.19081-1-devnexen%40gmail.com Fixes: 61d445af0a7c ("tracing: Add bulk garbage collection of freeing event_trigger_data") Signed-off-by: Steven Rostedt --- kernel/trace/trace_events_trigger.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index 46e60b70a4bb..ad83419cb420 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -1726,7 +1726,10 @@ int event_enable_trigger_print(struct seq_file *m, static void enable_trigger_private_data_free(struct event_trigger_data *data) { - kfree(data->private_data); + struct enable_trigger_data *enable_data = data->private_data; + + trace_event_put_ref(enable_data->file->event_call); + kfree(enable_data); } void event_enable_trigger_free(struct event_trigger_data *data) @@ -1741,7 +1744,6 @@ void event_enable_trigger_free(struct event_trigger_data *data) /* Remove the SOFT_MODE flag */ trace_event_enable_disable(enable_data->file, 0, 1); data->private_data_free = enable_trigger_private_data_free; - trace_event_put_ref(enable_data->file->event_call); trigger_data_free(data); } } -- cgit v1.2.3 From 2c1bd78dc8e2221549409769a76c39304b82a1b0 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Tue, 14 Jul 2026 14:59:44 +0200 Subject: dpll: use pin owner's dpll ref for pin-level attribute reporting Commit c191b319f208 ("dpll: allow registering FW-identified pin with a different DPLL") relaxed dpll_pin_register() to let fwnode-identified pins register with DPLLs from a different driver. This allows, for example, the ICE driver to register a zl3073x-created pin with its TXC DPLL using ice_dpll_txclk_ops, which lack frequency_get and phase_adjust_get callbacks. After such cross-driver registration, the pin's dpll_refs xarray contains refs from both drivers. dpll_cmd_pin_get_one() calls dpll_xa_ref_dpll_first() which returns the ref with the lowest DPLL id. When the foreign DPLL (e.g. ICE TXC) has a lower id than the owner DPLL (e.g. zl3073x), the foreign ops are used for reporting. Since those ops lack callbacks like frequency_get, pin-level attributes are silently omitted from the netlink response. For example, a zl3073x output pin that should report frequency and phase-adjust shows neither: Before: # dpll pin show id 45 pin id 45: module-name: zl3073x clock-id: 3427468959636104019 board-label: 156M25_NAC0_CLKREF_SYNC package-label: OUT3 type: synce-eth-port capabilities: 0x0 phase-adjust-min: -2147483648 phase-adjust-max: 2147483647 phase-adjust-gran: 800 parent-device: ... After: # dpll pin show id 19 pin id 19: module-name: zl3073x clock-id: 15964355450360090479 board-label: 156M25_NAC0_CLKREF_SYNC package-label: OUT3 type: synce-eth-port frequency: 156250000 Hz frequency-supported: 156250000 Hz capabilities: 0x0 phase-adjust-min: -2147483648 phase-adjust-max: 2147483647 phase-adjust-gran: 800 phase-adjust: 0 parent-device: ... Fix this by: 1. Adding dpll_pin_own_dpll_ref_first() helper that returns the first ref whose DPLL matches the pin's (module, clock_id) tuple -- i.e. the DPLL from the driver that created the pin and has the complete set of ops. Return NULL if no owner ref is found. 2. Using dpll_pin_own_dpll_ref_first() in dpll_cmd_pin_get_one() with a fallback to dpll_xa_ref_dpll_first() for pin-on-pin child pins whose dpll_refs all point to a different driver's DPLLs. 3. Using dpll_pin_own_dpll_ref_first() in SET operations (dpll_pin_freq_set, dpll_pin_esync_set, dpll_pin_ref_sync_state_set, dpll_pin_phase_adj_set) returning -ENODEV if no owner ref exists. Replacing the validation loops that rejected the entire operation when any ref's ops lacked the required callback -- instead validate only the owner refs so that foreign DPLLs with incomplete ops no longer block SET operations. 4. Guarding all SET and rollback xa_for_each loops against NULL set callbacks so that foreign refs without the operation are safely skipped instead of causing a NULL pointer dereference. Fixes: c191b319f208 ("dpll: allow registering FW-identified pin with a different DPLL") Signed-off-by: Ivan Vecera Acked-by: Vadim Fedorenko Link: https://patch.msgid.link/20260714125945.1823269-1-ivecera@redhat.com Signed-off-by: Jakub Kicinski --- drivers/dpll/dpll_core.c | 27 ++++++++++++++++++++ drivers/dpll/dpll_core.h | 1 + drivers/dpll/dpll_netlink.c | 60 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c index 2e8690cb3c16..43d51d942ead 100644 --- a/drivers/dpll/dpll_core.c +++ b/drivers/dpll/dpll_core.c @@ -1142,6 +1142,33 @@ void *dpll_pin_on_pin_priv(struct dpll_pin *parent, return reg->priv; } +/** + * dpll_pin_own_dpll_ref_first - find the first owner dpll ref of a pin + * @pin: pointer to a dpll pin + * + * Search pin's dpll_refs for a ref whose dpll matches the pin's + * (module, clock_id) tuple, i.e. the dpll registered by the driver + * that created the pin. This ensures pin-level attributes are + * reported and modified using the owner's ops even when the pin is + * also registered with dplls from other drivers. + * + * Return: pointer to the owner's dpll_pin_ref, or NULL if no + * owner ref is found. + */ +struct dpll_pin_ref *dpll_pin_own_dpll_ref_first(struct dpll_pin *pin) +{ + struct dpll_pin_ref *ref; + unsigned long i; + + xa_for_each(&pin->dpll_refs, i, ref) { + if (ref->dpll->module == pin->module && + ref->dpll->clock_id == pin->clock_id) + return ref; + } + + return NULL; +} + const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref) { struct dpll_pin_registration *reg; diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h index e24577113431..da8a369556ed 100644 --- a/drivers/dpll/dpll_core.h +++ b/drivers/dpll/dpll_core.h @@ -93,6 +93,7 @@ void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin); const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll); struct dpll_device *dpll_device_get_by_id(int id); +struct dpll_pin_ref *dpll_pin_own_dpll_ref_first(struct dpll_pin *pin); const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref); struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs); extern struct xarray dpll_device_xa; diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index 5703667593a7..afb31c004038 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -699,7 +699,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin, struct dpll_pin_ref *ref; int ret; - ref = dpll_xa_ref_dpll_first(&pin->dpll_refs); + ref = dpll_pin_own_dpll_ref_first(pin); + if (!ref) + ref = dpll_xa_ref_dpll_first(&pin->dpll_refs); ASSERT_NOT_NULL(ref); ret = dpll_msg_add_pin_handle(msg, pin); @@ -1090,12 +1092,19 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a, xa_for_each(&pin->dpll_refs, i, ref) { ops = dpll_pin_ops(ref); - if (!ops->frequency_set || !ops->frequency_get) { - NL_SET_ERR_MSG(extack, "frequency set not supported by the device"); + if ((!ops->frequency_set || !ops->frequency_get) && + ref->dpll->module == pin->module && + ref->dpll->clock_id == pin->clock_id) { + NL_SET_ERR_MSG(extack, + "frequency set not supported by the device"); return -EOPNOTSUPP; } } - ref = dpll_xa_ref_dpll_first(&pin->dpll_refs); + ref = dpll_pin_own_dpll_ref_first(pin); + if (!ref) { + NL_SET_ERR_MSG(extack, "pin owner dpll not found"); + return -ENODEV; + } ops = dpll_pin_ops(ref); dpll = ref->dpll; ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll, @@ -1109,6 +1118,8 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a, xa_for_each(&pin->dpll_refs, i, ref) { ops = dpll_pin_ops(ref); + if (!ops->frequency_set) + continue; dpll = ref->dpll; ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll, dpll_priv(dpll), freq, extack); @@ -1128,6 +1139,8 @@ rollback: if (ref == failed) break; ops = dpll_pin_ops(ref); + if (!ops->frequency_set) + continue; dpll = ref->dpll; if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll, dpll_priv(dpll), old_freq, extack)) @@ -1151,13 +1164,19 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a, xa_for_each(&pin->dpll_refs, i, ref) { ops = dpll_pin_ops(ref); - if (!ops->esync_set || !ops->esync_get) { + if ((!ops->esync_set || !ops->esync_get) && + ref->dpll->module == pin->module && + ref->dpll->clock_id == pin->clock_id) { NL_SET_ERR_MSG(extack, "embedded sync feature is not supported by this device"); return -EOPNOTSUPP; } } - ref = dpll_xa_ref_dpll_first(&pin->dpll_refs); + ref = dpll_pin_own_dpll_ref_first(pin); + if (!ref) { + NL_SET_ERR_MSG(extack, "pin owner dpll not found"); + return -ENODEV; + } ops = dpll_pin_ops(ref); dpll = ref->dpll; ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll, @@ -1181,6 +1200,8 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a, void *pin_dpll_priv; ops = dpll_pin_ops(ref); + if (!ops->esync_set) + continue; dpll = ref->dpll; pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin); ret = ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll), @@ -1204,6 +1225,8 @@ rollback: if (ref == failed) break; ops = dpll_pin_ops(ref); + if (!ops->esync_set) + continue; dpll = ref->dpll; pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin); if (ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll), @@ -1238,8 +1261,11 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin, NL_SET_ERR_MSG(extack, "reference sync pin not available"); return -EINVAL; } - ref = dpll_xa_ref_dpll_first(&pin->dpll_refs); - ASSERT_NOT_NULL(ref); + ref = dpll_pin_own_dpll_ref_first(pin); + if (!ref) { + NL_SET_ERR_MSG(extack, "pin owner dpll not found"); + return -ENODEV; + } ops = dpll_pin_ops(ref); if (!ops->ref_sync_set || !ops->ref_sync_get) { NL_SET_ERR_MSG(extack, "reference sync not supported by this pin"); @@ -1258,6 +1284,8 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin, return 0; xa_for_each(&pin->dpll_refs, i, ref) { ops = dpll_pin_ops(ref); + if (!ops->ref_sync_set) + continue; dpll = ref->dpll; ret = ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin), ref_sync_pin, @@ -1280,6 +1308,8 @@ rollback: if (ref == failed) break; ops = dpll_pin_ops(ref); + if (!ops->ref_sync_set) + continue; dpll = ref->dpll; if (ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin), ref_sync_pin, @@ -1471,12 +1501,18 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr, xa_for_each(&pin->dpll_refs, i, ref) { ops = dpll_pin_ops(ref); - if (!ops->phase_adjust_set || !ops->phase_adjust_get) { + if ((!ops->phase_adjust_set || !ops->phase_adjust_get) && + ref->dpll->module == pin->module && + ref->dpll->clock_id == pin->clock_id) { NL_SET_ERR_MSG(extack, "phase adjust not supported"); return -EOPNOTSUPP; } } - ref = dpll_xa_ref_dpll_first(&pin->dpll_refs); + ref = dpll_pin_own_dpll_ref_first(pin); + if (!ref) { + NL_SET_ERR_MSG(extack, "pin owner dpll not found"); + return -ENODEV; + } ops = dpll_pin_ops(ref); dpll = ref->dpll; ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin), @@ -1491,6 +1527,8 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr, xa_for_each(&pin->dpll_refs, i, ref) { ops = dpll_pin_ops(ref); + if (!ops->phase_adjust_set) + continue; dpll = ref->dpll; ret = ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin), @@ -1513,6 +1551,8 @@ rollback: if (ref == failed) break; ops = dpll_pin_ops(ref); + if (!ops->phase_adjust_set) + continue; dpll = ref->dpll; if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll, dpll_priv(dpll), old_phase_adj, -- cgit v1.2.3 From a3729e0df005a936ceb3c2b0d167f01a2b03f970 Mon Sep 17 00:00:00 2001 From: Jonas Köppeler Date: Mon, 20 Jul 2026 23:14:52 +0200 Subject: net/sched: sch_cake: skip clearing unused tins during rate adjustment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cake_configure_rates() is called from the dequeue path with rate_adjust=true, it only needs to update the rate parameters. The loop that clears the unused tins is both unnecessary and harmful in this path: - cake_clear_tin() overwrites q->cur_tin and q->cur_flow, which are actively used by cake_dequeue(), corrupting the dequeue state. - iterating over the unused tins and their internal queues to purge packets adds needless overhead to the hot path. Skip the entire loop when rate_adjust is set, as neither cake_clear_tin() nor the mtu_time update are needed when only the rate changes. The clearing loop runs on every rate adjustment from the dequeue path, clearing (max_tins - cur_tins) tins each time, so the cost grows the fewer tins the configured mode actually uses. Testing cake_mq over veth (8 rx/tx queues, 2 Gbit limit) with flent's [1] rrul and tcp_nup tests and 32 TCP upstreams shows a large drop in loaded latency and a throughput gain, restoring behaviour to pre-15c2715a5264 levels: +------------+------+------+-------+-------+---------+ | kernel | mode | test | base | load | tput | | | | | (ms) | (ms) | (Mbit) | +------------+------+------+-------+-------+---------+ | net-next | be | rrul | 0.810 | 11.78 | 1469.67 | | net-next | be | nup | 0.637 | 85.71 | 1243.15 | | net-next | ds3 | rrul | 0.397 | 15.28 | 1770.06 | | net-next | ds3 | nup | 0.351 | 15.98 | 1799.39 | +------------+------+------+-------+-------+---------+ | patched | be | rrul | 0.092 | 0.56 | 1873.40 | | patched | be | nup | 0.109 | 1.82 | 1869.12 | | patched | ds3 | rrul | 0.097 | 0.98 | 1866.10 | | patched | ds3 | nup | 0.101 | 0.51 | 1861.79 | +------------+------+------+-------+-------+---------+ The same trend holds on real hardware (IPQ8074A, 4 rx/tx queues, OpenWrt): in besteffort mode the tcp_nup loaded latency drops from ~470 ms to ~4 ms. [1] https://flent.org Fixes: 15c2715a5264 ("net/sched: sch_cake: fixup cake_mq rate adjustment for diffserv config") Signed-off-by: Jonas Köppeler Tested-by: Mike Pham Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260720-sch_cake-skip-clearing-tins-v2-1-e6a8b0275c73@tu-berlin.de Signed-off-by: Jakub Kicinski --- net/sched/sch_cake.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index 505f63fecf64..f64be54ead49 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -2609,9 +2609,11 @@ static void cake_configure_rates(struct Qdisc *sch, u64 rate, bool rate_adjust) break; } - for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) { - cake_clear_tin(sch, c); - qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time; + if (!rate_adjust) { + for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) { + cake_clear_tin(sch, c); + qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time; + } } qd->rate_ns = qd->tins[ft].tin_rate_ns; -- cgit v1.2.3 From 817ff6efdb7f484ea547218e11e17d8e43daa3b4 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Sun, 19 Jul 2026 22:57:40 +0800 Subject: net: pktgen: fix proc entry use-after-free pktgen_change_name() replaces pkt_dev->entry while holding t->if_lock. pktgen_remove_device() removes the same entry before _rem_dev_from_if_list() takes that lock. This allows the following interleaving: CPU 0 (NETDEV_CHANGENAME) CPU 1 (kpktgend) if_lock(t) proc_remove(pkt_dev->entry) proc_remove(pkt_dev->entry) pkt_dev->entry = proc_create_data(...) if_unlock(t) The kthread can pass the stale proc_dir_entry to proc_remove() after the rename path has freed it. A reproducer with a widened race window reports: BUG: KASAN: slab-use-after-free in proc_remove+0x78/0x80 Read of size 8 at addr ffff8881478fea70 by task kpktgend_0/67 Call Trace: proc_remove+0x78/0x80 pktgen_remove_device.isra.0+0x11c/0x4c0 pktgen_thread_worker+0x1214/0x6bc0 kthread+0x2c6/0x3b0 Allocated by task 95: __proc_create+0x204/0x790 proc_create_data+0x72/0xe0 pktgen_thread_write+0xd61/0x1510 Freed by task 28: kmem_cache_free+0xcb/0x3d0 proc_free_inode+0x5b/0x80 rcu_core+0x50a/0x1850 The buggy address belongs to the object at ffff8881478fea00 which belongs to the cache proc_dir_entry of size 192 Move proc_remove() into the if_lock-protected list removal helper. Keep it before list_del_rcu() to preserve the ordering required by add_device(). The rename path must then finish replacing the entry before removal, or it observes that the device is no longer on the list. Fixes: 39df232f1a9b ("[PKTGEN]: fix device name handling") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260719145740.2888967-1-nicoyip.dev@gmail.com Signed-off-by: Jakub Kicinski --- net/core/pktgen.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 8e185b318288..ee64f3012321 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3972,6 +3972,7 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t, struct pktgen_dev *p; if_lock(t); + proc_remove(pkt_dev->entry); list_for_each_safe(q, n, &t->if_list) { p = list_entry(q, struct pktgen_dev, list); if (p == pkt_dev) @@ -4001,9 +4002,6 @@ static int pktgen_remove_device(struct pktgen_thread *t, * list to determine if interface already exist, avoid race * with proc_create_data() */ - proc_remove(pkt_dev->entry); - - /* And update the thread if_list */ _rem_dev_from_if_list(t, pkt_dev); #ifdef CONFIG_XFRM -- cgit v1.2.3 From 7d5c576cb1c86047b1fcb1aa9532e17fc5e46c1d Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Sat, 18 Jul 2026 16:40:32 +0900 Subject: Input: cs40l50-vibra - validate custom data from user space cs40l50_add() copies the custom data of an FF_PERIODIC/FF_CUSTOM effect straight from the ff_effect the user passed to EVIOCSFF, without requiring it to hold anything: work_data.custom_data = memdup_array_user(periodic->custom_data, periodic->custom_len, sizeof(s16)); work_data.custom_len = periodic->custom_len; The driver then reads two words out of that buffer: custom_data[0] as the waveform bank in cs40l50_effect_bank_set(), and custom_data[1] as the index within the bank in cs40l50_effect_index_set(). Neither read is covered by a length check, and custom_len is fully user controlled: - custom_len == 0 makes memdup_array_user() call memdup_user() with a length of zero, which returns ZERO_SIZE_PTR rather than an error, so custom_data[0] dereferences it. - custom_len == 1 allocates two bytes. A bank of ROM or RAM keeps effect->type out of the OWT case, and custom_data[1] is then read one word past the allocation. The bank value itself is also mishandled. It is masked with CS40L50_CUSTOM_DATA_MASK (0xffff) but stored in an s16, so a custom_data[0] of 0x8000 or above wraps to a negative value that passes the "bank_type >= CS40L50_WVFRM_BANK_NUM" test. cs40l50_effect_index_set() indexes vib->dsp.banks[] with it before the switch statement's default case gets a chance to reject it: base_index = vib->dsp.banks[effect->type].base_index; max_index = vib->dsp.banks[effect->type].max_index; Require the two words the driver reads to be present, and hold the masked bank in a u32 so the existing upper-bound test covers the whole range. The da7280 haptic driver already range checks custom_len this way. Fixes: c38fe1bb5d21 ("Input: cs40l50 - Add support for the CS40L50 haptic driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260718074032.1864861-1-sammiee5311@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/cs40l50-vibra.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/cs40l50-vibra.c b/drivers/input/misc/cs40l50-vibra.c index 996d6c38cca4..7ef4534fea2f 100644 --- a/drivers/input/misc/cs40l50-vibra.c +++ b/drivers/input/misc/cs40l50-vibra.c @@ -139,10 +139,10 @@ static struct cs40l50_effect *cs40l50_find_effect(int id, struct list_head *effe static int cs40l50_effect_bank_set(struct cs40l50_work *work_data, struct cs40l50_effect *effect) { - s16 bank_type = work_data->custom_data[0] & CS40L50_CUSTOM_DATA_MASK; + u32 bank_type = work_data->custom_data[0] & CS40L50_CUSTOM_DATA_MASK; if (bank_type >= CS40L50_WVFRM_BANK_NUM) { - dev_err(work_data->vib->dev, "Invalid bank (%d)\n", bank_type); + dev_err(work_data->vib->dev, "Invalid bank (%u)\n", bank_type); return -EINVAL; } @@ -326,6 +326,12 @@ static int cs40l50_add(struct input_dev *dev, struct ff_effect *effect, return -EINVAL; } + if (periodic->custom_len < CS40L50_OWT_CUSTOM_DATA_SIZE) { + dev_err(vib->dev, "Invalid custom data length (%u)\n", + periodic->custom_len); + return -EINVAL; + } + work_data.custom_data = memdup_array_user(effect->u.periodic.custom_data, effect->u.periodic.custom_len, sizeof(s16)); -- cgit v1.2.3 From d0d6415963040c401e7a7e4e482a698ba52448cb Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Wed, 22 Jul 2026 20:19:25 +0100 Subject: veth: convert frag_list skbs before running XDP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A frag_list skb can reach veth with data_len set but nr_frags zero. veth_convert_skb_to_xdp_buff() only converts skbs that are shared, locked, have frags[], or do not have enough headroom. It later uses skb_is_nonlinear() to decide whether to set XDP_FLAGS_HAS_FRAGS and xdp_frags_size. That exposes frag_list data to XDP as if it were stored in frags[], but frags[] is empty. AF_XDP copy mode can then trust the bogus XDP fragment metadata, walk an empty fragment entry, and crash in memcpy() from __xsk_rcv(). Route non-linear skbs through skb_pp_cow_data() before exposing them to XDP, and only advertise XDP frags when the resulting skb has frags[]. skb_copy_bits() already handles frag_list input, and skb_pp_cow_data() builds frags[] output with skb_add_rx_frag(), which is the representation XDP multi-buffer expects. Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb") Cc: stable@vger.kernel.org Signed-off-by: Matt Fleming Reviewed-by: Toke Høiland-Jørgensen Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260722191925.2192070-1-matt@readmodwrite.com Signed-off-by: Jakub Kicinski --- drivers/net/veth.c | 4 ++-- net/core/skbuff.c | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 1c5142149175..00e34afd858e 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -756,7 +756,7 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq, u32 frame_sz; if (skb_shared(skb) || skb_head_is_locked(skb) || - skb_shinfo(skb)->nr_frags || + skb_is_nonlinear(skb) || skb_headroom(skb) < XDP_PACKET_HEADROOM) { if (skb_pp_cow_data(rq->page_pool, pskb, XDP_PACKET_HEADROOM)) goto drop; @@ -771,7 +771,7 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq, xdp_prepare_buff(xdp, skb->head, skb_headroom(skb), skb_headlen(skb), true); - if (skb_is_nonlinear(skb)) { + if (skb_shinfo(skb)->nr_frags) { skb_shinfo(skb)->xdp_frags_size = skb->data_len; xdp_buff_set_frags_flag(xdp); } else { diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 18dabb4e9cfa..ba3dbac80fb4 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -927,6 +927,18 @@ static void skb_clone_fraglist(struct sk_buff *skb) skb_get(list); } +/** + * skb_pp_cow_data() - copy skb data into page-pool backed storage + * @pool: page pool to allocate from + * @pskb: pointer to skb pointer, replaced with the copied skb on success + * @headroom: headroom to reserve in the copied skb + * + * skb_copy_bits() handles both frags[] and frag_list input. If the copied + * skb remains non-linear, it uses frags[], which is the representation used + * by XDP multi-buffer. + * + * Return: 0 on success or a negative errno on failure. + */ int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb, unsigned int headroom) { @@ -936,12 +948,6 @@ int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb, int err, i, head_off; void *data; - /* XDP does not support fraglist so we need to linearize - * the skb. - */ - if (skb_has_frag_list(skb)) - return -EOPNOTSUPP; - max_head_size = SKB_WITH_OVERHEAD(PAGE_SIZE - headroom); if (skb->len > max_head_size + MAX_SKB_FRAGS * PAGE_SIZE) return -ENOMEM; -- cgit v1.2.3 From 732f38c36059e68ba3b4b89c56911d777fd3185c Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 24 Jul 2026 18:42:06 -0700 Subject: Input: psxpad-spi - set driver data before use psxpad_spi_suspend() retrieves the controller state with spi_get_drvdata(), but probe never stores it, so suspend dereferences a NULL pointer. Store it during probe. Fixes: 8be193c7b1f4 ("Input: add support for PlayStation 1/2 joypads connected via SPI") Signed-off-by: Linmao Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260721055551.1714965-1-lilinmao@kylinos.cn Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/psxpad-spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c index f902a56d011f..aac77a2cfe46 100644 --- a/drivers/input/joystick/psxpad-spi.c +++ b/drivers/input/joystick/psxpad-spi.c @@ -373,6 +373,7 @@ static int psxpad_spi_probe(struct spi_device *spi) return err; } + spi_set_drvdata(spi, pad); pm_runtime_enable(&spi->dev); return 0; -- cgit v1.2.3 From 7720b63bcef3f54c7fe288774b720a227d54a306 Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Sat, 25 Jul 2026 02:47:21 +0000 Subject: ftrace: Add global mutex to serialize trace_parser access In ftrace, the trace_parser structure is allocated and initialized when a trace file is opened, and is subsequently used across write and release handlers to parse user input. The affected handler paths and their specific functions are: - Open paths: ftrace_regex_open(), ftrace_graph_open() - Write paths: ftrace_regex_write(), ftrace_graph_write() - Release paths: ftrace_regex_release(), ftrace_graph_release() If userspace opens a trace file descriptor and shares it across multiple threads, concurrent write calls will race on the parser's internal state, specifically the 'idx', 'cont', and 'buffer' fields, leading to corrupted input or undefined behavior. Fix this by adding a global mutex, parser_lock, to serialize all access to trace_parser across write and release paths, preventing concurrent corruption of parser state. Fixes: e704eff3ff51 ("ftrace: Have set_graph_function handle multiple functions in one write") Fixes: 689fd8b65d66 ("tracing: trace parser support for function and graph") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260725024721.1983675-1-wutengda@huaweicloud.com Signed-off-by: Tengda Wu Signed-off-by: Steven Rostedt --- kernel/trace/ftrace.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f93e34dd2328..6c47a94f5924 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1097,6 +1097,12 @@ struct ftrace_ops global_ops = { FTRACE_OPS_FL_PID, }; +/* + * parser_lock - Protects trace_parser state against concurrent operations. + * Held across trace_get_user() and subsequent buffer parsing to prevent races. + */ +static DEFINE_MUTEX(parser_lock); + /* * Used by the stack unwinder to know about dynamic ftrace trampolines. */ @@ -5842,6 +5848,8 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, /* iter->hash is a local copy, so we don't need regex_lock */ parser = &iter->parser; + + guard(mutex)(&parser_lock); read = trace_get_user(parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) && @@ -6984,12 +6992,14 @@ int ftrace_regex_release(struct inode *inode, struct file *file) iter = file->private_data; parser = &iter->parser; + mutex_lock(&parser_lock); if (trace_parser_loaded(parser)) { int enable = !(iter->flags & FTRACE_ITER_NOTRACE); ftrace_process_regex(iter, parser->buffer, parser->idx, enable); } + mutex_unlock(&parser_lock); trace_parser_put(parser); @@ -7321,10 +7331,12 @@ ftrace_graph_release(struct inode *inode, struct file *file) parser = &fgd->parser; + mutex_lock(&parser_lock); if (trace_parser_loaded((parser))) { ret = ftrace_graph_set_hash(fgd->new_hash, parser->buffer); } + mutex_unlock(&parser_lock); trace_parser_put(parser); @@ -7437,6 +7449,7 @@ ftrace_graph_write(struct file *file, const char __user *ubuf, parser = &fgd->parser; + guard(mutex)(&parser_lock); read = trace_get_user(parser, ubuf, cnt, ppos); if (read >= 0 && trace_parser_loaded(parser) && -- cgit v1.2.3 From 2c2b322acdcc78575b8d6afa64a085cf92e03c12 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 24 Jul 2026 19:32:10 -0400 Subject: tracing: perf: Fix stale head for perf syscall tracing The code that can read the user space parameters of a system call may enable preemption and migrate. The head of the per CPU perf events list may be pointing to the wrong CPU event if the code migrates the task. Reassign the head pointer if the system call event called the code that may have caused a migration. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260724193210.03fae1d6@gandalf.local.home Reported-by: Sashiko <> Link: https://sashiko.dev/#/patchset/20260717173252.3431565-1-usama.arif%40linux.dev Fixes: edca33a56297d ("tracing: Fix failure to read user space from system call trace events") Signed-off-by: Steven Rostedt --- kernel/trace/trace_syscalls.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index e98ee7e1e66f..8a4f3c75e39f 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -1451,6 +1451,11 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) if (syscall_get_data(sys_data, args, &user_ptr, &size, user_sizes, &uargs, buf_size) < 0) return; + + /* The above may have caused a migration */ + head = this_cpu_ptr(sys_data->enter_event->perf_events); + if (hlist_empty(head)) + return; } /* get the size after alignment with the u32 buffer size field */ -- cgit v1.2.3 From 5751c781d3c97ab6ce0e2a966156ed882152c415 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 24 Jul 2026 20:46:27 -0700 Subject: Input: iforce - validate input packet lengths iforce_process_packet() reads fixed fields from joystick, wheel and status packets without first checking their lengths. In particular, the shared hats-and-buttons helper unconditionally reads data[6]. The status tail is a sequence of 16-bit effect addresses, but an incomplete final address is also consumed. A successful zero-length USB URB additionally reads the packet ID before the common parser is called. Reject the zero-length USB transfer, require the seven-byte joystick and wheel prefixes and the two-byte status prefix, and consume only complete status-tail addresses. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260720115018.75045-1-pengpeng@iscas.ac.cn Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/iforce/iforce-packets.c | 11 ++++++++++- drivers/input/joystick/iforce/iforce-usb.c | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index effa76bfd8f9..01fee14054fb 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -155,6 +155,9 @@ void iforce_process_packet(struct iforce *iforce, switch (packet_id) { case 0x01: /* joystick position data */ + if (len < 7) + break; + input_report_abs(dev, ABS_X, (__s16) get_unaligned_le16(data)); input_report_abs(dev, ABS_Y, @@ -170,6 +173,9 @@ void iforce_process_packet(struct iforce *iforce, break; case 0x03: /* wheel position data */ + if (len < 7) + break; + input_report_abs(dev, ABS_WHEEL, (__s16) get_unaligned_le16(data)); input_report_abs(dev, ABS_GAS, 255 - data[2]); @@ -181,6 +187,9 @@ void iforce_process_packet(struct iforce *iforce, break; case 0x02: /* status report */ + if (len < 2) + break; + input_report_key(dev, BTN_DEAD, data[0] & 0x02); input_sync(dev); @@ -200,7 +209,7 @@ void iforce_process_packet(struct iforce *iforce, } } - for (j = 3; j < len; j += 2) + for (j = 3; j + sizeof(u16) <= len; j += sizeof(u16)) mark_core_as_ready(iforce, get_unaligned_le16(data + j)); break; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 0482eaaecf39..f04370e4191e 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -158,6 +158,9 @@ static void iforce_usb_irq(struct urb *urb) goto exit; } + if (!urb->actual_length) + goto exit; + iforce_process_packet(iforce, iforce_usb->data_in[0], iforce_usb->data_in + 1, urb->actual_length - 1); -- cgit v1.2.3 From 21e19688433452dfbbbe6b2bb670dea6eb92f0f6 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Sat, 25 Jul 2026 08:33:45 +0200 Subject: ALSA: seq: Fix division by zero in initialize_timer() A userspace-driven ALSA timer (SND_UTIMER) lets an unprivileged user set the backing snd_timer's hardware resolution to an arbitrary 64-bit value via SNDRV_TIMER_IOCTL_CREATE. snd_utimer_create() only rejects zero. When such a timer is bound to a sequencer queue, initialize_timer() computes the tick period as tmr->ticks = 1000000000 / (r * freq); where r is that user-controlled resolution and freq is the sequencer update rate in Hz, clamped to MIN_FREQUENCY..MAX_FREQUENCY (10..6250). A resolution of 2^63 makes the 64-bit product r * freq wrap to zero for any even freq, including DEFAULT_FREQUENCY (1000), so the division faults with a divide-by-zero. The division runs under tmr->lock with interrupts disabled, so the oops leaves the spinlock held and hangs the CPU. It is reachable by an unprivileged user with access to /dev/snd/timer and /dev/snd/seq. Oops: divide error: 0000 [#1] SMP KASAN PTI CPU: 7 UID: 1000 PID: 456 Comm: alsa_seq_utimer Not tainted 7.2.0-rc4+ RIP: 0010:initialize_timer.constprop.0+0x20a/0x2d0 snd_seq_timer_start+0x15e/0x2b0 snd_seq_control_queue+0x56f/0xba0 snd_seq_write+0x3e0/0x730 Reject an overflowing product with check_mul_overflow() and fall back to a single tick, which also avoids feeding a wrapped-but-nonzero divisor (e.g. 2^63 * 1000 mod 2^64 == 0, or other resolutions wrapping to a small value) into the period computation. Fixes: 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers") Cc: Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/DF8A3844-AD5E-4B8A-9CFC-BD83C212BA38@doyensec.com Signed-off-by: Takashi Iwai --- sound/core/seq/seq_timer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 419288eec4bb..7b671e270ef4 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -362,11 +362,10 @@ static int initialize_timer(struct snd_seq_timer *tmr) tmr->ticks = 1; if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { unsigned long r = snd_timer_resolution(tmr->timeri); - if (r) { - tmr->ticks = (unsigned int)(1000000000uL / (r * freq)); - if (! tmr->ticks) - tmr->ticks = 1; - } + unsigned long den; + + if (r && !check_mul_overflow(r, freq, &den)) + tmr->ticks = max(1U, (unsigned int)(1000000000uL / den)); } tmr->initialized = 1; return 0; -- cgit v1.2.3 From 015b5bcbcb622b32317642be91a7f79aa5413649 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sun, 19 Jul 2026 14:11:40 +0200 Subject: pinctrl: devicetree: don't free uninitialized dev_name on error path dt_remember_or_free_map() duplicates dev_name for each map entry. If kstrdup_const() fails, dt_free_map() frees dev_name in all num_maps entries, including entries that have not been initialized. Some pinctrl drivers, including pinctrl-imx, allocate the map with kmalloc() and leave dev_name for the core to initialize. The untouched entries therefore contain uninitialized data which is passed to kfree_const(). Reproduced on qemu's mcimx6ul-evk (pinctrl-imx) with failslab injection while binding the pinctrl-consuming device, under KASAN: BUG: KASAN: double-free in dt_free_map+0x34/0xa4 Free of addr c425a900 by task init/1 kfree from dt_free_map+0x34/0xa4 dt_free_map from dt_remember_or_free_map+0x184/0x198 dt_remember_or_free_map from pinctrl_dt_to_map+0x33c/0x4c8 pinctrl_dt_to_map from create_pinctrl+0x9c/0x5c0 Initialize all dev_name fields to NULL before duplicating the device name, making the full-map cleanup safe after a partial failure. Fixes: be4c60b563ed ("pinctrl: devicetree: Avoid taking direct reference to device name string") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-fable-5 Signed-off-by: Karl Mehltretter Signed-off-by: Linus Walleij --- drivers/pinctrl/devicetree.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 02a271dd292f..465b43092eb7 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -69,6 +69,10 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename, int i; struct pinctrl_dt_map *dt_map; + /* Initialize dev_name before any allocation can fail */ + for (i = 0; i < num_maps; i++) + map[i].dev_name = NULL; + /* Initialize common mapping table entry fields */ for (i = 0; i < num_maps; i++) { const char *devname; -- cgit v1.2.3 From 25cb6e9a13123d1039cdc75b446ac52e1ebdc26d Mon Sep 17 00:00:00 2001 From: Benjamin Boortz Date: Sun, 19 Jul 2026 11:41:46 +0200 Subject: pinctrl: microchip-sgpio: add missing select REGMAP_MMIO The driver calls ocelot_regmap_from_resource() via , which internally uses devm_regmap_init_mmio() and requires REGMAP_MMIO. The Kconfig entry does not select REGMAP_MMIO, causing a build failure when no other driver in the config happens to pull in REGMAP_MMIO: include/linux/mfd/ocelot.h:34:24: error: implicit declaration of function 'devm_regmap_init_mmio' Found by randconfig testing on arm64; tinyconfig reproducer below. Fixes: 2afbbab45c26 ("pinctrl: microchip-sgpio: update to support regmap") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Boortz Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c2cdd7b2c49b..23ea76dc6261 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -426,6 +426,7 @@ config PINCTRL_MICROCHIP_SGPIO select GENERIC_PINCONF select GENERIC_PINCTRL_GROUPS select GENERIC_PINMUX_FUNCTIONS + select REGMAP_MMIO help Support for the serial GPIO interface used on Microsemi and Microchip SoCs. By using a serial interface, the SIO -- cgit v1.2.3 From ffe8a0c6b55285ceaf2f42fc20c3a0594d14f1e9 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 20 Jul 2026 11:28:44 -0500 Subject: pinctrl-amd: Don't clear S4 wake bits at probe commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") introduced a regression where Wake-on-LAN no longer works after suspend or shutdown on some AMD platforms. Firmware-programmed S4 wake bits for devices like PCIe NICs using PCI PME are cleared at probe, but nothing restores them. Unlike S0i3/S3 wake sources that use enable_irq_wake() -> amd_gpio_irq_set_wake(), PCIe PME does not use GPIO IRQ infrastructure and relies on firmware configuration. The original intent of commit 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") was to clear spurious wake bits left by firmware to prevent unwanted wakeups. However, S4 wake bits are used for hardware-level wake sources like WoL that bypass the kernel's IRQ wake API. Fix by preserving S4 wake bits at probe and only clearing S0i3/S3 bits: - Firmware-configured S4 wake sources (WoL) continue working - Kernel maintains control of S3/S0i3 wake policy via set_wake() - S3-only wake sources work correctly per commit f31f33dbb3ba ("pinctrl: amd: Take suspend type into consideration which pins are non-wake") The trade-off is that firmware-programmed spurious S4 wake bits remain set, but this is less problematic than breaking WoL. Fixes: 6bc3462a0f5e ("pinctrl: amd: Mask wake bits on probe again") Signed-off-by: Mario Limonciello Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-amd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index e3128b0045d2..15a398bb3be2 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -884,8 +884,7 @@ static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) u32 pin_reg, mask; int i; - mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | - BIT(WAKE_CNTRL_OFF_S4); + mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); for (i = 0; i < desc->npins; i++) { int pin = desc->pins[i].number; -- cgit v1.2.3 From dad6e107b3cd9d20514e7799b7ad8674f81e3f30 Mon Sep 17 00:00:00 2001 From: Benjamin Boortz Date: Mon, 20 Jul 2026 19:51:04 +0200 Subject: pinctrl: bm1880: add missing select GENERIC_PINCONF drivers/pinctrl/pinctrl-bm1880.c initialises its pinconf_ops with .is_generic = true, but that field is only present when CONFIG_GENERIC_PINCONF is enabled (guarded by #ifdef in pinconf.h). The Kconfig entry for PINCTRL_BM1880 never selects GENERIC_PINCONF, so any config that enables CONFIG_PINCTRL_BM1880=y without CONFIG_GENERIC_PINCONF=y fails to compile: drivers/pinctrl/pinctrl-bm1880.c:1288:10: error: 'const struct pinconf_ops' has no member named 'is_generic' Found by randconfig testing on arm64; tinyconfig reproducer below. Add the missing select to fix the build. Fixes: 49bd61ebce5f ("pinctrl: Add pinconf support for BM1880 SoC") Cc: stable@vger.kernel.org Signed-off-by: Benjamin Boortz Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 23ea76dc6261..eda54aa5fde6 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -166,6 +166,7 @@ config PINCTRL_BM1880 depends on OF && (ARCH_BITMAIN || COMPILE_TEST) default ARCH_BITMAIN select PINMUX + select GENERIC_PINCONF help Pinctrl driver for Bitmain BM1880 SoC. -- cgit v1.2.3 From 797fe91e50d6927f90f0c3b4444277c2c7c8b42b Mon Sep 17 00:00:00 2001 From: Michal Clapinski Date: Fri, 17 Jul 2026 15:40:28 +0200 Subject: kho: align kho_scratch to MAX_ORDER_NR_PAGES pages While booting with KHO, the following crash was observed: BUG: unable to handle page fault for address: ff19164fffff8328 RIP: 0010:__free_one_page+0x1a1/0x6b0 Call Trace: [] free_one_page+0xaf/0x240 [] deferred_free_pages+0xa8/0xd0 [] deferred_init_memmap_chunk+0x10f/0x1b0 [] padata_mt_helper+0x65/0xa0 [] process_scheduled_works+0x202/0x410 [] worker_thread+0x1f9/0x2d0 [] kthread+0x27d/0x2f0 [] ? __pfx_worker_thread+0x10/0x10 [] ? __pfx_kthread+0x10/0x10 [] ret_from_fork+0x145/0x280 [] ? __pfx_kthread+0x10/0x10 [] ret_from_fork_asm+0x1a/0x30 deferred_init_memmap_chunk() interleaves initialization of struct pages with freeing them. This works fine without KHO because free regions will never be buddy neighbors. However, with KHO, free memory will be split into (free && scratch) and (free && !scratch), that can be buddy neighbors. KHO scratch is aligned to CMA_MIN_ALIGNMENT_PAGES pages but buddy looks at the neighborhood of MAX_ORDER_NR_PAGES pages. These values are configurable but CMA_MIN_ALIGNMENT_PAGES is always less or equal to MAX_ORDER_NR_PAGES. In the crashing configuration they were set as follows: CMA_MIN_ALIGNMENT_PAGES = 1 << 9 MAX_ORDER_NR_PAGES = 1 << 10 So while freeing one chunk, buddy accessed uninitialized struct pages from another chunk, tried to merge the blocks and crashed. To fix this, let's just align KHO scratch to MAX_ORDER_NR_PAGES pages. Fixes: c6073743d0c7 ("kho: make preserved pages compatible with deferred struct page init") Signed-off-by: Michal Clapinski Link: https://patch.msgid.link/20260717134028.2880508-1-mclapinski@google.com [rppt: massaged the changelog] Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/kexec_handover.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 4834a809985a..175c08a6e41e 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -38,6 +38,16 @@ #include "../kexec_internal.h" #include "kexec_handover_internal.h" +/* + * This is the minimal alignment required by deferred struct page init. + * deferred_init_memmap_chunk frees memory to the buddy allocator, which looks + * at the neighboring pages (up to MAX_PAGE_ORDER) to merge them. + * If KHO scratch is not aligned to that value, buddy can access uninitialized + * struct pages, which can cause a crash. + */ +#define SCRATCH_ALIGNMENT_BYTES (PAGE_SIZE * MAX_ORDER_NR_PAGES) +static_assert(SCRATCH_ALIGNMENT_BYTES >= CMA_MIN_ALIGNMENT_BYTES); + /* The magic token for preserved pages */ #define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */ @@ -640,8 +650,8 @@ static void __init scratch_size_update(void) * Scratch areas are released as MIGRATE_CMA. Round them up to the right * size. */ - scratch_size_lowmem = round_up(scratch_size_lowmem, CMA_MIN_ALIGNMENT_BYTES); - scratch_size_global = round_up(scratch_size_global, CMA_MIN_ALIGNMENT_BYTES); + scratch_size_lowmem = round_up(scratch_size_lowmem, SCRATCH_ALIGNMENT_BYTES); + scratch_size_global = round_up(scratch_size_global, SCRATCH_ALIGNMENT_BYTES); } static phys_addr_t __init scratch_size_node(int nid) @@ -656,7 +666,7 @@ static phys_addr_t __init scratch_size_node(int nid) size = scratch_size_pernode; } - return round_up(size, CMA_MIN_ALIGNMENT_BYTES); + return round_up(size, SCRATCH_ALIGNMENT_BYTES); } /** @@ -692,7 +702,7 @@ static void __init kho_reserve_scratch(void) * next kernel */ size = scratch_size_lowmem; - addr = memblock_phys_alloc_range(size, CMA_MIN_ALIGNMENT_BYTES, 0, + addr = memblock_phys_alloc_range(size, SCRATCH_ALIGNMENT_BYTES, 0, ARCH_LOW_ADDRESS_LIMIT); if (!addr) { pr_err("Failed to reserve lowmem scratch buffer\n"); @@ -705,7 +715,7 @@ static void __init kho_reserve_scratch(void) /* reserve large contiguous area for allocations without nid */ size = scratch_size_global; - addr = memblock_phys_alloc(size, CMA_MIN_ALIGNMENT_BYTES); + addr = memblock_phys_alloc(size, SCRATCH_ALIGNMENT_BYTES); if (!addr) { pr_err("Failed to reserve global scratch buffer\n"); goto err_free_scratch_areas; @@ -721,7 +731,7 @@ static void __init kho_reserve_scratch(void) */ for_each_node_state(nid, N_MEMORY) { size = scratch_size_node(nid); - addr = memblock_alloc_range_nid(size, CMA_MIN_ALIGNMENT_BYTES, + addr = memblock_alloc_range_nid(size, SCRATCH_ALIGNMENT_BYTES, 0, MEMBLOCK_ALLOC_ACCESSIBLE, nid, true); if (!addr) { -- cgit v1.2.3 From 6437033bffe8bd2af174d139af552d90d40c7ac6 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 23 Jul 2026 16:57:10 +0800 Subject: ALSA: lx6464es: fix period byte count for 16-bit streams The lx6464es driver advertises both 16-bit and packed 24-bit PCM formats, but lx_trigger_start() and lx_interrupt_request_new_buffer() calculate the DMA period size as runtime->period_size * runtime->channels * 3. That is only correct for the packed 24-bit formats. For 16-bit streams the driver submits buffers that are 50% larger than the actual ALSA period and advances the DMA address by the same wrong amount. For example, with 2 channels, 256 frames and 4 periods, the third buffer already extends beyond the ALSA buffer and the fourth buffer starts outside it. Use snd_pcm_lib_period_bytes() so the byte count matches the runtime format, channel count and period size. Fixes: 02bec4904508 ("ALSA: lx6464es - driver for the digigram lx6464es interface") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/8BB12E8D92A7CDBA+20260723085710.2567463-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai --- sound/pci/lx6464es/lx6464es.c | 5 +---- sound/pci/lx6464es/lx_core.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 96df00db51d5..3b40ec071379 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -402,11 +402,8 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream) int err; - const u32 channels = substream->runtime->channels; - const u32 bytes_per_frame = channels * 3; - const u32 period_size = substream->runtime->period_size; const u32 periods = substream->runtime->periods; - const u32 period_bytes = period_size * bytes_per_frame; + const u32 period_bytes = snd_pcm_lib_period_bytes(substream); dma_addr_t buf = substream->dma_buffer.addr; int i; diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index 6f0843cfb3be..9909f1067871 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -969,10 +969,7 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip, const unsigned int is_capture = lx_stream->is_capture; int err; - const u32 channels = substream->runtime->channels; - const u32 bytes_per_frame = channels * 3; - const u32 period_size = substream->runtime->period_size; - const u32 period_bytes = period_size * bytes_per_frame; + const u32 period_bytes = snd_pcm_lib_period_bytes(substream); const u32 pos = lx_stream->frame_pos; const u32 next_pos = ((pos+1) == substream->runtime->periods) ? 0 : pos + 1; -- cgit v1.2.3 From bf4fc9f33ec21595143132a3e7fb8b5d2c2261cd Mon Sep 17 00:00:00 2001 From: Madhavender Singh Date: Thu, 23 Jul 2026 16:17:36 +0530 Subject: ALSA: hda/realtek: Add mute LED quirk for HP Laptop 14s-dr1xxx This laptop with an ALC236 codec requires the ALC236_FIXUP_HP_MUTE_LED_COEFBIT2 fixup for its mute LED to function correctly. Add the subsystem ID 0x103c:0x86c8 to the quirk table to apply this fixup. Signed-off-by: Madhavender Singh Link: https://patch.msgid.link/20260723104736.23386-1-madhav@disroot.org 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 fcc60e64137d..c246086c5082 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7142,6 +7142,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), + SND_PCI_QUIRK(0x103c, 0x86c8, "HP Laptop 14s-dr1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), SND_PCI_QUIRK(0x103c, 0x863e, "HP Spectre x360 15-df1xxx", ALC285_FIXUP_HP_SPECTRE_X360_DF1), SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), -- cgit v1.2.3 From 8c63f59dcf474f5843d71fb58281a8329be5b034 Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Fri, 24 Jul 2026 12:46:33 +0000 Subject: ALSA: usb-audio: Add iface reset and delay quirk for JKY Technology Q2A Setting up the interface when suspended/resuming fails on this card. Adding a reset and delay quirk will eliminate this problem. Note: This device's VID conflicts with Apple's (0x05ac). usb 1-1: New USB device found, idVendor=05ac, idProduct=110b usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: Q2A usb 1-1: Manufacturer: JKY Technology usb 1-1: SerialNumber: 330270D2251225 Suggested-by: Rong Zhang Signed-off-by: Lianqin Hu Reviewed-by: Rong Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/TYUPR06MB6217566CFD33F57D3AE46816D2CF2@TYUPR06MB6217.apcprd06.prod.outlook.com --- sound/usb/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 49b56287cdb9..40aa40fccb46 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2200,6 +2200,9 @@ struct usb_audio_quirk_flags_table { static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { /* Device and string descriptor matches */ + DEVICE_STRING_FLG(0x05ac, 0x110b, /* VID conflicts with Apple */ + "JKY Technology", NULL, /* Q2A */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), /* Device matches */ DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */ -- cgit v1.2.3 From 26a94400ffa4fcbeff32e23abebb83a1a20eb401 Mon Sep 17 00:00:00 2001 From: Eckhart Mohr Date: Fri, 24 Jul 2026 21:00:13 +0200 Subject: ALSA: hda/realtek: Add quirk for TongFang X6SP45xU TongFang X6KK45xU and X6SP45xU have actually different PCI IDs. This patch Adds the missing PCI ID to fix headphone detection and clarifies the naming. Fixes: d595255241e5 ("ALSA: hda/realtek: Add quirk for TongFang X6xx45xU") Signed-off-by: Eckhart Mohr Cc: stable@vger.kernel.org Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260724190109.169889-1-wse@tuxedocomputers.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c246086c5082..a0abb8993440 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8036,7 +8036,8 @@ 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(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", 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), -- cgit v1.2.3 From cd74e5cba460e9ba604e9a418317e38cf50401c8 Mon Sep 17 00:00:00 2001 From: Marcos Paulo Medeiros Date: Sat, 25 Jul 2026 18:41:31 -0300 Subject: ALSA: hda/realtek: Fix headset mic on Acer Nitro 5 AN515-46 The Acer Nitro 5 AN515-46 (SSID 1025:159e, Realtek ALC287) has a combo headset jack whose microphone does not work out of the box: the BIOS leaves pin 0x19 unconfigured, so no headset mic is created. Apply ALC2XX_FIXUP_HEADSET_MIC, the same fixup already used by the sibling models AN515-57 (1025:1539) and AN517-55 (1025:1597), which makes the headset microphone work correctly. Tested on an Acer Nitro 5 AN515-46 by overriding the model via a patch firmware with model=alc2xx-fixup-headset-mic. Signed-off-by: Marcos Paulo Medeiros Link: https://patch.msgid.link/20260725214131.25872-1-maarcospm1996@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 a0abb8993440..9533be5c398b 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6940,6 +6940,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), -- cgit v1.2.3 From 4a05b2d1b4642df74f30b6f54843e825c4a2bfd3 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 14:13:37 +0900 Subject: ALSA: usb-audio: fix use-after-free in ump_to_endpoint() create_midi2_ump() registers a card-owned snd_ump_endpoint and stores a back-pointer to its per-interface snd_usb_midi2_ump object in ump->private_data, but it never installs an ump->private_free hook and never clears that pointer. If a later step of snd_usb_midi_v2_create() fails, its error path calls free_all_midi2_umps(), which kfree()s the snd_usb_midi2_ump object while the already-registered endpoint keeps pointing at it. The created /dev/snd/umpC*D* node stays exposed, so the first operation of any UMP open, ump_to_endpoint(), dereferences the dangling ump->private_data and reads rmidi->eps[dir] out of freed memory. A malicious USB MIDI 2.0 device that makes creation fail after the endpoint is registered can thus trigger a slab use-after-free read on a subsequent open of the UMP node. Clear the endpoint's back-pointer before freeing the object, and let ump_to_endpoint() tolerate a NULL private_data so the open/close/trigger callbacks fail cleanly (their callers already handle a NULL endpoint) instead of dereferencing a stale pointer. Discovered by XBOW, triaged by Baul Lee Fixes: ff49d1df79ae ("ALSA: usb-audio: USB MIDI 2.0 UMP support") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726051337.41124-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/midi2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 3ec633291772..83980fb83ac8 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -329,7 +329,7 @@ ump_to_endpoint(struct snd_ump_endpoint *ump, int dir) { struct snd_usb_midi2_ump *rmidi = ump->private_data; - return rmidi->eps[dir]; + return rmidi ? rmidi->eps[dir] : NULL; } /* ump open callback */ @@ -685,6 +685,8 @@ static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi) rmidi = list_first_entry(&umidi->rawmidi_list, struct snd_usb_midi2_ump, list); list_del(&rmidi->list); + if (rmidi->ump) + rmidi->ump->private_data = NULL; kfree(rmidi); } } -- cgit v1.2.3 From 70c977815af0d997feb2d0c5d284d55689bf7051 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 14:16:33 +0900 Subject: ALSA: ump: fix double free of out_cvts on rawmidi error snd_ump_attach_legacy_rawmidi() allocates the legacy conversion array ump->out_cvts and, on the snd_rawmidi_new() error path, frees it with kfree() but leaves ump->out_cvts pointing at the freed memory. When the endpoint is later torn down, snd_ump_endpoint_free() frees ump->out_cvts a second time, resulting in a double free. The host snd-usb-audio driver attaches the legacy rawmidi for any USB MIDI 2.0 (UMP) device, so a device that makes snd_rawmidi_new() fail reaches this path on enumeration. Clear ump->out_cvts after freeing it on the error path so it is not freed again during teardown. Discovered by XBOW, triaged by Baul Lee Fixes: 33cd7630782d ("ALSA: ump: Export MIDI1 / UMP conversion helpers") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726051633.41206-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/core/ump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/ump.c b/sound/core/ump.c index 70520c7ca293..632c13baf21e 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -1365,6 +1365,7 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, &rmidi); if (err < 0) { kfree(ump->out_cvts); + ump->out_cvts = NULL; return err; } -- cgit v1.2.3 From 441aaad150c57edaf57ee482a79a3bf4c5b7e353 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 15:50:20 +0900 Subject: ALSA: usb-audio: fix stack info leak in RME Digiface status snd_rme_digiface_read_status() reads a four-word status block from the device into an uninitialised on-stack __le32 buf[4] and, whenever the vendor control-IN transfer does not return a negative error, copies all four words into the caller's status[]. snd_usb_ctl_msg() copies the full requested size back into the caller's buffer regardless of how many bytes the data stage actually delivered: buf = kmemdup(data, size, GFP_KERNEL); err = usb_control_msg(dev, pipe, request, requesttype, value, index, buf, size, timeout); memcpy(data, buf, size); usb_control_msg() returns the transferred length on a short control-IN, which is a non-negative value, and writes only that many bytes. The remainder of the copy back is the kmemdup()ed image of the caller's buffer, so a device answering with a short data stage leaves the trailing words of buf[] holding leftover kernel stack. The only guard in the caller is err < 0, so those words are stored into status[]. They then reach user space: snd_rme_digiface_get_status_val() selects a 16-bit halfword of status[] per the control's reg/mask, and the eight Digiface status controls together expose the whole 16-byte frame to an unprivileged reader of /dev/snd/controlC*. Zero-initialise the buffer so a short read yields zeros instead of stack residue. This mirrors snd_rme_get_status1(), which already clears its output word before the same kind of vendor read. Discovered by XBOW, triaged by Baul Lee Fixes: 611a96f6acf2 ("ALSA: usb-audio: Add mixer quirk for RME Digiface USB") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726065020.46070-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 10792d26fa94..74d26fd86efb 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3496,7 +3496,7 @@ static int snd_rme_digiface_read_status(struct snd_kcontrol *kcontrol, u32 statu struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct snd_usb_audio *chip = list->mixer->chip; struct usb_device *dev = chip->dev; - __le32 buf[4]; + __le32 buf[4] = {}; int err; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), -- cgit v1.2.3 From 0970274613fb463d376211450cab066d34ebfe6a Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 16:45:00 +0900 Subject: ALSA: usb-audio: fix OOB write in snd_usbmidi_akai_output() snd_usbmidi_akai_output() computes its fill-loop bound buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1; as a signed int, so a small device-advertised bulk-OUT max_transfer makes buf_end negative. The loop guard then compares the u32 urb->transfer_buffer_length against that negative int: the usual arithmetic conversion turns buf_end into a large unsigned value, so the guard stays true and each iteration keeps appending SysEx framing and payload bytes past the end of the URB transfer buffer, which is only max_transfer bytes long. A USB device that advertises a tiny bulk-OUT endpoint can therefore trigger an attacker-length- and content-controlled heap out-of-bounds write when a process writes to the created /dev/snd/midiC*D* node. Return early when there is no room for even one SysEx, so the loop is never entered with a bound that would wrap. The loop is the last statement of the function, so bailing out is equivalent to it not running. Discovered by XBOW, triaged by Baul Lee Fixes: 4434ade8c933 ("ALSA: usb-audio: add support for Akai MPD16") Suggested-by: Takashi Iwai Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726074500.50145-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/midi.c b/sound/usb/midi.c index d87e3f357cf7..f8996416c3be 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -797,6 +797,8 @@ static void snd_usbmidi_akai_output(struct snd_usb_midi_out_endpoint *ep, msg = urb->transfer_buffer + urb->transfer_buffer_length; buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1; + if (buf_end <= 0) + return; /* only try adding more data when there's space for at least 1 SysEx */ while (urb->transfer_buffer_length < buf_end) { -- cgit v1.2.3 From a54bf16965f896415c3337bc4fbb40fb11941d99 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 26 Jul 2026 09:48:19 +0200 Subject: ALSA: 6fire: Fix UAF at error handling during probe Although 6fire driver had a few fixes for dealing with the early error handling during the probe phase, it forgot a pending URB before freeing the resources, which may lead to a UAF. This patch addresses it by doing the almost same cleanup procedure like the normal disconnect phase at the error path. Reported-and-tested-by: Shuangpeng Bai Closes: https://lore.kernel.org/20260724030900.1984491-1-shuangpeng.kernel@gmail.com Cc: Link: https://patch.msgid.link/20260726074821.2288158-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/6fire/chip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 2c5648966412..6cf410a92fa2 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -150,6 +150,10 @@ static int usb6fire_chip_probe(struct usb_interface *intf, return 0; destroy_chip: + chip->shutdown = true; + if (card) + snd_card_disconnect(card); + usb6fire_chip_abort(chip); snd_card_free(card); return ret; } -- cgit v1.2.3 From fd71820e876ef4b816b6305416ba5973bb6b9665 Mon Sep 17 00:00:00 2001 From: Gunal Seenivasagan Date: Sun, 26 Jul 2026 14:27:11 +0530 Subject: ALSA: hda/realtek: Add quirk for Infinix INBOOK X3 Slim The Infinix INBOOK X3 Slim (ALC269VB, subsystem 0x2782:0250) has its internal speakers wired to pin 0x1b, but the BIOS pin configuration table declares 0x1b as "no physical connection" (0x411111f0). It instead declares pin 0x14 as the internal speaker, although nothing is connected to 0x14. As a result the internal speakers are silent under Linux while the headphone jack works correctly. The codec output path to 0x14 is fully open (DAC assigned and streaming, mixer and pin unmuted, EAPD asserted), so the failure is silent with no error reported. The speakers work under Windows, where the vendor driver supplies its own pin table. Add a fixup that disables the unconnected pin 0x14 and declares pin 0x1b as the internal speaker. Reusing the existing ALC269VC_FIXUP_INFINIX_Y4_MAX was tried first, since it also remaps 0x1b to an internal speaker. It is not sufficient here: it leaves 0x14 declared, so autoconfig finds two line_outs line_outs=2 (0x14/0x1b/0x0/0x0/0x0) type:speaker and binds the primary "Speaker" control to the unconnected pin 0x14 while demoting the working speaker at 0x1b to "Bass Speaker". Audio is audible that way, but the volume and mute controls a desktop actually uses end up attached to a pin that is not wired to anything. Disabling 0x14 is what produces a single correct Speaker output. Verified on the affected machine: with the corrected pin configuration the driver's autoconfig reports line_outs=1 (0x1b/0x0/0x0/0x0/0x0) type:speaker both channels play, headphone auto-mute switches correctly in both directions, and audio survives codec runtime suspend (D3) and resume. Signed-off-by: Gunal Seenivasagan Link: https://patch.msgid.link/20260726085715.229802-1-gunal2002@gmail.com 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 9533be5c398b..09225801a177 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3949,6 +3949,7 @@ enum { ALC269_FIXUP_DMIC_THINKPAD_ACPI, ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, ALC269VC_FIXUP_INFINIX_Y4_MAX, + ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM, ALC269VC_FIXUP_LUNNEN_GROUND_14, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, @@ -4421,6 +4422,14 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x411111f0 }, /* disable, not connected */ + { 0x1b, 0x90170110 }, /* use as internal speaker */ + { } + }, + }, [ALC269VC_FIXUP_LUNNEN_GROUND_14] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8058,6 +8067,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x0250, "Infinix INBOOK X3 Slim", ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM), SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), SND_PCI_QUIRK(0x2782, 0x1409, "Positivo K116J", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), -- cgit v1.2.3 From c2744d5f3aea474513fd2298daecb94a952ce441 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Sun, 26 Jul 2026 10:01:45 +0200 Subject: ALSA: timer: Clear SNDRV_TIMER_IFLG_DEAD once the close completes snd_timer_close_locked() marks an instance with SNDRV_TIMER_IFLG_DEAD and returns early when the flag is already set, but the flag is never cleared again. A completed close ends in remove_slave_links(), which leaves timeri->timer NULL, so a second close is already harmless through the timer == NULL path; the early return can only be reached by an instance that was opened again in between. For such an instance the close unlinks nothing, so snd_timer_instance_free() frees an object that is still on timer->open_list_head, still on snd_timer_master_list if it was opened with a slave key, still owns any adopted slaves, and still holds its timer and module references. snd_seq_timer_open() reopens an instance exactly like that: it retries its fallback open on the same object after a failure that has already run snd_timer_close_locked() internally. An unprivileged user with access to /dev/snd/timer and /dev/snd/seq can force that failure, since snd_timer_check_master() returns -EBUSY when a pending slave matches the new master's (slave_class, slave_id) key and the target timer has reached max_instances, and SNDRV_TIMER_IOCTL_SELECT with dev_class = SNDRV_TIMER_CLASS_SLAVE keeps the caller-supplied dev_sclass, so a sequencer queue's key can be forged. The freed instance is afterwards dereferenced by any further snd_timer_open() on that timer, by snd_timer_check_slave(), and by /proc/asound/timers, which faults on the stale ti->owner pointer. The flag only has to be visible while the close is in progress, which is all its other users need. Clear it in remove_slave_links(), under the same timer->lock that sets it, once the instance is off every list. Fixes: da3039e91d1f ("ALSA: timer: Forcibly close timer instances at closing") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/CA41AA48-75BF-45E9-A36D-3A5D2F124F60@doyensec.com Signed-off-by: Takashi Iwai --- sound/core/timer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/timer.c b/sound/core/timer.c index 07ef127b29e1..9407954034cf 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -458,6 +458,8 @@ static void remove_slave_links(struct snd_timer_instance *timeri, list_del_init(&slave->ack_list); list_del_init(&slave->active_list); } + /* the close is done; a reopen must not see the mark */ + timeri->flags &= ~SNDRV_TIMER_IFLG_DEAD; } /* -- cgit v1.2.3 From 7e7f81cf6f5ca3311e526308f55d7c54d3ba71f9 Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Tue, 2 Jun 2026 21:26:44 -0700 Subject: x86/bugs: Make Safe-RET robust against interrupt injection An attacker injecting interrupts while the Safe-RET mitigation executes on machines affected by SRSO can neutralize the safe return sequence, potentially leading to data leakage through speculative execution. Fixup register state as if the Safe-RET sequence executed successfully by "emulating" it, in a manner of speaking, and avoid executing a RET instruction after returning from the interrupt. Co-developed-by: David Kaplan Signed-off-by: David Kaplan Signed-off-by: Borislav Petkov (AMD) --- arch/x86/entry/entry_64.S | 8 ++++- arch/x86/include/asm/nospec-branch.h | 57 ++++++++++++++++++++++++++++++++++++ arch/x86/kernel/cpu/bugs.c | 39 ++++++++++++++++++++++++ arch/x86/lib/retpoline.S | 20 +++++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index c6d996593f32..253f0a585927 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -937,6 +937,8 @@ SYM_CODE_START(paranoid_entry) IBRS_ENTER save_reg=%r15 UNTRAIN_RET_FROM_CALL + HANDLE_INTR_SAFERET 8(%rsp) + RET SYM_CODE_END(paranoid_entry) @@ -1039,6 +1041,11 @@ SYM_CODE_START(error_entry) movl %ecx, %eax /* zero extend */ cmpq %rax, RIP+8(%rsp) je .Lbstep_iret + + VALIDATE_UNRET_END + + HANDLE_INTR_SAFERET 8(%rsp) + cmpq $.Lgs_change, RIP+8(%rsp) jne .Lerror_entry_done_lfence @@ -1057,7 +1064,6 @@ SYM_CODE_START(error_entry) FENCE_SWAPGS_KERNEL_ENTRY CALL_DEPTH_ACCOUNT leaq 8(%rsp), %rax /* return pt_regs pointer */ - VALIDATE_UNRET_END RET .Lbstep_iret: diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index b68892e6d58c..2ea6591bc7b9 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -12,6 +12,7 @@ #include #include #include +#include /* * Call depth tracking for Intel SKL CPUs to address the RSB underflow @@ -176,6 +177,50 @@ add $(BITS_PER_LONG/8), %_ASM_SP; \ lfence; +/* + * Helper for detecting if an interrupt occurred at an unsafe location within + * Safe-RET. If Safe-RET is interrupted after the CALL or LEA the RSB may get + * poisoned by the interrupt handler. + * + * The Safe-RET sequence is: + * + * CALL + * LEA 8(%RSP), %RSP + * RET + * + * The two CMPs below check whether RIP points to after the CALL or after the + * LEA. + * + * The LFENCE below is to address this particular speculation case: + * + * 1. Userspace runs and poisons the BTB around the safe-RET routine + * + * 2. Userspace triggers some kind of exception + * + * 3. Kernel executes error_entry() and mis-speculates the branch into thinking + * it actually came from kernel space + * + * 4. The kernel then further mis-speculates that the exception occurred due + * to an interrupted safe-RET + * + * 5. The handle_interrupted_saferet() routine speculatively executes and + * speculatively does a safe-RET. But this is unsafe since it was never + * untrained. + * + * The LFENCE fixes this by ensuring step 5 is never reached speculatively. + * Note that this LFENCE only occurs if safe-RET was actually interrupted (so + * it's outside of the normal path). + */ +#define __HANDLE_INTR_SAFERET(name, pt_regs) \ + cmpq $(name), RIP+pt_regs; \ + jb 1f; \ + cmpq $(name)+5, RIP+pt_regs; \ + ja 1f; \ + lfence; \ + leaq pt_regs, %rdi; \ + call handle_interrupted_saferet; \ + 1: + #ifdef __ASSEMBLER__ /* @@ -293,6 +338,14 @@ #define UNTRAIN_RET_FROM_CALL \ __UNTRAIN_RET X86_FEATURE_ENTRY_IBPB, __stringify(RESET_CALL_DEPTH_FROM_CALL) +.macro HANDLE_INTR_SAFERET pt_regs +#ifdef CONFIG_MITIGATION_SRSO + ALTERNATIVE_2 "", \ + __stringify(__HANDLE_INTR_SAFERET(srso_safe_ret, \pt_regs)), X86_FEATURE_SRSO, \ + __stringify(__HANDLE_INTR_SAFERET(srso_alias_safe_ret, \pt_regs)), X86_FEATURE_SRSO_ALIAS + +#endif +.endm .macro CALL_DEPTH_ACCOUNT #ifdef CONFIG_MITIGATION_CALL_DEPTH_TRACKING @@ -625,6 +678,10 @@ static __always_inline void x86_idle_clear_cpu_buffers(void) x86_clear_cpu_buffers(); } +void srso_safe_ret(void); +void srso_alias_safe_ret(void); +void handle_interrupted_saferet(struct pt_regs *regs); + #endif /* __ASSEMBLER__ */ #endif /* _ASM_X86_NOSPEC_BRANCH_H_ */ diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index d9af230c0512..82436b3534fa 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -3775,3 +3775,42 @@ void __warn_thunk(void) { WARN_ONCE(1, "Unpatched return thunk in use. This should not happen!\n"); } + +#ifdef CONFIG_MITIGATION_SRSO +/* + * Called during exception/interrupt entry if interrupted during the + * safe-RET sequence. The safe-RET sequence consists of 3 instructions: + * + * CALL + * LEA 8(%RSP), %RSP + * RET + * + * An interrupt after the CALL or after the LEA could potentially lead + * to branch predictor poisoning and results in the sequence not being + * able to be safely resumed. + * + * Therefore, modify the regs state as if the remaining part of the + * safe-RET sequence executed so the interrupt returns back to the + * desired return target, instead of the to the safe-RET sequence. + */ +void noinstr handle_interrupted_saferet(struct pt_regs *regs) +{ + unsigned long rip = regs->ip; + + if (rip == (unsigned long) srso_safe_ret || + rip == (unsigned long) srso_alias_safe_ret) { + /* Modify stack pointer as if LEA executed: */ + regs->sp += 8; + } + + /* + * Adjust registers as if RET executed: + * + * 1. Read the return address off the stack and into rIP: + */ + regs->ip = *(unsigned long *)(regs->sp); + + /* 2. Pop rIP off the stack: */ + regs->sp += 8; +} +#endif /* CONFIG_MITIGATION_SRSO */ diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S index 8f1fed0c3b83..f9ca1d8eabca 100644 --- a/arch/x86/lib/retpoline.S +++ b/arch/x86/lib/retpoline.S @@ -207,10 +207,24 @@ __EXPORT_THUNK(srso_alias_untrain_ret) .pushsection .text..__x86.rethunk_safe SYM_CODE_START_NOALIGN(srso_alias_safe_ret) + + /* + * Tell objtool that those are not function pointers referenced by + * __HANDLE_INTR_SAFERET(). Below too. + */ + ANNOTATE_NOENDBR + + /* + * Safe-RET sequence. If you need to change it, adjust + * handle_interrupted_saferet() too. + */ lea 8(%_ASM_SP), %_ASM_SP UNWIND_HINT_FUNC + + ANNOTATE_NOENDBR ANNOTATE_UNRET_SAFE ret + /* End of Safe-RET sequence */ int3 SYM_FUNC_END(srso_alias_safe_ret) @@ -245,8 +259,14 @@ SYM_CODE_START_LOCAL_NOALIGN(srso_untrain_ret) * the stack. */ SYM_INNER_LABEL(srso_safe_ret, SYM_L_GLOBAL) + /* + * Safe-RET sequence. If you need to change it, adjust + * handle_interrupted_saferet() too. + */ lea 8(%_ASM_SP), %_ASM_SP ret + /* End of Safe-RET sequence */ + int3 int3 /* end of movabs */ -- cgit v1.2.3 From 749d7aa0377aae32af8c0a4ad43371e7bf830ab5 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 23 Jul 2026 11:37:05 +0200 Subject: super: fix emergency thaw deadlock on frozen block devices do_thaw_all_callback() calls bdev_thaw() while holding sb->s_umount exclusively. If the block device was frozen via bdev_freeze() dropping the last block layer freeze reference calls fs_bdev_thaw() which reacquires s_umount: do_thaw_all_callback(sb) super_lock_excl(sb) # holds sb->s_umount bdev_thaw(sb->s_bdev) mutex_lock(&bdev->bd_fsfreeze_mutex) # bd_fsfreeze_count drops 1 -> 0 bd_holder_ops->thaw == fs_bdev_thaw get_bdev_super(bdev) bdev_super_lock(bdev, true) super_lock(sb, true) down_write(&sb->s_umount) # same task: deadlock The emergency thaw worker deadlocks against itself holding both s_umount and bd_fsfreeze_mutex. That fscks any subsequent unmount, freeze, or thaw of that filesystem and block device. [ 81.878470] sysrq: Show Blocked State [ 81.880140] task:kworker/0:1 state:D stack:0 pid:11 tgid:11 ppid:2 task_flags:0x4208060 flags:0x00080000 [ 81.884876] Workqueue: events do_thaw_all [ 81.886656] Call Trace: [ 81.887759] [ 81.888763] __schedule+0x579/0x1420 [ 81.890372] schedule+0x3a/0x100 [ 81.891794] schedule_preempt_disabled+0x15/0x30 [ 81.893848] rwsem_down_write_slowpath+0x1ea/0x900 [ 81.895191] ? __pfx_do_thaw_all_callback+0x10/0x10 [ 81.896528] down_write+0xbd/0xc0 [ 81.897505] super_lock+0x91/0x180 [ 81.898457] ? __mutex_lock+0xa99/0x1140 [ 81.900748] ? __mutex_unlock_slowpath+0x1f/0x400 [ 81.902069] bdev_super_lock+0x5b/0x150 [ 81.903132] get_bdev_super+0x10/0x60 [ 81.904042] fs_bdev_thaw+0x23/0xf0 [ 81.904755] bdev_thaw+0x82/0x100 [ 81.905484] do_thaw_all_callback+0x2c/0x50 [ 81.906298] __iterate_supers+0x5d/0x130 [ 81.907067] do_thaw_all+0x20/0x40 [ 81.907739] process_one_work+0x206/0x5e0 [ 81.908545] worker_thread+0x1e2/0x3c0 [ 81.909339] ? __pfx_worker_thread+0x10/0x10 [ 81.910171] kthread+0xf4/0x130 [ 81.910799] ? __pfx_kthread+0x10/0x10 [ 81.911528] ret_from_fork+0x2e2/0x3b0 [ 81.912259] ? __pfx_kthread+0x10/0x10 [ 81.913010] ret_from_fork_asm+0x1a/0x30 [ 81.913806] bdev_super_lock() even documents the violated requirement with lockdep_assert_not_held(&sb->s_umount). Acquiring bd_fsfreeze_mutex under s_umount also inverts the bd_fsfreeze_mutex vs. s_umount ordering established by bdev_{freeze,thaw}() and can thus ABBA against a concurrent block-layer freeze even when the recursive path isn't hit. Fix this by not holding s_umount around the bdev_thaw() loop at all. Pin the superblock with an active reference instead as filesystems_freeze_callback() does. The active reference keeps the superblock from being shut down and so ->s_bdev stays valid without holding s_umount. The block-layer-held freeze is dropped by fs_bdev_thaw() with FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE exactly as a regular unfreeze would and thaw_super_locked() handles filesystem-level freezes as before. The emergency thaw path has deadlocked like this in one form or another for a long long time but the current exclusively-held shape dates back to commit [1] where thaw_bdev() already ended in thaw_super() with s_umount held by do_thaw_all_callback(). Fixes: 08fdc8a0138a ("buffer.c: call thaw_super during emergency thaw") [1] Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260723-work-super-emergency_thaw-v1-1-7c315c600245@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/super.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/super.c b/fs/super.c index 70dcb07e7fa5..ffdcc6a2e0de 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1082,16 +1082,30 @@ void emergency_remount(void) } } +static inline bool get_active_super(struct super_block *sb) +{ + bool active = false; + + if (super_lock_excl(sb)) { + active = atomic_inc_not_zero(&sb->s_active); + super_unlock_excl(sb); + } + return active; +} + static void do_thaw_all_callback(struct super_block *sb, void *unused) { - if (!super_lock_excl(sb)) + if (!get_active_super(sb)) return; + /* fs_bdev_thaw() acquires s_umount so it must not be held here */ if (IS_ENABLED(CONFIG_BLOCK)) while (sb->s_bdev && !bdev_thaw(sb->s_bdev)) pr_warn("Emergency Thaw on %pg\n", sb->s_bdev); - thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL); + if (super_lock_excl(sb)) + thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL); + deactivate_super(sb); } static void do_thaw_all(struct work_struct *work) @@ -1117,17 +1131,6 @@ void emergency_thaw_all(void) } } -static inline bool get_active_super(struct super_block *sb) -{ - bool active = false; - - if (super_lock_excl(sb)) { - active = atomic_inc_not_zero(&sb->s_active); - super_unlock_excl(sb); - } - return active; -} - static const char *filesystems_freeze_ptr = "filesystems_freeze"; static void filesystems_freeze_callback(struct super_block *sb, void *freeze_all_ptr) -- cgit v1.2.3 From 738e6f32e61d80b554e37015ecb7bc620b88001c Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Mon, 20 Jul 2026 16:50:16 +0800 Subject: iommu/iommufd: Fix IOPF group ownership UAF iopf_group_alloc() links each last-page IOPF group into the generic IOPF pending list before invoking the domain fault handler. iommufd_fault_iopf_handler() also queued an accepted group in the IOMMUFD deliver list without removing it from the generic pending list. When detach or HWPT replacement drops the device's IOPF reference count to zero, an IOMMU driver may call iopf_queue_remove_device(). That function responds to and frees groups through the generic pending list without removing the same groups from IOMMUFD's deliver list or response xarray. A later read, response, or cleanup can then access the freed group and cause a UAF. Fix this by dequeuing an accepted group from the generic pending list before IOMMUFD queues it for userspace response. Make iopf_group_response() send a response regardless of pending-list membership, so the dequeued group can still be completed by IOMMUFD. Link: https://patch.msgid.link/r/3CFD314D0FE4D7EC+20260720085017.3998878-2-peiyang_he@smail.nju.edu.cn Closes: https://lore.kernel.org/all/B4F28798E2E784CA+d29f723c-b2b5-4b67-8d1c-4f7b9b0b27cb@smail.nju.edu.cn/ Fixes: 34765cbc679c ("iommufd: Associate fault object with iommufd_hw_pgtable") Cc: stable@vger.kernel.org Tested-by: Peiyang He Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Peiyang He Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/io-pgfault.c | 24 +++++++++++++++++++----- drivers/iommu/iommufd/eventq.c | 2 ++ include/linux/iommu.h | 5 +++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c index cca52a34d0ed..c16ff1fc4b95 100644 --- a/drivers/iommu/io-pgfault.c +++ b/drivers/iommu/io-pgfault.c @@ -332,16 +332,30 @@ void iopf_group_response(struct iopf_group *group, .code = status, }; - /* Only send response if there is a fault report pending */ mutex_lock(&fault_param->lock); - if (!list_empty(&group->pending_node)) { - ops->page_response(dev, &group->last_fault, &resp); - list_del_init(&group->pending_node); - } + ops->page_response(dev, &group->last_fault, &resp); + list_del_init(&group->pending_node); mutex_unlock(&fault_param->lock); } EXPORT_SYMBOL_GPL(iopf_group_response); +/** + * iopf_group_dequeue - Dequeue a page fault group from the pending list + * @group: the group to dequeue + * + * The fault handler is responsible for responding to the group after + * this function returns. + */ +void iopf_group_dequeue(struct iopf_group *group) +{ + struct iommu_fault_param *fault_param = group->fault_param; + + mutex_lock(&fault_param->lock); + list_del_init(&group->pending_node); + mutex_unlock(&fault_param->lock); +} +EXPORT_SYMBOL_GPL(iopf_group_dequeue); + /** * iopf_queue_discard_partial - Remove all pending partial fault * @queue: the queue whose partial faults need to be discarded diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 5129e3bf5461..747dd5155121 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -484,6 +484,8 @@ int iommufd_fault_iopf_handler(struct iopf_group *group) hwpt = group->attach_handle->domain->iommufd_hwpt; fault = hwpt->fault; + iopf_group_dequeue(group); + spin_lock(&fault->common.lock); list_add_tail(&group->node, &fault->common.deliver); spin_unlock(&fault->common.lock); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d20aa6f6863a..ac43b8b93f14 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -1704,6 +1704,7 @@ void iopf_free_group(struct iopf_group *group); int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt); void iopf_group_response(struct iopf_group *group, enum iommu_page_response_code status); +void iopf_group_dequeue(struct iopf_group *group); #else static inline int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev) @@ -1749,5 +1750,9 @@ static inline void iopf_group_response(struct iopf_group *group, enum iommu_page_response_code status) { } + +static inline void iopf_group_dequeue(struct iopf_group *group) +{ +} #endif /* CONFIG_IOMMU_IOPF */ #endif /* __LINUX_IOMMU_H */ -- cgit v1.2.3 From 98b87885de4b7f605533a2860685f5689fce8e82 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 14 Jul 2026 19:49:34 +0900 Subject: scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the target-supplied data segment. The segment carries a 2-byte sense length followed by the sense bytes, so it must hold 2 + senselen bytes, but the bounds check only requires datalen >= senselen: senselen = get_unaligned_be16(data); if (datalen < senselen) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); A target that returns a SCSI Response whose datalen equals senselen (with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + 2 read up to two bytes past the received data. Those bytes are stale conn->data contents and end up in the command's sense buffer, which is returned to userspace. Account for the 2-byte sense length prefix in the check. Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") Suggested-by: Sashiko AI Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Acked-by: Chris Leech Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/libiscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 160f02f2f51d..5cbc51899de0 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -918,7 +918,7 @@ invalid_datalen: } senselen = get_unaligned_be16(data); - if (datalen < senselen) + if (datalen < senselen + 2) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, -- cgit v1.2.3 From c1dea15f819cded9b3faf58f8bec72323568b6e6 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Thu, 16 Jul 2026 15:58:48 +0900 Subject: scsi: libiscsi_tcp: Bound SCSI Response data segment to the connection buffer iscsi_tcp_hdr_dissect() receives the data segment of several PDU types into the fixed-size conn->data buffer, which is allocated for ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU whose DataSegmentLength exceeds that buffer. The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its data segment (sense/response data) into conn->data via iscsi_tcp_data_recv_prep(), but it does so without the same check. The only upstream bound on in.datalen is conn->max_recv_dlength, the initiator's advertised MaxRecvDataSegmentLength, which is commonly negotiated well above 8192 (open-iscsi defaults to 262144). A target that returns a SCSI Response with a DataSegmentLength between 8193 and max_recv_dlength therefore overflows the 8192-byte conn->data buffer. Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly like those responses: bound the data segment, receive it into conn->data when present, and otherwise complete the PDU with no data. Fold the opcode into that case group rather than duplicating the check. Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") Suggested-by: Chris Leech Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Acked-by: Chris Leech Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/libiscsi_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index e90805ba868f..7223bb18b048 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); spin_unlock(&conn->session->back_lock); break; - case ISCSI_OP_SCSI_CMD_RSP: - if (tcp_conn->in.datalen) { - iscsi_tcp_data_recv_prep(tcp_conn); - return 0; - } - rc = iscsi_complete_pdu(conn, hdr, NULL, 0); - break; case ISCSI_OP_R2T: if (ahslen) { rc = ISCSI_ERR_AHSLEN; @@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) } rc = iscsi_tcp_r2t_rsp(conn, hdr); break; + case ISCSI_OP_SCSI_CMD_RSP: case ISCSI_OP_LOGIN_RSP: case ISCSI_OP_TEXT_RSP: case ISCSI_OP_REJECT: -- cgit v1.2.3 From 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 Mon Sep 17 00:00:00 2001 From: Xingui Yang Date: Thu, 16 Jul 2026 16:11:45 +0800 Subject: scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock: the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI devices and waits for the host to become runtime-active. But the host cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the drain is blocked on that very handler. However skipping the drain reintroduces a race: hisi_sas returns from resume before all PHY UP work and libsas discovery work finish. The controller may then autosuspend while disks are still waking up. The disks issue IO to a suspended controller, the IO fails, and the disks get disabled. Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT notification to after sas_drain_work(). By then the host resume is about to complete, so device removal through device_link no longer blocks on the resume and the cycle is broken. With the deadlock gone, restore sas_resume_ha() (the draining variant) in hisi_sas and remove sas_resume_ha_no_sync(). The reorder is safe for the other libsas consumers (isci, pm8001, aic94xx, mvsas). During suspend, sas_suspend_devices() calls sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a timed-out phy's disk is immediately rejected by the LLDD before reaching hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both complete directly via scsi_done() without entering SCSI EH. This is identical in both the old and new ordering since lldd_dev_gone runs during suspend, before resume. The reorder only affects when the PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() vs. asynchronous after resume returns), not whether I/O can reach the device. aic94xx and mvsas do not register any PM ops and never reach this code path. Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") Signed-off-by: Xingui Yang Reviewed-by: John Garry Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +-------- drivers/scsi/libsas/sas_init.c | 37 +++++++++++++++++----------------- include/scsi/libsas.h | 1 - 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 213d5b5dea94..8a2500993e19 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device) return rc; } phys_init_v3_hw(hisi_hba); - - /* - * If a directly-attached disk is removed during suspend, a deadlock - * may occur, as the PHYE_RESUME_TIMEOUT processing will require the - * hisi_hba->device to be active, which can only happen when resume - * completes. So don't wait for the HA event workqueue to drain upon - * resume. - */ - sas_resume_ha_no_sync(sha); + sas_resume_ha(sha); clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); dev_warn(dev, "end of resuming controller\n"); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 0bec236f0fb5..c3f3d05b46de 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -410,7 +410,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) } } -static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) +void sas_resume_ha(struct sas_ha_struct *ha) { const unsigned long tmo = msecs_to_jiffies(25000); int i; @@ -426,6 +426,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", i, i > 1 ? "s" : ""); wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); + + /* + * All phys are back up or timed out. Turn on I/O and drain + * pending work. + */ + scsi_unblock_requests(ha->shost); + sas_drain_work(ha); + + /* + * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler + * calls sas_deform_port() -> sas_destruct_devices(), which removes + * SCSI devices and, for LLDDs using device_link() PM sync, waits + * for the host to be runtime-active. Sending it before the drain + * would deadlock: the drain waits for the handler, the handler + * waits for host resume, and host resume waits for the drain to + * finish. + */ for (i = 0; i < ha->num_phys; i++) { struct asd_sas_phy *phy = ha->sas_phy[i]; @@ -436,12 +453,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) } } - /* all phys are back up or timed out, turn on i/o so we can - * flush out disks that did not return - */ - scsi_unblock_requests(ha->shost); - if (drain) - sas_drain_work(ha); clear_bit(SAS_HA_RESUMING, &ha->state); sas_queue_deferred_work(ha); @@ -450,20 +461,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) */ sas_resume_insert_broadcast_ha(ha); } - -void sas_resume_ha(struct sas_ha_struct *ha) -{ - _sas_resume_ha(ha, true); -} EXPORT_SYMBOL(sas_resume_ha); -/* A no-sync variant, which does not call sas_drain_ha(). */ -void sas_resume_ha_no_sync(struct sas_ha_struct *ha) -{ - _sas_resume_ha(ha, false); -} -EXPORT_SYMBOL(sas_resume_ha_no_sync); - void sas_suspend_ha(struct sas_ha_struct *ha) { int i; diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 163f23c92b41..36d4cb567837 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -680,7 +680,6 @@ extern int sas_register_ha(struct sas_ha_struct *); extern int sas_unregister_ha(struct sas_ha_struct *); extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); extern void sas_resume_ha(struct sas_ha_struct *sas_ha); -extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); int sas_phy_reset(struct sas_phy *phy, int hard_reset); -- cgit v1.2.3 From 4d5282c06ca198319c2de41b10511ddcb8068f42 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 20 Jul 2026 10:35:52 +0800 Subject: scsi: ufs: dt-bindings: Add missing mcq reg for qcom,sa8255p-ufshc Qualcomm UFS controller found on SoCs SA8255P/SA8797P has a MCQ I/O address space. It should be defined in the bindings even though Linux driver currently doesn't utilize it. Fix the binding before it gets adopted by DTS. Fixes: e2725ed2a7fb ("scsi: ufs: dt-bindings: Document bindings for SA8255P UFS Host Controller") Cc: stable@vger.kernel.org Signed-off-by: Shawn Guo Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260720023552.2667237-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml index db165a235cb6..66d73e9025d6 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml @@ -18,7 +18,14 @@ properties: - const: qcom,sa8255p-ufshc reg: - maxItems: 1 + minItems: 1 + maxItems: 2 + + reg-names: + minItems: 1 + items: + - const: std + - const: mcq interrupts: maxItems: 1 -- cgit v1.2.3 From f5098b6bae761e346ebcd9da7f95622c04733cff Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 26 Jul 2026 14:45:48 -0700 Subject: Linux 7.2-rc5 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b86c0d7ed0e5..2f5bf24e3389 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc4 +EXTRAVERSION = -rc5 NAME = Baby Opossum Posse # *DOCUMENTATION* -- cgit v1.2.3 From 9349dd0646673964eb9e993050526029fc599677 Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Wed, 22 Jul 2026 14:18:44 -0700 Subject: smb: client: use GFP_KERNEL for registry allocation Currently, cifs_get_swn_reg() allocates new registry entries using GFP_ATOMIC. Since we lock a mutex here, this is clearly not an atomic context. Use GFP_KERNEL instead. Also, fix a minor grammatical error in the comment above the function. Signed-off-by: Fredric Cover Signed-off-by: Steve French --- fs/smb/client/cifs_swn.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c index 9951817d0d7f..fe10719e627e 100644 --- a/fs/smb/client/cifs_swn.c +++ b/fs/smb/client/cifs_swn.c @@ -425,7 +425,7 @@ static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon) /* * Get a registration for the tcon's server and share name, allocating a new one if it does not - * exists + * exist. */ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) { @@ -443,7 +443,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) goto unlock; } - reg = kmalloc_obj(struct cifs_swn_reg, GFP_ATOMIC); + reg = kmalloc_obj(struct cifs_swn_reg, GFP_KERNEL); if (reg == NULL) { ret = -ENOMEM; goto fail_unlock; @@ -451,7 +451,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) kref_init(®->ref_count); - reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC); + reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_KERNEL); if (reg->id < 0) { cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__); ret = reg->id; -- cgit v1.2.3 From 6a3e16d60e81a4aa3056ab15617036cfbea2e07d Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sun, 28 Jun 2026 14:59:09 +0800 Subject: smb: client: fix buffer leaks in SMB1 read and write CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request buffer before checking whether tcon->ses->server is NULL. If that defensive check ever fails, the helper returns -ECONNABORTED without releasing the request buffer. Fix these leaks by releasing the allocated request buffer before returning from these error paths. Use cifs_small_buf_release() for the buffers allocated by small_smb_init() and cifs_buf_release() for the buffer allocated by smb_init(). The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1. An x86_64 allyesconfig build showed no new warnings. Runtime validation used a temporary fault-injection hook to force tcon->ses->server to NULL after request-buffer initialization. On the unfixed kernel, the harness observed two leaked small request buffers and one leaked large request buffer, with directed kmemleak dumps confirming the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer deltas remained. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Dawei Feng Signed-off-by: Steve French --- fs/smb/client/cifssmb.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 40162d5554ea..1f77512252e7 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -1681,8 +1681,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_small_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; @@ -1796,8 +1798,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; @@ -2077,8 +2081,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_small_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; -- cgit v1.2.3 From aed0714255c80d143e9f6d4ae00ee14423204ad5 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 24 Jul 2026 03:45:36 +0900 Subject: smb: client: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Signed-off-by: Steve French --- fs/smb/client/smb1maperror.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/smb/client/smb1maperror.c b/fs/smb/client/smb1maperror.c index ab3d09613c91..395299f9121b 100644 --- a/fs/smb/client/smb1maperror.c +++ b/fs/smb/client/smb1maperror.c @@ -234,11 +234,7 @@ int __init smb1_init_maperror(void) if (rc) return rc; - rc = mapping_table_ERRSRV_is_sorted(); - if (rc) - return rc; - - return rc; + return mapping_table_ERRSRV_is_sorted(); } #if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS) -- cgit v1.2.3 From 455488cd5054bcc59db40fa1cc2c004031a5b2a5 Mon Sep 17 00:00:00 2001 From: Li Qiang Date: Sun, 19 Jul 2026 00:22:27 +0800 Subject: cifs: validate idmap key payload length The cifs.idmap key type stores its payload length in key->datalen, which is limited to U16_MAX. Accepting a larger key payload truncates the recorded length and can make later users interpret the payload using inconsistent bounds. Reject oversized preparsed payloads before allocating or copying them. This keeps key->datalen consistent with the stored data for both inline and separately allocated idmap payloads. Signed-off-by: Li Qiang Signed-off-by: Steve French --- fs/smb/client/cifsacl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 9424281a7674..12005f46307d 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -68,6 +68,9 @@ cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) { char *payload; + if (prep->datalen > U16_MAX) + return -EINVAL; + /* * If the payload is less than or equal to the size of a pointer, then * an allocation here is wasteful. Just copy the data directly to the -- cgit v1.2.3 From 96b2dbbe58a1ea5df8d29c2fe24b5f04715f4443 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 27 Jul 2026 12:27:39 +0800 Subject: erofs: ensure valid f_path for page cache sharing Previously, backing files for page cache sharing were set up with f_path left as NULL (only f_inode was valid). It worked, but a recent mincore fix relies on f_path.mnt and crashes (found by "erofs/028" on 7.2-rc4): BUG: kernel NULL pointer dereference, address: 0000000000000018 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: Oops: 0000 [#1] SMP PTI CPU: 3 UID: 0 PID: 675528 Comm: fincore Not tainted 7.2.0-rc4-00002-g[]-dirty #1 PREEMPT(lazy) Hardware name: Red Hat KVM, BIOS 1.16.0-4.al8 04/01/2014 RIP: 0010:__do_sys_mincore+0xc0/0x2c0 ... Specify valid paths using valid disconnected dentries together with erofs_ishare_mnt instead of leaving f_path empty, so they are more like real backing files in a pseudo filesystem and standard backing_file_open() can be used directly. Fixes: e187bc02f8fa ("mm: do file ownership checks with the proper mount idmap") Acked-by: Hongbo Li Signed-off-by: Gao Xiang --- fs/erofs/Kconfig | 1 + fs/erofs/internal.h | 4 ++-- fs/erofs/ishare.c | 58 ++++++++++++++++++++++++++--------------------------- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index 4789b1077d8c..1feb28cfe557 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -189,6 +189,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI config EROFS_FS_PAGE_CACHE_SHARE bool "EROFS page cache share support (experimental)" depends on EROFS_FS && EROFS_FS_XATTR + select FS_STACK help This enables page cache sharing among inodes with identical content fingerprints on the same machine. diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 580f8d9f14e7..57bd21859c65 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -288,8 +288,8 @@ struct erofs_inode { struct erofs_inode_fingerprint fingerprint; spinlock_t ishare_lock; }; - /* for each real inode */ - struct inode *sharedinode; + /* for each real filesystem inode */ + struct dentry *sharedentry; }; #endif /* the corresponding vfs inode */ diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c index a1a20a5ba548..fa7d4112dec5 100644 --- a/fs/erofs/ishare.c +++ b/fs/erofs/ishare.c @@ -2,14 +2,13 @@ /* * Copyright (C) 2024, Alibaba Cloud */ +#include #include #include #include #include "internal.h" #include "xattr.h" -#include "../internal.h" - static struct vfsmount *erofs_ishare_mnt; static int erofs_ishare_iget5_eq(struct inode *inode, void *data) @@ -33,10 +32,12 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data) bool erofs_ishare_fill_inode(struct inode *inode) { + static const struct file_operations empty_fops = {}; struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); const struct address_space_operations *aops; struct erofs_inode *vi = EROFS_I(inode); struct erofs_inode_fingerprint fp; + struct dentry *sd; struct inode *si; aops = erofs_get_aops(inode); @@ -49,7 +50,9 @@ bool erofs_ishare_fill_inode(struct inode *inode) xxh32(fp.opaque, fp.size, 0), erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp); if (si && (inode_state_read_once(si) & I_NEW)) { + si->i_fop = &empty_fops; si->i_mapping->a_ops = aops; + si->i_mode = 0444 | S_IFREG; si->i_size = inode->i_size; unlock_new_inode(si); } else { @@ -65,7 +68,10 @@ bool erofs_ishare_fill_inode(struct inode *inode) return false; } } - vi->sharedinode = si; + sd = d_obtain_alias(si); /* disconnected denties for sharedinodes */ + if (IS_ERR(sd)) + return false; + vi->sharedentry = sd; INIT_LIST_HEAD(&vi->ishare_list); spin_lock(&EROFS_I(si)->ishare_lock); list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list); @@ -75,48 +81,40 @@ bool erofs_ishare_fill_inode(struct inode *inode) void erofs_ishare_free_inode(struct inode *inode) { - struct erofs_inode *vi = EROFS_I(inode); - struct inode *sharedinode = vi->sharedinode; + struct erofs_inode *vi = EROFS_I(inode), *svi; - if (!sharedinode) + if (!vi->sharedentry) return; - spin_lock(&EROFS_I(sharedinode)->ishare_lock); + svi = EROFS_I(d_inode(vi->sharedentry)); + spin_lock(&svi->ishare_lock); list_del(&vi->ishare_list); - spin_unlock(&EROFS_I(sharedinode)->ishare_lock); - iput(sharedinode); - vi->sharedinode = NULL; + spin_unlock(&svi->ishare_lock); + dput(vi->sharedentry); + vi->sharedentry = NULL; } static int erofs_ishare_file_open(struct inode *inode, struct file *file) { - struct inode *sharedinode = EROFS_I(inode)->sharedinode; - struct file *realfile; + struct path sharedpath = { + .mnt = erofs_ishare_mnt, + .dentry = EROFS_I(inode)->sharedentry, + }; + struct file *rf; if (file->f_flags & O_DIRECT) return -EINVAL; - realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred(), - file); - if (IS_ERR(realfile)) - return PTR_ERR(realfile); - ihold(sharedinode); - realfile->f_op = &erofs_file_fops; - realfile->f_inode = sharedinode; - realfile->f_mapping = sharedinode->i_mapping; - path_get(&file->f_path); - backing_file_set_user_path(realfile, &file->f_path); - - file_ra_state_init(&realfile->f_ra, file->f_mapping); - realfile->private_data = EROFS_I(inode); - file->private_data = realfile; + + rf = backing_file_open(file, file->f_flags | O_NOATIME, + &sharedpath, current_cred()); + if (IS_ERR(rf)) + return PTR_ERR(rf); + file->private_data = rf; return 0; } static int erofs_ishare_file_release(struct inode *inode, struct file *file) { - struct file *realfile = file->private_data; - - iput(realfile->f_inode); - fput(realfile); + fput(file->private_data); file->private_data = NULL; return 0; } -- cgit v1.2.3 From c9b47e6b23114e939b17f818471c7a46e59006e7 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 14 Jul 2026 07:47:29 -0400 Subject: erofs: cap LZMA stream pool size fs/erofs/decompressor_lzma.c sizes the module-global MicroLZMA stream pool from num_possible_cpus() when the lzma_streams module parameter is unset, then z_erofs_load_lzma_config() preallocates one image-supplied dictionary per stream, accepting dictionaries up to 8 MiB. On high-CPU systems, a small EROFS image can pin hundreds of MiB of vmalloc-backed decoder state until the erofs module is unloaded. Impact: An EROFS image mounted by the system can pin up to 8 MiB of vmalloc memory per LZMA stream, either as intended or unexpectedly. Bound the default stream count by a new CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS option, default 16, so the worst-case default preallocation is 128 MiB if the number of CPUs is no less than 16 while preserving the existing per-image dictionary limit. An explicit lzma_streams module parameter is still honoured as-is, so administrators who deliberately size the pool are not affected. Fixes: 622ceaddb764 ("erofs: lzma compression support") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Reviewed-by: Gao Xiang Signed-off-by: Gao Xiang --- fs/erofs/Kconfig | 14 ++++++++++++++ fs/erofs/decompressor_lzma.c | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index 1feb28cfe557..8ca1767dafb6 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -131,6 +131,20 @@ config EROFS_FS_ZIP_LZMA Say N if you want to disable LZMA compression support. +config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS + int "EROFS LZMA default maximum decompression streams" + depends on EROFS_FS_ZIP_LZMA + range 1 NR_CPUS + default 16 + help + By default EROFS allocates one LZMA decompression stream per CPU. + Each stream can hold a dictionary of up to 8 MiB taken from the + mounted image, so on systems with many CPUs this can reserve a lot + of memory. This caps the default; the lzma_streams module parameter + still overrides it. + + If unsure, keep the default of 16. + config EROFS_FS_ZIP_DEFLATE bool "EROFS DEFLATE compressed data support" depends on EROFS_FS_ZIP diff --git a/fs/erofs/decompressor_lzma.c b/fs/erofs/decompressor_lzma.c index f6692d0f2f04..6b0cdb446c6a 100644 --- a/fs/erofs/decompressor_lzma.c +++ b/fs/erofs/decompressor_lzma.c @@ -51,7 +51,8 @@ static int __init z_erofs_lzma_init(void) /* by default, use # of possible CPUs instead */ if (!z_erofs_lzma_nstrms) - z_erofs_lzma_nstrms = num_possible_cpus(); + z_erofs_lzma_nstrms = min_t(unsigned int, num_possible_cpus(), + CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS); for (i = 0; i < z_erofs_lzma_nstrms; ++i) { struct z_erofs_lzma *strm = kzalloc_obj(*strm); -- cgit v1.2.3 From 9972befc3e34ff8b6847198c84f11bfc312dde40 Mon Sep 17 00:00:00 2001 From: Jaehoon Kim Date: Fri, 24 Jul 2026 08:39:43 -0500 Subject: KVM: s390: Fall back to short-term pinning in MAP ioctl FOLL_LONGTERM pinning fails for some memory types, such as file-backed guest memory. As a result, kvm_s390_adapter_map() returns -EINVAL and irqfd adapter registration fails even though interrupt delivery could still work via the existing non-atomic path. When FOLL_LONGTERM pinning fails, verify that the page is accessible using a short-term pin instead. If the short-term pin succeeds, unpin the page and add a map entry with pinned=false to preserve MAP/UNMAP symmetry. The non-atomic irqfd path already performs short-term pinning for interrupt delivery, so this restores the previous behavior for memory that cannot be pinned long-term. get_map_info() is updated to return NULL for unpinned entries so that the atomic irqfd fast path falls back to the non-atomic path. kvm_s390_adapter_unmap() and kvm_s390_unmap_all_adapters() skip dirty marking and unpin for unpinned entries. Update Documentation/virt/kvm/devices/s390_flic.rst to reflect the new MAP/UNMAP behavior. Fixes: c9a568838086 ("KVM: s390: Add map/unmap ioctl and clean mappings post-guest") Signed-off-by: Jaehoon Kim Reviewed-by: Douglas Freimuth Reviewed-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- Documentation/virt/kvm/devices/s390_flic.rst | 15 +++++++- arch/s390/include/asm/kvm_host.h | 5 +++ arch/s390/kvm/interrupt.c | 56 +++++++++++++++++++++------- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/Documentation/virt/kvm/devices/s390_flic.rst b/Documentation/virt/kvm/devices/s390_flic.rst index b784f8016748..983c858b444a 100644 --- a/Documentation/virt/kvm/devices/s390_flic.rst +++ b/Documentation/virt/kvm/devices/s390_flic.rst @@ -112,9 +112,20 @@ Groups: mask or unmask the adapter, as specified in mask KVM_S390_IO_ADAPTER_MAP - This is now a no-op. The mapping is purely done by the irq route. + Map an adapter indicator or summary page for long-term pinning so that + interrupt injection can be performed in atomic context. If long-term + pinning is not possible (e.g. file-backed memory), the page is verified + via a short-term pin and the ioctl returns success; interrupt injection + will use the non-atomic irqfd path with short-term pinning on each + interrupt. In Secure Execution mode this is a no-op and the ioctl + returns success. + KVM_S390_IO_ADAPTER_UNMAP - This is now a no-op. The mapping is purely done by the irq route. + Unmap a previously mapped adapter indicator or summary page and release + the long-term pin. If the page was not long-term pinned (e.g. file-backed + memory), the map entry is removed and success is returned; if no prior + map entry exists, -ENOENT is returned. In Secure Execution mode this is + a no-op and the ioctl returns success. KVM_DEV_FLIC_AISM modify the adapter-interruption-suppression mode for a given isc if the diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index eaa34c5bd3c1..c172f9b212d1 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -476,6 +476,11 @@ struct s390_map_info { __u64 guest_addr; __u64 addr; struct page *page; + /* + * True if the page is long-term pinned. False if long-term pinning + * failed and this entry exists only to preserve MAP/UNMAP symmetry. + */ + bool pinned; }; struct s390_io_adapter { diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 9e3e6b0d72ad..7514d9e2403c 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -2520,8 +2520,22 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr) map->addr = host_addr; map->page = pin_map_page(kvm, host_addr, FOLL_LONGTERM); if (!map->page) { - ret = -EINVAL; - goto out; + /* + * Long-term pinning may fail for memory types such as file-backed + * memory. Verify that short-term pinning succeeds so that the + * non-atomic irqfd path can handle interrupt injection. + */ + map->page = pin_map_page(kvm, host_addr, 0); + if (!map->page) { + ret = -EINVAL; + goto out; + } + unpin_user_page(map->page); + map->page = NULL; + map->pinned = false; + /* Add an entry to preserve MAP/UNMAP symmetry. */ + } else { + map->pinned = true; } spin_lock_irqsave(&adapter->maps_lock, flags); if (adapter->nr_maps < MAX_S390_ADAPTER_MAPS) { @@ -2532,7 +2546,7 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr) ret = -EINVAL; } spin_unlock_irqrestore(&adapter->maps_lock, flags); - if (ret) + if (ret && map->page) unpin_user_page(map->page); out: if (ret) @@ -2546,6 +2560,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) struct s390_map_info *map, *tmp, *map_to_free; struct page *map_page_to_put = NULL; u64 map_addr_to_mark = 0; + bool map_pinned = false; unsigned long flags; int found = 0, idx; @@ -2560,6 +2575,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) list_del(&map->list); map_page_to_put = map->page; map_addr_to_mark = map->guest_addr; + map_pinned = map->pinned; map_to_free = map; break; } @@ -2568,11 +2584,18 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) if (found) { kfree(map_to_free); - idx = srcu_read_lock(&kvm->srcu); - mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT); - set_page_dirty_lock(map_page_to_put); - srcu_read_unlock(&kvm->srcu, idx); - unpin_user_page(map_page_to_put); + if (map_pinned) { + /* + * Only long-term pinned pages need to be marked dirty + * and released. Fallback entries exist only for + * MAP/UNMAP symmetry. + */ + idx = srcu_read_lock(&kvm->srcu); + mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT); + set_page_dirty_lock(map_page_to_put); + srcu_read_unlock(&kvm->srcu, idx); + unpin_user_page(map_page_to_put); + } } return found ? 0 : -ENOENT; @@ -2598,11 +2621,13 @@ void kvm_s390_unmap_all_adapters(struct kvm *kvm) list_for_each_entry_safe(map, tmp, &local_list, list) { list_del(&map->list); - idx = srcu_read_lock(&kvm->srcu); - mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT); - set_page_dirty_lock(map->page); - srcu_read_unlock(&kvm->srcu, idx); - unpin_user_page(map->page); + if (map->pinned) { + idx = srcu_read_lock(&kvm->srcu); + mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT); + set_page_dirty_lock(map->page); + srcu_read_unlock(&kvm->srcu, idx); + unpin_user_page(map->page); + } kfree(map); } } @@ -2929,8 +2954,11 @@ static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter, return NULL; list_for_each_entry(map, &adapter->maps, list) { - if (map->addr == addr) + if (map->addr == addr) { + if (!map->pinned) + return NULL; return map; + } } return NULL; } -- cgit v1.2.3 From 3e013bc8b941bd52c8e3a99798d0ae8792cb71ca Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 8 Jul 2026 17:20:38 +0800 Subject: pmdomain: mediatek: fix remaining %pOF after of_node_put() scpsys_get_bus_protection_legacy() looks up several legacy bus protection regmaps from device-tree nodes. Two error paths put the device node before checking whether the regmap lookup failed, but still pass that node to dev_err_probe() with %pOF on failure. If of_node_put() drops the last reference, the later %pOF formatting can dereference a freed device node. Keep the node reference until after the error message has been emitted in the infracfg and SMI lookup paths. Also drop the SMI node before returning when the SMI phandle is missing. Fixes: c29345fa5f66 ("pmdomain: mediatek: Refactor bus protection regmaps retrieval") Signed-off-by: Guangshuo Li Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-pm-domains.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index 881a1a3b3632..6089c8cf5bbc 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -1050,12 +1050,15 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s node = of_find_node_with_property(np, "mediatek,infracfg"); if (node) { regmap[0] = syscon_regmap_lookup_by_phandle(node, "mediatek,infracfg"); - of_node_put(node); num_regmaps++; - if (IS_ERR(regmap[0])) - return dev_err_probe(dev, PTR_ERR(regmap[0]), + if (IS_ERR(regmap[0])) { + ret = dev_err_probe(dev, PTR_ERR(regmap[0]), "%pOF: failed to get infracfg regmap\n", node); + of_node_put(node); + return ret; + } + of_node_put(node); } else { regmap[0] = NULL; } @@ -1064,17 +1067,22 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s node = of_find_node_with_property(np, "mediatek,smi"); if (node) { smi_np = of_parse_phandle(node, "mediatek,smi", 0); - of_node_put(node); - if (!smi_np) + if (!smi_np) { + of_node_put(node); return -ENODEV; + } regmap[1] = device_node_to_regmap(smi_np); num_regmaps++; of_node_put(smi_np); - if (IS_ERR(regmap[1])) - return dev_err_probe(dev, PTR_ERR(regmap[1]), + if (IS_ERR(regmap[1])) { + ret = dev_err_probe(dev, PTR_ERR(regmap[1]), "%pOF: failed to get SMI regmap\n", node); + of_node_put(node); + return ret; + } + of_node_put(node); } else { regmap[1] = NULL; } -- cgit v1.2.3 From 97b228e596740fca5eeeb1d8e619c1f457d7e2f0 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 11 Jun 2026 14:45:30 +0800 Subject: thunderbolt: stream: Unmap buffers with mapped size The size passed to dma_unmap_page() must match the size used for the corresponding dma_map_page() call. Stream RX and TX buffers are mapped with TB_MAX_FRAME_SIZE when the buffer pools are allocated. However, tbstream_ring_free() currently uses tb_ring_frame_size() as the unmap size. That helper returns the current frame payload size, not the DMA mapping size. On the TX path, tbstream_dev_alloc_tx() stores a shorter payload length in frame.size when the payload is smaller than TB_MAX_FRAME_SIZE. This happens for a short final DATA frame, and also for the CLOSE frame, which is allocated with SZ_256. In those cases the buffer was mapped with TB_MAX_FRAME_SIZE, but tb_ring_frame_size() returns the shorter frame payload length. This makes the dma_unmap_page() size differ from the original dma_map_page() size. Use TB_MAX_FRAME_SIZE when unmapping stream buffers so the unmap size matches the DMA mapping size used by the buffer allocation paths. Signed-off-by: Xu Rao Fixes: 6db21d817b43 ("thunderbolt: Add support for USB4STREAM") Signed-off-by: Mika Westerberg --- drivers/thunderbolt/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c index c1f5c55583d0..4cc86d8d6491 100644 --- a/drivers/thunderbolt/stream.c +++ b/drivers/thunderbolt/stream.c @@ -257,7 +257,7 @@ static void tbstream_ring_free(struct tbstream_ring *ring) if (sf->frame.buffer_phy) dma_unmap_page(dma_dev, sf->frame.buffer_phy, - tb_ring_frame_size(&sf->frame), dir); + TB_MAX_FRAME_SIZE, dir); sf->frame.buffer_phy = 0; if (sf->page) __free_page(sf->page); -- cgit v1.2.3 From d2ee4d47aacbd2ba456092eeec670dba35fde291 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Wed, 24 Jun 2026 14:27:03 +0800 Subject: thunderbolt: Fix bandwidth group reservation indexing Valid bandwidth group IDs range from 1 through MAX_GROUPS, while Group ID 0 is reserved. tb_consumed_dp_bandwidth() uses the Group ID directly to index its local group_reserved[] array. The array currently has MAX_GROUPS entries, so its valid indices are 0 through MAX_GROUPS - 1. Group ID MAX_GROUPS therefore accesses one element past the end, and the final group's reserved bandwidth is not included when the array is summed. Give group_reserved[] MAX_GROUPS + 1 entries so direct Group ID indexing covers the reserved ID 0 and valid IDs 1 through MAX_GROUPS. Fixes: 52a4490e89d7 ("thunderbolt: Reserve released DisplayPort bandwidth for a group for 10 seconds") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 76323255439a..f43f2d952372 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -609,7 +609,7 @@ static int tb_consumed_dp_bandwidth(struct tb *tb, int *consumed_up, int *consumed_down) { - int group_reserved[MAX_GROUPS] = {}; + int group_reserved[MAX_GROUPS + 1] = {}; struct tb_cm *tcm = tb_priv(tb); struct tb_tunnel *tunnel; bool downstream; -- cgit v1.2.3 From d6764992f17b23d91ff93ce905ab53c2aa7191f0 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 25 Jun 2026 06:54:09 -0500 Subject: thunderbolt: Bound the DROM dual link port number before indexing sw->ports tb_drom_parse_entry_port() validates the device-supplied header->index against sw->config.max_port_number before indexing sw->ports[], but the sibling field entry->dual_link_port_nr -- a 6-bit value also read from the DROM -- indexes the same array with no such check. A malicious or malformed Thunderbolt device can set dual_link_port_nr beyond the allocated sw->ports[] (max_port_number + 1 entries), producing an out-of-bounds tb_port pointer that is stored and later dereferenced. Reject a port entry whose dual_link_port_nr exceeds max_port_number, the same bound already applied to header->index. Fixes: cd22e73bdf5e ("thunderbolt: Read port configuration from eeprom.") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: Mika Westerberg --- drivers/thunderbolt/eeprom.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 5681c17f82ec..2a13fa6888ba 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -394,9 +394,16 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw, return -EIO; } port->link_nr = entry->link_nr; - if (entry->has_dual_link_port) + if (entry->has_dual_link_port) { + if (entry->dual_link_port_nr > sw->config.max_port_number) { + tb_sw_warn(sw, + "port entry has invalid dual link port number %u\n", + entry->dual_link_port_nr); + return -EIO; + } port->dual_link_port = &port->sw->ports[entry->dual_link_port_nr]; + } } return 0; } -- cgit v1.2.3 From e48844ece5e3ed1d1eb865f6da2b16f62cd9f86d Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 13 Jul 2026 17:32:37 +0800 Subject: thunderbolt: icm: Preserve USB4 proxy data-valid bit The ICM USB4 switch operation request encodes two values in request.data_len_valid: bit 4 marks the data payload valid, while bits 3:0 hold the payload length in dwords. A zero length with the valid bit set represents the full 16-dword data array. icm_usb4_switch_op() sets the valid bit when a transmit payload is present. For payloads shorter than the full 16 dwords, it then assigns the length to the whole field and clears the valid bit that was just set. The payload is still copied into the request, but the descriptor sent to firmware marks that data as invalid. This affects USB4 router operations that send short payloads through the firmware connection manager. In particular, USB4 NVM writes can send a short final block when the image size is not aligned to the 64-byte proxy payload size. Firmware may then ignore or reject that final block, while full 16-dword blocks are unaffected because they are encoded as length 0 with the valid bit set. OR the short payload length into data_len_valid so the valid bit is preserved. Fixes: 9039387e166e ("thunderbolt: Add USB4 router operation proxy for firmware connection manager") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 10fefac3b1d9..669807f0eaf8 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -2341,7 +2341,7 @@ static int icm_usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, if (tx_data_len) { request.data_len_valid |= ICM_USB4_SWITCH_DATA_VALID; if (tx_data_len < ARRAY_SIZE(request.data)) - request.data_len_valid = + request.data_len_valid |= tx_data_len & ICM_USB4_SWITCH_DATA_LEN_MASK; memcpy(request.data, tx_data, tx_data_len * sizeof(u32)); } -- cgit v1.2.3 From 67ff4bf723c8bd1f1b10450fa3e8f55762418104 Mon Sep 17 00:00:00 2001 From: Alex Tran Date: Fri, 24 Jul 2026 09:42:28 -0700 Subject: gpio: gpio-by-pinctrl: Apply initial value in direction output wrapper After successfully configuring gpio pin as output, set the requested initial output value via the existing gpio set wrapper, so that the pin is not left at its previous level. Fixes: 7671f4949a6c ("gpio: gpio-by-pinctrl: add pinctrl based generic GPIO driver") Signed-off-by: Alex Tran Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260724-gpio-pinctrl-output-set-val-v2-1-cad55d025636@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-by-pinctrl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-by-pinctrl.c b/drivers/gpio/gpio-by-pinctrl.c index 7d7c48ce5163..fb8440acb31d 100644 --- a/drivers/gpio/gpio-by-pinctrl.c +++ b/drivers/gpio/gpio-by-pinctrl.c @@ -27,12 +27,6 @@ static int pin_control_gpio_get_direction(struct gpio_chip *gc, unsigned int off return GPIO_LINE_DIRECTION_IN; } -static int pin_control_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int val) -{ - return pinctrl_gpio_direction_output(chip, offset); -} - static int pin_control_gpio_get(struct gpio_chip *chip, unsigned int offset) { unsigned long config; @@ -55,6 +49,18 @@ static int pin_control_gpio_set(struct gpio_chip *chip, unsigned int offset, return pinctrl_gpio_set_config(chip, offset, config); } +static int pin_control_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int val) +{ + int ret; + + ret = pinctrl_gpio_direction_output(chip, offset); + if (ret) + return ret; + + return pin_control_gpio_set(chip, offset, val); +} + static int pin_control_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; -- cgit v1.2.3 From 6a39ca1286dd97ad02bb8e03bbb65ee05368d778 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 Jul 2026 21:48:36 +0300 Subject: drm/bridge: display-connector: Fix I2C adapter resource leak If the probe function returns an error after getting the I2C adapter for DDC, the reference to the adapter is never released. Fix it by releasing it in the bridge .destroy() handler. There is no need to test the ddc pointer with !IS_ERR(), as of_get_i2c_adapter_by_node() returns NULL on error. Fixes: 2e2bf3a5584d ("drm/bridge: display-connector: add DP support") Cc: stable@vger.kernel.org Signed-off-by: Laurent Pinchart Reviewed-by: Johan Hovold Reviewed-by: Luca Ceresoli Link: https://patch.msgid.link/20260717184836.2017386-1-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/bridge/display-connector.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index 16c0631adeb1..6b128fabe3a9 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -40,6 +40,13 @@ static int display_connector_attach(struct drm_bridge *bridge, return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL; } +static void display_connector_destroy(struct drm_bridge *bridge) +{ + struct display_connector *conn = to_display_connector(bridge); + + i2c_put_adapter(conn->bridge.ddc); +} + static enum drm_connector_status display_connector_detect(struct drm_bridge *bridge) { struct display_connector *conn = to_display_connector(bridge); @@ -177,6 +184,7 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs display_connector_bridge_funcs = { .attach = display_connector_attach, + .destroy = display_connector_destroy, .detect = display_connector_bridge_detect, .edid_read = display_connector_edid_read, .atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts, @@ -403,9 +411,6 @@ static void display_connector_remove(struct platform_device *pdev) regulator_disable(conn->supply); drm_bridge_remove(&conn->bridge); - - if (!IS_ERR(conn->bridge.ddc)) - i2c_put_adapter(conn->bridge.ddc); } static const struct of_device_id display_connector_match[] = { -- cgit v1.2.3 From d233087c19f6607ef926ac3f47d776e2406ffd1f Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Mon, 27 Jul 2026 15:02:05 +0700 Subject: gpio: pca953x: fix cache_only and IRQ state on restore_context() failure When pca953x_restore_context() fails, cache_only is left disabled and the IRQ left enabled, even though register synchronization may not have completed successfully. Restore cache_only and disable the IRQ again on failure, matching the state set by pca953x_save_context(). Fixes: ec5bde62019b ("gpio: pca953x: Split pca953x_restore_context() and pca953x_save_context()") Fixes: 3e38f946062b ("gpio: pca953x: fix IRQ storm on system wake up") Cc: stable@vger.kernel.org Reviewed-by: Linus Walleij Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260727080205.16353-1-phucduc.bui@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e03bf1b12091..aac9ea7520b6 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -1378,9 +1378,20 @@ static int pca953x_restore_context(struct pca953x_chip *chip) regcache_mark_dirty(chip->regmap); ret = pca953x_regcache_sync(chip); if (ret) - return ret; + goto err; + + ret = regcache_sync(chip->regmap); + if (ret) + goto err; + + return 0; - return regcache_sync(chip->regmap); +err: + if (chip->client->irq > 0) + disable_irq(chip->client->irq); + regcache_cache_only(chip->regmap, true); + + return ret; } static void pca953x_save_context(struct pca953x_chip *chip) -- cgit v1.2.3 From a02b8950d619123da64f69b70fe1dadef217dfe4 Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Thu, 23 Jul 2026 09:41:29 +0800 Subject: gpio: pch: use raw_spinlock_t for the register lock pch_irq_type() is registered as the irq_chip .irq_set_type callback and takes chip->spinlock with spin_lock_irqsave(). This callback is reached from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is an rtmutex-backed sleeping lock, so acquiring it there is invalid. This was confirmed on a PREEMPT_RT kernel with lockdep (PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored pch_irq_type()'s locking and drove it through the real genirq carrier irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. the same __irq_set_trigger() edge that __setup_irq() takes for a requested IRQ. With the original spin_lock_irqsave() edge lockdep reported an invalid wait context, immediately followed by: BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60 rt_spin_lock+0x3a/0x1c0 repro_irq_set_type+0x64/0xa0 [pch_repro] __irq_set_trigger+0x69/0x140 irq_set_irq_type+0x78/0xd0 Switching the mirrored lock to raw_spinlock_t made both splats go away. Convert the register lock to raw_spinlock_t. The same lock also serializes the GPIO direction/value callbacks and the suspend/resume register save/restore, but all of those critical sections only perform MMIO register accesses (ioread32()/iowrite32()) and irq_set_handler_locked(); none of them contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract. This is the same class of issue and fix as recently addressed for other GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use raw_spinlock_t in the irq startup path"). Fixes: 38eb18a6f92d ("gpio-pch: Support interrupt function") Cc: stable@vger.kernel.org Signed-off-by: Junjie Cao Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260723014129.1129730-1-junjie.cao@intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 4ffa0955a9e3..07a5617b314b 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -96,7 +96,7 @@ struct pch_gpio { struct pch_gpio_reg_data pch_gpio_reg; int irq_base; enum pch_type_t ioh; - spinlock_t spinlock; + raw_spinlock_t spinlock; }; static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) @@ -105,7 +105,7 @@ static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) struct pch_gpio *chip = gpiochip_get_data(gpio); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); reg_val = ioread32(&chip->reg->po); if (val) reg_val |= BIT(nr); @@ -113,7 +113,7 @@ static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->po); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -133,7 +133,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, u32 reg_val; unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); reg_val = ioread32(&chip->reg->po); if (val) @@ -147,7 +147,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, pm |= BIT(nr); iowrite32(pm, &chip->reg->pm); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -158,12 +158,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) u32 pm; unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); pm = ioread32(&chip->reg->pm); pm &= BIT(gpio_pins[chip->ioh]) - 1; pm &= ~BIT(nr); iowrite32(pm, &chip->reg->pm); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -265,7 +265,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) return 0; } - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); /* Set interrupt mode */ im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); @@ -277,7 +277,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) else if (type & IRQ_TYPE_EDGE_BOTH) irq_set_handler_locked(d, handle_edge_irq); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -374,7 +374,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, chip->ioh = id->driver_data; chip->reg = chip->base; pci_set_drvdata(pdev, chip); - spin_lock_init(&chip->spinlock); + raw_spin_lock_init(&chip->spinlock); pch_gpio_setup(chip); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); @@ -407,9 +407,9 @@ static int pch_gpio_suspend(struct device *dev) struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); pch_gpio_save_reg_conf(chip); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -419,11 +419,11 @@ static int pch_gpio_resume(struct device *dev) struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); iowrite32(0x01, &chip->reg->reset); iowrite32(0x00, &chip->reg->reset); pch_gpio_restore_reg_conf(chip); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } -- cgit v1.2.3 From 47d4e945dff8139050473be4ab263a32e1da910c Mon Sep 17 00:00:00 2001 From: Christian Loehle Date: Fri, 24 Jul 2026 11:40:42 +0100 Subject: ACPI: CPPC: Skip writes to unsupported performance controls MIN_PERF and MAX_PERF are optional CPPC controls. DESIRED_PERF is also optional with CPPC2 when autonomous selection is supported. The cppc-cpufreq target callbacks populate both limits for every request without checking whether the controls are implemented. cppc_set_perf() consequently passes NULL register descriptors to cpc_write(). The writes fail width validation and their return values are ignored, so the failed access paths are repeated on every target request. An autonomous-only platform can take the same path for DESIRED_PERF. Check that each performance control is supported before calling cpc_write(). Fixes: ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks") Reviewed-by: Sumit Gupta Signed-off-by: Christian Loehle Reviewed-by: Lifeng Zheng Link: https://patch.msgid.link/20260724104042.1481804-1-christian.loehle@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 1d3a94100491..53d09ca98f06 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1963,16 +1963,17 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) cpc_desc->write_cmd_status = 0; } - cpc_write(cpu, desired_reg, perf_ctrls->desired_perf); + if (CPC_SUPPORTED(desired_reg)) + cpc_write(cpu, desired_reg, perf_ctrls->desired_perf); /* * Only write if min_perf and max_perf not zero. Some drivers pass zero * value to min and max perf, but they don't mean to set the zero value, * they just don't want to write to those registers. */ - if (perf_ctrls->min_perf) + if (perf_ctrls->min_perf && CPC_SUPPORTED(min_perf_reg)) cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf); - if (perf_ctrls->max_perf) + if (perf_ctrls->max_perf && CPC_SUPPORTED(max_perf_reg)) cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf); if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) -- cgit v1.2.3 From 27460bd80bbab542f7f2ddf687923a73984757eb Mon Sep 17 00:00:00 2001 From: Jackie Dong Date: Mon, 27 Jul 2026 16:00:48 +0800 Subject: ALSA:hda/realtek:ALC269 fixup for Legion 7 15ASH11 Mic Mute LED Lenovo Legion 7 15ASH11 with AMD RYZEN AI MAX+ 392 (Strix Halo, ACP 7.0) uses Realtek ALC287 series codec. Its audio subsystem adopts a hardware design similar to that of the Yoga Pro 7 15ASH11. It shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5. Therefore, use HDA_CODEC_QUIRK to apply ALC287_FIXUP_LENOVO_YOGA_PRO7 for identification. After added the HDA_CODEC_QUIRK quirk special for Lenovo Legion 7 15ASH11, the mic mute LED works well. Signed-off-by: Jackie Dong Link: https://patch.msgid.link/20260727080048.13254-1-xy-jackie@139.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 09225801a177..5a516dee56ff 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7973,6 +7973,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), + /* Legion 7 15ASH11 shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5; + * use codec SSID to distinguish them + */ + HDA_CODEC_QUIRK(0x17aa, 0x395b, "Lenovo Legion 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), -- cgit v1.2.3 From bed0c8084044364f5ac3f3e89e1bbad423f6b0d4 Mon Sep 17 00:00:00 2001 From: Michael Diesen Date: Mon, 27 Jul 2026 09:19:21 +0000 Subject: ALSA: hda/realtek: Add quirk for HP Dragonfly Folio G3 2-in-1 (103c:8a05) The HP Dragonfly Folio G3 2-in-1 also ships with PCI SSID 103c:8a05. On this unit the ALC245 codec reports subsystem id 103c:8a06 - the SSID that is already covered by commit 0a10faad5ca5 ("ALSA: hda/realtek: add quirk for HP Dragonfly Folio G3 2-in-1") - while the PCI SSID that SND_PCI_QUIRK matches against is 103c:8a05: snd_hda_codec_alc269 ehdaudio0D0: ALC245: picked fixup for PCI SSID 103c:8a05 cs35l41-hda spi1-CSC3551:00-cs35l41-hda.0: CS35L41 Bound - SSID: 103C8A06 The existing entry therefore never applies here, the four CS35L41 amplifiers on SPI are not registered and the internal speakers stay silent. Add the same fixup that the 8a06 entry uses: the four amplifiers bind and the speaker mute LED (codec GPIO 0x04) works. Signed-off-by: Michael Diesen Link: https://patch.msgid.link/20260727091920.4634-1-michael.diesen@posteo.de 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 5a516dee56ff..f620cd429f20 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7255,6 +7255,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a1b, "HP 255 15.6 inch G9 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), -- cgit v1.2.3 From 4a2e2c563b0ee2253a7902f775bf71232820186d Mon Sep 17 00:00:00 2001 From: Taceddin Sancak Date: Sat, 18 Jul 2026 03:21:46 +0300 Subject: pinctrl: qcom: ipq9650: fix audio_sec_mclk_in1/out1 group pins The audio_sec_mclk_in1 and audio_sec_mclk_out1 groups both list "gpio37", but in the pingroup table those functions are muxed on gpio39, while gpio37 provides the audio_sec_mclk_in0/out0 variants. This makes both functions unusable: selecting them on gpio39 is rejected by the pinmux core because the group is not listed for the function, and selecting them on gpio37 trips the WARN_ON() in msm_pinmux_set_mux() and fails with -EINVAL because that group cannot mux them. Point both groups at gpio39, matching the pingroup table. This also mirrors the primary audio MCLK pair, where the mclk0 and mclk1 variants live on separate pins (gpio53 and gpio51 respectively). Fixes: 3c8e7ba0e399 ("pinctrl: qcom: Introduce IPQ9650 TLMM driver") Assisted-by: Claude:claude-fable-5 Signed-off-by: Taceddin Sancak Acked-by: Linus Walleij Reviewed-by: Kathiravan Thirumoorthy Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260718002146.698973-1-ts.solidarity@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/pinctrl/qcom/pinctrl-ipq9650.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq9650.c b/drivers/pinctrl/qcom/pinctrl-ipq9650.c index 64e443aa31b2..5399d7ad934c 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq9650.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq9650.c @@ -310,11 +310,11 @@ static const char *const audio_sec_mclk_out0_groups[] = { }; static const char *const audio_sec_mclk_in1_groups[] = { - "gpio37", + "gpio39", }; static const char *const audio_sec_mclk_out1_groups[] = { - "gpio37", + "gpio39", }; static const char *const audio_sec_groups[] = { -- cgit v1.2.3 From d5f8e5f6040d052d44fcbf4f31dd35145c0c8d7d Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Mon, 27 Jul 2026 15:05:51 +0530 Subject: cpufreq: powernow-k8: Fix possible memory leak in powernowk8_cpu_init() The memory allocated for data->powernow_table inside powernow_k8_cpu_init_acpi() or find_psb_table() is not freed in one of the error paths in powernowk8_cpu_init(). Fix that by adding a kfree(). Fixes: 1ff6e97f1d99 ("[CPUFREQ] cpumask: avoid playing with cpus_allowed in powernow-k8.c") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Acked-by: Viresh Kumar Reviewed-by: Zhongqiu Han Link: https://patch.msgid.link/20260727093553.98246-1-nihaal@cse.iitm.ac.in Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/powernow-k8.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 2b791f1ec51b..909a3f0598a9 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -1084,6 +1084,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol) err_out_exit_acpi: powernow_k8_cpu_exit_acpi(data); + kfree(data->powernow_table); err_out: kfree(data); -- cgit v1.2.3 From 47abd2ca281531deee38a3b3770d885e270e9fc9 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 20 Jul 2026 14:43:22 +0800 Subject: wifi: ath12k: fix out-of-bounds clear_bit in ath12k_mac_dp_peer_cleanup() ath12k_mac_dp_peer_cleanup() clears the ML peer ID slot on the free_ml_peer_id_map bitmap by indexing it with dp_peer->peer_id. That is wrong: dp_peer->peer_id for an MLO peer always carries the ATH12K_PEER_ML_ID_VALID bit (BIT(13)), so clear_bit() is invoked with index >= 0x2000, which is far outside the bitmap of ATH12K_MAX_MLO_PEERS (256) bits and corrupts memory adjacent to ah->free_ml_peer_id_map. The intended bitmap entry also never gets cleared, so subsequent ath12k_peer_ml_alloc() calls eventually run out of IDs. The ID without the VALID bit is what ath12k_peer_ml_alloc() returned and is stored in ahsta->ml_peer_id. Use that instead. While there, also reset ahsta->ml_peer_id to ATH12K_MLO_PEER_ID_INVALID so the bitmap and ahsta->ml_peer_id stay in sync. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: ee16dcf573d5 ("wifi: ath12k: Define ath12k_dp_peer structure & APIs for create & delete") Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-1-630632758a80@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 51c4df32e716..aa82c8fccc4e 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1287,8 +1287,11 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah) spin_lock_bh(&dp_hw->peer_lock); list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) { if (dp_peer->is_mlo) { + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(dp_peer->sta); + rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL); - clear_bit(dp_peer->peer_id, ah->free_ml_peer_id_map); + clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map); + ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; } list_move(&dp_peer->list, &peers); -- cgit v1.2.3 From 21ca38bb6b53a0b610998f370a91e656dc9e0542 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 20 Jul 2026 14:43:23 +0800 Subject: wifi: ath12k: factor out peer assoc send-and-wait into a helper ath12k_bss_assoc(), ath12k_mac_station_assoc() and ath12k_sta_rc_update_wk() all open-code the same sequence: reinit the peer_assoc_done completion, send the peer assoc WMI command, then wait for the firmware confirmation event. The reinit_completion() was buried in ath12k_peer_assoc_prepare(), far from the wait_for_completion_timeout() that consumes it, making the reinit/send/wait sequence hard to follow, and the three open-coded copies are easy to get out of sync. Move the sequence into a new helper ath12k_mac_peer_assoc() and call it from all three sites. The reinit, send and wait now live together so the completion's lifecycle is easy to read. While at it, ath12k_sta_rc_update_wk() previously warned but still waited the full timeout when the peer assoc command failed to send. Now a send failure returns immediately and skips the pointless 1 second wait, matching the other two callers. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-2-630632758a80@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 59 +++++++++++++++++------------------ 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index aa82c8fccc4e..b4df222f0c19 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3598,8 +3598,6 @@ static void ath12k_peer_assoc_prepare(struct ath12k *ar, memset(arg, 0, sizeof(*arg)); - reinit_completion(&ar->peer_assoc_done); - arg->peer_new_assoc = !reassoc; ath12k_peer_assoc_h_basic(ar, arvif, arsta, arg); ath12k_peer_assoc_h_crypto(ar, arvif, arsta, arg); @@ -3839,6 +3837,29 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, return bw; } +static int ath12k_mac_peer_assoc(struct ath12k *ar, + struct ath12k_wmi_peer_assoc_arg *peer_arg) +{ + int ret; + + reinit_completion(&ar->peer_assoc_done); + + ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); + if (ret) { + ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n", + peer_arg->peer_mac, peer_arg->vdev_id, ret); + return ret; + } + + if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { + ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", + peer_arg->peer_mac, peer_arg->vdev_id); + return -ETIMEDOUT; + } + + return 0; +} + static void ath12k_bss_assoc(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ieee80211_bss_conf *bss_conf) @@ -3919,18 +3940,10 @@ static void ath12k_bss_assoc(struct ath12k *ar, } peer_arg->is_assoc = true; - ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); - if (ret) { - ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n", - bss_conf->bssid, arvif->vdev_id, ret); - return; - } - if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { - ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", - bss_conf->bssid, arvif->vdev_id); + ret = ath12k_mac_peer_assoc(ar, peer_arg); + if (ret) return; - } ret = ath12k_setup_peer_smps(ar, arvif, bss_conf->bssid, &link_sta->ht_cap, &link_sta->he_6ghz_capa); @@ -6484,18 +6497,10 @@ static int ath12k_mac_station_assoc(struct ath12k *ar, } peer_arg->is_assoc = true; - ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); - if (ret) { - ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", - arsta->addr, arvif->vdev_id, ret); - return ret; - } - if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { - ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", - arsta->addr, arvif->vdev_id); - return -ETIMEDOUT; - } + ret = ath12k_mac_peer_assoc(ar, peer_arg); + if (ret) + return ret; num_vht_rates = ath12k_mac_bitrate_mask_num_vht_rates(ar, band, mask); num_he_rates = ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask); @@ -6844,14 +6849,8 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk) peer_arg, true); peer_arg->is_assoc = false; - err = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); - if (err) - ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n", - arsta->addr, arvif->vdev_id, err); - if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) - ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", - arsta->addr, arvif->vdev_id); + ath12k_mac_peer_assoc(ar, peer_arg); } } } -- cgit v1.2.3 From dd121ed779dd62c7679815f7c5a0b07da60a39bf Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 20 Jul 2026 14:43:24 +0800 Subject: wifi: ath12k: keep ATH12K_PEER_ML_ID_VALID set in ath12k_sta::ml_peer_id Several pieces of host bookkeeping for MLD peer IDs encode the same fact in different ways: - ath12k_sta::ml_peer_id stores the raw ID in [0, ATH12K_MAX_MLO_PEERS); - ath12k_dp_peer::peer_id, ath12k_dp_link_peer::ml_id and the index used on ath12k_dp_hw::dp_peers[] always carry the ATH12K_PEER_ML_ID_VALID bit (BIT(13)) when the ID is real; - WMI_MLO_PEER_ASSOC_PARAMS::ml_peer_id sent down to firmware is raw, without the bookkeeping bit. The mismatch leaks into call sites that have to remember to OR the bit in (ath12k_peer_create(), ath12k_mac_op_sta_state()) or remember not to (ath12k_peer_assoc_h_mlo()). Make ath12k_sta::ml_peer_id carry the VALID bit when valid, the same way ath12k_dp_peer::peer_id and ath12k_dp_link_peer::ml_id do: - ath12k_peer_ml_alloc() OR-s the bit in once on the way out; the internal bitmap stays raw [0, ATH12K_MAX_MLO_PEERS); - ath12k_peer_create() and ath12k_mac_op_sta_state() drop the explicit OR; - ath12k_peer_assoc_h_mlo() masks the bit off when populating the WMI ml_peer_id; While there, introduce ath12k_peer_ml_free() to mirror ath12k_peer_ml_alloc(), which helps avoid code duplication. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-3-630632758a80@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 27 +++++++++++++-------------- drivers/net/wireless/ath/ath12k/peer.c | 17 ++++++++++++++--- drivers/net/wireless/ath/ath12k/peer.h | 1 + 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index b4df222f0c19..06f1a1ba994a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1282,16 +1282,15 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah) struct ath12k_dp_peer *dp_peer, *tmp; struct ath12k_dp_hw *dp_hw = &ah->dp_hw; + lockdep_assert_wiphy(ah->hw->wiphy); + INIT_LIST_HEAD(&peers); spin_lock_bh(&dp_hw->peer_lock); list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) { if (dp_peer->is_mlo) { - struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(dp_peer->sta); - rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL); - clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map); - ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; + ath12k_peer_ml_free(ah, ath12k_sta_to_ahsta(dp_peer->sta)); } list_move(&dp_peer->list, &peers); @@ -3551,7 +3550,11 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta, ether_addr_copy(ml->mld_addr, sta->addr); ml->logical_link_idx = arsta->link_idx; - ml->ml_peer_id = ahsta->ml_peer_id; + /* + * WMI_MLO_PEER_ASSOC_PARAMS expects the raw ML peer ID without + * the host-side ATH12K_PEER_ML_ID_VALID bookkeeping bit. + */ + ml->ml_peer_id = ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID; ml->ieee_link_id = arsta->link_id; ml->num_partner_links = 0; ml->eml_cap = sta->eml_cap; @@ -7268,10 +7271,8 @@ static void ath12k_mac_ml_station_remove(struct ath12k_vif *ahvif, ath12k_mac_free_unassign_link_sta(ah, ahsta, link_id); } - if (sta->mlo) { - clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map); - ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; - } + if (sta->mlo) + ath12k_peer_ml_free(ah, ahsta); } static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, @@ -7743,7 +7744,7 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, } dp_params.is_mlo = true; - dp_params.peer_id = ahsta->ml_peer_id | ATH12K_PEER_ML_ID_VALID; + dp_params.peer_id = ahsta->ml_peer_id; } dp_params.sta = sta; @@ -7880,10 +7881,8 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, peer_delete: ath12k_dp_peer_delete(&ah->dp_hw, sta->addr, sta); ml_peer_id_clear: - if (sta->mlo) { - clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map); - ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; - } + if (sta->mlo) + ath12k_peer_ml_free(ah, ahsta); exit: /* update the state if everything went well */ if (!ret) diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 2681a047d4d5..5dd7c6470219 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -230,7 +230,7 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, /* Fill ML info into created peer */ if (sta->mlo) { ml_peer_id = ahsta->ml_peer_id; - peer->ml_id = ml_peer_id | ATH12K_PEER_ML_ID_VALID; + peer->ml_id = ml_peer_id; ether_addr_copy(peer->ml_addr, sta->addr); /* the assoc link is considered primary for now */ @@ -276,9 +276,20 @@ u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah) } if (ml_peer_id == ATH12K_MAX_MLO_PEERS) - ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; + return ATH12K_MLO_PEER_ID_INVALID; - return ml_peer_id; + return ml_peer_id | ATH12K_PEER_ML_ID_VALID; +} + +void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta) +{ + lockdep_assert_wiphy(ah->hw->wiphy); + + if (ahsta->ml_peer_id < + (ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID)) + clear_bit(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID, + ah->free_ml_peer_id_map); + ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; } int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta) diff --git a/drivers/net/wireless/ath/ath12k/peer.h b/drivers/net/wireless/ath/ath12k/peer.h index 49d89796bc46..0f7f25b8e89c 100644 --- a/drivers/net/wireless/ath/ath12k/peer.h +++ b/drivers/net/wireless/ath/ath12k/peer.h @@ -26,4 +26,5 @@ int ath12k_link_sta_rhash_add(struct ath12k_base *ab, struct ath12k_link_sta *ar struct ath12k_link_sta *ath12k_link_sta_find_by_addr(struct ath12k_base *ab, const u8 *addr); u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah); +void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta); #endif /* _PEER_H_ */ -- cgit v1.2.3 From a08455ee85a2b32a5503b84fdc6b88a144cb2388 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 20 Jul 2026 14:43:25 +0800 Subject: wifi: ath12k: add support for HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP Firmware on chips that allocate the MLD peer ID itself (WCN7850 and QCC2072) reports the assignment back to the host through HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP. The message carries the chosen MLD peer id, the MLD MAC address etc. Add the message type, the on-the-wire struct, the field masks and a handler that parses them out. The host-side state update (publishing the dp peer into ath12k_dp_hw::dp_peers[], propagating the ID to ath12k_dp_link_peer::ml_id and ath12k_sta::ml_peer_id) is added in a follow-up patch; Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-4-630632758a80@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_htt.c | 30 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/dp_htt.h | 12 ++++++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c index 52e10059c6d5..150b190f9c7f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.c +++ b/drivers/net/wireless/ath/ath12k/dp_htt.c @@ -575,6 +575,33 @@ exit: rcu_read_unlock(); } +static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab, + struct sk_buff *skb) +{ + struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data; + struct htt_t2h_mlo_peer_map_event *ev = &resp->mlo_peer_map_ev; + u16 raw_peer_id, peer_id, addr_h16; + u8 peer_addr[ETH_ALEN]; + + if (skb->len < sizeof(*ev)) { + ath12k_warn(ab, "unexpected htt mlo peer map event len %u\n", + skb->len); + return; + } + + raw_peer_id = le32_get_bits(ev->info0, + HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID); + peer_id = raw_peer_id | ATH12K_PEER_ML_ID_VALID; + + addr_h16 = le32_get_bits(ev->info1, + HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16); + ath12k_dp_get_mac_addr(le32_to_cpu(ev->mac_addr_l32), addr_h16, + peer_addr); + + ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt mlo peer map peer %pM id %u\n", + peer_addr, peer_id); +} + void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, struct sk_buff *skb) { @@ -659,6 +686,9 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, case HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND: ath12k_htt_mlo_offset_event_handler(ab, skb); break; + case HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP: + ath12k_dp_htt_mlo_peer_map_handler(ab, skb); + break; default: ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "dp_htt event %d not handled\n", type); diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.h b/drivers/net/wireless/ath/ath12k/dp_htt.h index 987689f11cda..2db7fb27c036 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.h +++ b/drivers/net/wireless/ath/ath12k/dp_htt.h @@ -930,6 +930,7 @@ enum htt_t2h_msg_type { HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c, HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND = 0x24, HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND = 0x28, + HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP = 0x29, HTT_T2H_MSG_TYPE_PEER_MAP3 = 0x2b, HTT_T2H_MSG_TYPE_VDEV_TXRX_STATS_PERIODIC_IND = 0x2c, }; @@ -974,11 +975,22 @@ struct htt_t2h_peer_unmap_event { __le32 info1; } __packed; +#define HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID GENMASK(23, 8) +#define HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0) + +struct htt_t2h_mlo_peer_map_event { + __le32 info0; + __le32 mac_addr_l32; + __le32 info1; + __le32 reserved[5]; +} __packed; + struct htt_resp_msg { union { struct htt_t2h_version_conf_msg version_msg; struct htt_t2h_peer_map_event peer_map_ev; struct htt_t2h_peer_unmap_event peer_unmap_ev; + struct htt_t2h_mlo_peer_map_event mlo_peer_map_ev; }; } __packed; -- cgit v1.2.3 From 378e659029d55cf57ee2eddf1d67672ed53c3bb4 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 20 Jul 2026 14:43:26 +0800 Subject: wifi: ath12k: introduce host_alloc_ml_id hardware parameter Different ath12k devices diverge on who allocates MLD peer id: WCN7850/QCC2072 have the firmware allocate it and notify the host via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP event; While others let the host allocate it and pass it down through WMI_PEER_ASSOC_CMDID with ATH12K_WMI_FLAG_MLO_PEER_ID_VALID set. Currently ath12k host allocates this ID and sends it to firmware by default for all devices. This breaks WCN7850/QCC2072, because the host maintained ID may be different from the firmware-allocated one. Consequently data path may fail to find the dp peer and drop some received packets. From user point of view, this results in bugs reported in [1] or the 4-way handshake timeout issue. Add host_alloc_ml_id flag to struct ath12k_hw_params (and a copy on struct ath12k_hw for hot-path access) so subsequent patches can branch on it. Set true for QCN9274/IPQ5332/IPQ5424, false for WCN7850/QCC2072. The flag will be consumed by subsequent patches. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Link: https://bugzilla.kernel.org/show_bug.cgi?id=221039 # 1 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-5-630632758a80@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/hw.h | 2 ++ drivers/net/wireless/ath/ath12k/mac.c | 18 +++++++++++++++++- drivers/net/wireless/ath/ath12k/wifi7/hw.c | 12 ++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index fc5127b5c1a3..1f56474efbea 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -793,6 +793,7 @@ struct ath12k_hw { enum ath12k_hw_state state; bool regd_updated; bool use_6ghz_regd; + bool host_alloc_ml_id; u8 num_radio; diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 86fb8b719613..8d2fa0bfb96c 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -236,6 +236,8 @@ struct ath12k_hw_params { u32 max_client_dbs; u32 max_client_dbs_sbs; } client; + + bool host_alloc_ml_id; }; struct ath12k_hw_ops { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 06f1a1ba994a..51641c5ff265 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -15385,8 +15385,9 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag) int mac_id, device_id, total_radio, num_hw; struct ath12k_base *ab; struct ath12k_hw *ah; - int ret, i, j; + bool conf = false; u8 radio_per_hw; + int ret, i, j; total_radio = 0; for (i = 0; i < ag->num_devices; i++) { @@ -15426,6 +15427,20 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag) } ab = ag->ab[device_id]; + + /* + * the assumption is all devices within an ah + * share the same host_alloc_ml_id configuration + */ + if (j == 0) { + conf = ab->hw_params->host_alloc_ml_id; + } else if (conf != ab->hw_params->host_alloc_ml_id) { + ath12k_warn(ab, "inconsistent ML ID config within ah, device 0 uses %s allocated ID, while device %u doesn't\n", + conf ? "host" : "firmware", device_id); + ret = -EINVAL; + goto err; + } + pdev_map[j].ab = ab; pdev_map[j].pdev_idx = mac_id; mac_id++; @@ -15450,6 +15465,7 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag) } ah->dev = ab->dev; + ah->host_alloc_ml_id = conf; ag->ah[i] = ah; ag->num_hw++; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index d9fdd2fc8298..03dedfd907fc 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -442,6 +442,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_client_dbs = 128, .max_client_dbs_sbs = 128, }, + + .host_alloc_ml_id = true, }, { .name = "wcn7850 hw2.0", @@ -533,6 +535,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_client_dbs = 128, .max_client_dbs_sbs = 128, }, + + .host_alloc_ml_id = false, }, { .name = "qcn9274 hw2.0", @@ -620,6 +624,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_client_dbs = 128, .max_client_dbs_sbs = 128, }, + + .host_alloc_ml_id = true, }, { .name = "ipq5332 hw1.0", @@ -700,6 +706,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_client_dbs = 128, .max_client_dbs_sbs = 128, }, + + .host_alloc_ml_id = true, }, { .name = "qcc2072 hw1.0", @@ -792,6 +800,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_client_dbs = 128, .max_client_dbs_sbs = 128, }, + + .host_alloc_ml_id = false, }, { .name = "ipq5424 hw1.0", @@ -876,6 +886,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_client_dbs = 128, .max_client_dbs_sbs = 128, }, + + .host_alloc_ml_id = true, }, }; -- cgit v1.2.3 From 1726a7a10c4fee262549bc6fa142e1051192be0c Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 20 Jul 2026 14:43:27 +0800 Subject: wifi: ath12k: do not advertise MLD peer ID for firmware-allocate devices ath12k_peer_assoc_h_mlo() unconditionally sets ml->peer_id_valid and copies ahsta->ml_peer_id (with the ATH12K_PEER_ML_ID_VALID bookkeeping bit masked off) into the WMI_PEER_ASSOC_CMDID ML params, which causes ath12k_wmi_send_peer_assoc_cmd() to set ATH12K_WMI_FLAG_MLO_PEER_ID_VALID. This needs to be gated on chips where the firmware allocates the MLD peer ID: - WCN7850/QCC2072 firmware always picks the ID itself and does not honor a host-supplied one, so the value would be silently ignored anyway; - QCC2072 firmware additionally crashes during MLO disconnect when ATH12K_WMI_FLAG_MLO_PEER_ID_VALID was set in the preceding peer assoc, so the bit must not be sent at all. Branch on ah->host_alloc_ml_id: - When true (QCN9274 etc.), behavior is unchanged: peer_id_valid is set and the raw ahsta->ml_peer_id (without the VALID bit) is sent down. - When false (WCN7850, QCC2072), peer_id_valid stays unset and ml_peer_id is sent as 0. The firmware ignores both fields and reports the ID it allocated through HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP. The early-return on ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID only applies on the host-alloc path, since on the firmware-alloc path the value is ATH12K_MLO_PEER_ID_PENDING here, not INVALID. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-6-630632758a80@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 51641c5ff265..1004c290e5d0 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3533,11 +3533,16 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta, struct ath12k_sta *ahsta = arsta->ahsta; struct ath12k_link_sta *arsta_p; struct ath12k_link_vif *arvif; + struct ath12k_hw *ah = arsta->arvif->ar->ah; unsigned long links; u8 link_id; int i; - if (!sta->mlo || ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) + if (!sta->mlo) + return; + + if (ah->host_alloc_ml_id && + ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) return; ml->enabled = true; @@ -3545,16 +3550,25 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta, /* For now considering the primary umac based on assoc link */ ml->primary_umac = arsta->is_assoc_link; - ml->peer_id_valid = true; + /* + * Only chips that allocate the MLD peer ID on the host send a valid + * ml_peer_id in WMI_PEER_ASSOC_CMDID. For chips where the firmware + * picks the ID, leave peer_id_valid false to avoid unexpected issues. + */ + ml->peer_id_valid = ah->host_alloc_ml_id; ml->logical_link_idx_valid = true; ether_addr_copy(ml->mld_addr, sta->addr); ml->logical_link_idx = arsta->link_idx; /* * WMI_MLO_PEER_ASSOC_PARAMS expects the raw ML peer ID without - * the host-side ATH12K_PEER_ML_ID_VALID bookkeeping bit. + * the host-side ATH12K_PEER_ML_ID_VALID bookkeeping bit. For chips + * where the firmware allocates the ID, the field is unused (the + * firmware always allocates regardless of the value here); send 0 + * to make that intent explicit. */ - ml->ml_peer_id = ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID; + ml->ml_peer_id = ah->host_alloc_ml_id ? + (ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID) : 0; ml->ieee_link_id = arsta->link_id; ml->num_partner_links = 0; ml->eml_cap = sta->eml_cap; -- cgit v1.2.3 From a7619b3bcba42be62b3b4b941d4175234dce34f0 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 20 Jul 2026 14:43:28 +0800 Subject: wifi: ath12k: defer dp_peer registration when firmware allocates MLD peer ID For chips with host_alloc_ml_id=true (QCN9274 etc.), the host allocates the MLD peer ID up front; ath12k_dp_peer_create() publishes the dp_peer into dp_hw->dp_peers[] using that ID immediately. WCN7850/QCC2072 does not work that way: the firmware picks the ID and only tells the host afterwards via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP, so the publication has to be delayed until the event arrives. Introduce ATH12K_MLO_PEER_ID_PENDING (0xFFFE) as a sentinel for "is_mlo, but ID not yet known". On the firmware-allocates path: - ath12k_mac_op_sta_state(NOTEXIST->NONE) skips ath12k_peer_ml_alloc() and stores PENDING in ahsta->ml_peer_id and dp_params.peer_id; - ath12k_dp_peer_create() skips dp_peer registration until a real ID is known; - ath12k_peer_create() leaves peer->ml_id at INVALID so consumer sites do not treat PENDING as a real ID; - ath12k_peer_ml_free() and ath12k_mac_dp_peer_cleanup() skip the dp_peers[] write and the free_ml_peer_id_map clear when host_alloc_ml_id is false or the ID is still PENDING. The HTT handler change that resolves the PENDING ID is added in a follow-up patch. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-7-630632758a80@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/dp_peer.c | 23 +++++++++++++++-------- drivers/net/wireless/ath/ath12k/mac.c | 22 ++++++++++++++++------ drivers/net/wireless/ath/ath12k/peer.c | 20 +++++++++++++++++--- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 1f56474efbea..8769b41f5db5 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -72,6 +72,7 @@ #define ATH12K_MAX_MLO_PEERS 256 #define ATH12K_MLO_PEER_ID_INVALID 0xFFFF +#define ATH12K_MLO_PEER_ID_PENDING 0xFFFE #define ATH12K_INVALID_RSSI_FULL -1 #define ATH12K_INVALID_RSSI_EMPTY -128 diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index a12073afc307..cd6a0eb207bd 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -475,7 +475,9 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr, dp_peer->is_mlo = params->is_mlo; /* - * For MLO client, the host assigns the ML peer ID, so set peer_id in dp_peer + * For MLO client, the ML peer ID, either known or PENDING, needs to be + * initialized here since the following logic depends on it. + * * For non-MLO client, host gets link peer ID from firmware and will be * assigned at the time of link peer creation */ @@ -491,13 +493,17 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr, list_add(&dp_peer->list, &dp_hw->dp_peers_list); /* - * For MLO client, the peer_id for ath12k_dp_peer is allocated by host - * and that peer_id is known at this point, and hence this ath12k_dp_peer - * can be added to the RCU table using the peer_id. - * For non-MLO client, this addition to RCU table shall be done at the - * time of assignment of ath12k_dp_link_peer to ath12k_dp_peer. + * For an MLO client whose ML peer ID is allocated by the host, the + * peer_id is known here and the dp_peer can be added to the RCU + * table using it. For an MLO client on chips where the firmware + * allocates the ID, peer_id is ATH12K_MLO_PEER_ID_PENDING and the + * RCU table publish is deferred to the + * HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For a non-MLO client + * the publish happens later, at the time of assignment of + * ath12k_dp_link_peer to ath12k_dp_peer. */ - if (dp_peer->is_mlo) + if (dp_peer->is_mlo && + dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING) rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], dp_peer); spin_unlock_bh(&dp_hw->peer_lock); @@ -518,7 +524,8 @@ void ath12k_dp_peer_delete(struct ath12k_dp_hw *dp_hw, u8 *addr, return; } - if (dp_peer->is_mlo) + if (dp_peer->is_mlo && + dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING) rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL); list_del(&dp_peer->list); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 1004c290e5d0..760afe1c7f7a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1289,7 +1289,9 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah) spin_lock_bh(&dp_hw->peer_lock); list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) { if (dp_peer->is_mlo) { - rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL); + if (dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING) + rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], + NULL); ath12k_peer_ml_free(ah, ath12k_sta_to_ahsta(dp_peer->sta)); } @@ -7750,11 +7752,19 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, /* ML sta */ if (sta->mlo && !ahsta->links_map && (hweight16(sta->valid_links) == 1)) { - ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah); - if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) { - ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM", - sta->addr); - goto exit; + if (ah->host_alloc_ml_id) { + ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah); + if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) { + ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM", + sta->addr); + goto exit; + } + } else { + /* + * firmware allocates the ML peer ID and notifies + * the host via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP + */ + ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_PENDING; } dp_params.is_mlo = true; diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 5dd7c6470219..ed0524ddff80 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -230,7 +230,16 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, /* Fill ML info into created peer */ if (sta->mlo) { ml_peer_id = ahsta->ml_peer_id; - peer->ml_id = ml_peer_id; + /* + * For chips where firmware allocates the ML peer ID, + * ml_peer_id is ATH12K_MLO_PEER_ID_PENDING here. The + * MLO_RX_PEER_MAP HTT event handler fixes up + * peer->ml_id once the ID is known. + */ + if (ml_peer_id == ATH12K_MLO_PEER_ID_PENDING) + peer->ml_id = ATH12K_MLO_PEER_ID_INVALID; + else + peer->ml_id = ml_peer_id; ether_addr_copy(peer->ml_addr, sta->addr); /* the assoc link is considered primary for now */ @@ -285,8 +294,13 @@ void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta) { lockdep_assert_wiphy(ah->hw->wiphy); - if (ahsta->ml_peer_id < - (ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID)) + /* + * Only devices that allocate the ID on the host own a slot in + * free_ml_peer_id_map. + */ + if (ah->host_alloc_ml_id && + (ahsta->ml_peer_id < + (ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID))) clear_bit(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID, ah->free_ml_peer_id_map); ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID; -- cgit v1.2.3 From 469d7e6077c1665754eaf330e1feabdca7b060ae Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 20 Jul 2026 14:43:29 +0800 Subject: wifi: ath12k: resolve PENDING ML peer ID from MLO_PEER_MAP HTT event Add ath12k_dp_peer_fixup_peer_id() and call it from the HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For devices where the firmware allocates the MLD peer ID, this is the point at which all data structures that were left with ATH12K_MLO_PEER_ID_PENDING or ATH12K_MLO_PEER_ID_INVALID get their real ID: - dp_peer->peer_id is updated and the dp_peer is published into dp_hw->dp_peers[]; - every existing dp_link_peer in dp_peer->link_peers[] gets its ml_id set to the same value; - ahsta->ml_peer_id is updated to the same value so peer_assoc, sta_state and cleanup paths see a consistent ID. Devices with host_alloc_ml_id == true also receive the same HTT event, but the firmware-reported ID always matches the host-allocated one and everything has already been populated by ath12k_dp_peer_create(); Skips the helper entirely on those devices. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221039 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260720-ath12k-fw-allocated-ml-peer-id-v2-8-630632758a80@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 2 ++ drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/dp_htt.c | 19 +++++++++++ drivers/net/wireless/ath/ath12k/dp_peer.c | 52 +++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/dp_peer.h | 2 ++ drivers/net/wireless/ath/ath12k/mac.c | 24 ++++++++++++++ 6 files changed, 100 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 742d4fd1b598..e87165e4f4b3 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1544,6 +1544,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) } wiphy_unlock(ah->hw->wiphy); + + complete(&ah->peer_ml_id_done); } wake_up(&ab->wmi_ab.tx_credits_wq); diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 8769b41f5db5..30726e580833 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -795,6 +795,7 @@ struct ath12k_hw { bool regd_updated; bool use_6ghz_regd; bool host_alloc_ml_id; + struct completion peer_ml_id_done; u8 num_radio; diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c index 150b190f9c7f..68968f96b4f1 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.c +++ b/drivers/net/wireless/ath/ath12k/dp_htt.c @@ -6,6 +6,7 @@ #include "core.h" #include "peer.h" +#include "dp_peer.h" #include "htc.h" #include "dp_htt.h" #include "debugfs_htt_stats.h" @@ -582,6 +583,7 @@ static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab, struct htt_t2h_mlo_peer_map_event *ev = &resp->mlo_peer_map_ev; u16 raw_peer_id, peer_id, addr_h16; u8 peer_addr[ETH_ALEN]; + int ret; if (skb->len < sizeof(*ev)) { ath12k_warn(ab, "unexpected htt mlo peer map event len %u\n", @@ -600,6 +602,23 @@ static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt mlo peer map peer %pM id %u\n", peer_addr, peer_id); + + /* + * Fix up the dp_peer entry created with ATH12K_MLO_PEER_ID_PENDING + * earlier; on chips with host_alloc_ml_id == false this is the only + * point at which the host learns the firmware-assigned ID. Chips + * that allocate the ID on the host also receive this event but the + * firmware-reported ID matches the host-allocated one, so there is + * nothing to fix up. + */ + if (!ab->hw_params->host_alloc_ml_id) { + ret = ath12k_dp_peer_fixup_peer_id(ab, peer_addr, + peer_id); + if (ret) + ath12k_warn(ab, + "failed to fix up peer id %u for dp peer %pM: %d\n", + peer_id, peer_addr, ret); + } } void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index cd6a0eb207bd..bb5341b4251a 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -702,3 +702,55 @@ void ath12k_dp_link_peer_reset_rx_stats(struct ath12k_dp *dp, const u8 *addr) if (rx_stats) memset(rx_stats, 0, sizeof(*rx_stats)); } + +int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab, + const u8 *peer_addr, u16 peer_id) +{ + struct ath12k_dp_link_peer *link_peer; + struct ath12k_dp_peer *dp_peer = NULL; + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_dp_hw *dp_hw = NULL; + struct ath12k_hw *ah; + int i; + + if (peer_id >= (ATH12K_PEER_ML_ID_VALID | ATH12K_MAX_MLO_PEERS)) + return -EINVAL; + + for (i = 0; i < ag->num_hw; i++) { + ah = ag->ah[i]; + if (!ah) + continue; + + spin_lock_bh(&ah->dp_hw.peer_lock); + dp_peer = ath12k_dp_peer_find_by_addr(&ah->dp_hw, + (u8 *)peer_addr); + if (dp_peer) { + dp_hw = &ah->dp_hw; + break; + } + spin_unlock_bh(&ah->dp_hw.peer_lock); + } + + if (!dp_peer) + return -ENOENT; + + /* dp_hw->peer_lock is held */ + + dp_peer->peer_id = peer_id; + rcu_assign_pointer(dp_hw->dp_peers[peer_id], dp_peer); + + for (i = 0; i < ATH12K_NUM_MAX_LINKS; i++) { + link_peer = rcu_dereference_protected(dp_peer->link_peers[i], + lockdep_is_held(&dp_hw->peer_lock)); + if (link_peer) + link_peer->ml_id = peer_id; + } + + ath12k_sta_to_ahsta(dp_peer->sta)->ml_peer_id = peer_id; + + spin_unlock_bh(&dp_hw->peer_lock); + + complete(&ah->peer_ml_id_done); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h index 7c9709bf717b..3503840b0329 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.h +++ b/drivers/net/wireless/ath/ath12k/dp_peer.h @@ -181,4 +181,6 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p struct ath12k_dp_link_peer * ath12k_dp_link_peer_find_by_peerid(struct ath12k_pdev_dp *dp_pdev, u16 peer_id); void ath12k_dp_link_peer_free(struct ath12k_dp_link_peer *peer); +int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab, const u8 *peer_addr, + u16 peer_id); #endif diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 760afe1c7f7a..a0928890671a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3859,9 +3859,11 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, static int ath12k_mac_peer_assoc(struct ath12k *ar, struct ath12k_wmi_peer_assoc_arg *peer_arg) { + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); int ret; reinit_completion(&ar->peer_assoc_done); + reinit_completion(&ah->peer_ml_id_done); ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); if (ret) { @@ -3876,6 +3878,27 @@ static int ath12k_mac_peer_assoc(struct ath12k *ar, return -ETIMEDOUT; } + /* + * For devices where the firmware allocates the MLD peer ID, the host + * learns the real ID only from the MLO_RX_PEER_MAP HTT event, which is + * handled in a softirq (BH workqueue) context that cannot take the + * wiphy lock. Block here, while still holding the wiphy lock, until + * that event has fixed up the ID. This serialises the fixup against + * all other wiphy-locked ml_peer_id accesses. + * + * The firmware sends the event only once, in response to the assoc-link + * peer assoc, so block only for that link. + */ + if (!ah->host_alloc_ml_id && + peer_arg->is_assoc && + peer_arg->ml.enabled && + peer_arg->ml.assoc_link && + !wait_for_completion_timeout(&ah->peer_ml_id_done, 1 * HZ)) { + ath12k_warn(ar->ab, "failed to get MLO peer map event for %pM vdev %i\n", + peer_arg->peer_mac, peer_arg->vdev_id); + return -ETIMEDOUT; + } + return 0; } @@ -15335,6 +15358,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag, ah->num_radio = num_pdev_map; mutex_init(&ah->hw_mutex); + init_completion(&ah->peer_ml_id_done); spin_lock_init(&ah->dp_hw.peer_lock); INIT_LIST_HEAD(&ah->dp_hw.dp_peers_list); -- cgit v1.2.3 From 83195b778f2d109a3a4f3ffaba4dce7e4cdb58aa Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:22 -0400 Subject: drm/vmwgfx: fix guest_memory_dirty bitfield clobbered as size Two sites in vmwgfx_resource.c assign boolean literals to res->guest_memory_size, which is an unsigned long allocation-size field; the intended target is the adjacent res->guest_memory_dirty bitfield. After the assignments the field holds 0 or 1 instead of the resource's MOB allocation size: - vmw_resource_release() writes 0 (false), and - vmw_resource_unbind_list() writes 1 (true). Subsequent revalidation paths read guest_memory_size when computing the dirty page range (vmw_bo_dirty_transfer_to_res()) and the buffer allocation size (vmw_resource_buf_alloc()), producing zero-length walks or wrap-around ranges that read or write past the MOB bitmap. The dirty-tracking intent of the original code (mark the resource as dirtied since the last sync) is also lost, since guest_memory_dirty is never updated. Rename both assignments to guest_memory_dirty. Fixes: 668b206601c5 ("drm/vmwgfx: Stop using raw ttm_buffer_object's") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-2-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 388011696941..e3a187a2c7a1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -136,7 +136,7 @@ static void vmw_resource_release(struct kref *kref) val_buf.num_shared = 0; res->func->unbind(res, false, &val_buf); } - res->guest_memory_size = false; + res->guest_memory_dirty = false; vmw_resource_mob_detach(res); if (res->dirty) res->func->dirty_free(res); @@ -773,7 +773,7 @@ void vmw_resource_unbind_list(struct vmw_bo *vbo) if (!WARN_ON_ONCE(!res->func->unbind)) (void) res->func->unbind(res, res->res_dirty, &val_buf); - res->guest_memory_size = true; + res->guest_memory_dirty = true; res->res_dirty = false; vmw_resource_mob_detach(res); } -- cgit v1.2.3 From 55ec09c9ce10b1272802c7ab6c1be2ea0dbc68db Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:23 -0400 Subject: drm/vmwgfx: reject DX_BIND_QUERY without a DX context vmw_cmd_dx_bind_query() unconditionally dereferences sw_context->dx_ctx_node->ctx. Userspace can trigger a NULL pointer dereference from any render-node fd by submitting an execbuf with dx_context_handle == SVGA3D_INVALID_ID and a SVGA_3D_CMD_DX_BIND_QUERY opcode in the command stream: dx_ctx_node is left NULL and the kernel oopses on the assignment. The same NULL is then re-read in vmw_resources_reserve() via vmw_context_get_dx_query_mob(). All sibling DX handlers fail-close on a missing dx_ctx_node using VMW_GET_CTX_NODE(). Use the same pattern here, returning -EINVAL up front before any relocation state is published. Fixes: 9c079b8ce8bf ("drm/vmwgfx: Adapt execbuf to the new validation api") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-3-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index e1f18020170a..b07f052474d0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1272,9 +1272,13 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv, SVGA3dCmdHeader *header) { VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindQuery); + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); struct vmw_bo *vmw_bo; int ret; + if (!ctx_node) + return -EINVAL; + cmd = container_of(header, typeof(*cmd), header); /* @@ -1288,7 +1292,7 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv, return ret; sw_context->dx_query_mob = vmw_bo; - sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx; + sw_context->dx_query_ctx = ctx_node->ctx; return 0; } -- cgit v1.2.3 From f47d542d5912f236273399d7139b522f6950e2e4 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:24 -0400 Subject: drm/vmwgfx: clamp dirty-page range with min, not max vmw_bo_dirty_transfer_to_res() and vmw_bo_dirty_clear() compute the intersection of a resource's page range with the BO's tracked dirty range, but clamp res_end against dirty->end with max() instead of min(). When dirty->end exceeds the resource end, the loop walks past the resource's pages, calls vmw_resource_dirty_update() for ranges owned by other resources sharing the same backing MOB and clears their pending dirty bits via bitmap_clear(). The result is silent loss of writeback for unrelated resources whenever two resources share a MOB. Use min() in both functions so the loop is bounded to the intersection of the resource and dirty ranges. Fixes: b7468b15d271 ("drm/vmwgfx: Implement an infrastructure for write-coherent resources") Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-4-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c index 45561bc1c9ef..8ab88f388652 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c @@ -311,7 +311,7 @@ void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res) return; cur = max(res_start, dirty->start); - res_end = max(res_end, dirty->end); + res_end = min(res_end, dirty->end); while (cur < res_end) { unsigned long num; @@ -347,7 +347,7 @@ void vmw_bo_dirty_clear(struct vmw_bo *vbo) return; cur = max(res_start, dirty->start); - res_end = max(res_end, dirty->end); + res_end = min(res_end, dirty->end); while (cur < res_end) { unsigned long num; -- cgit v1.2.3 From 250af2e8c3e90dc978e062a936b633870a22e660 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:25 -0400 Subject: drm/vmwgfx: take fman->lock around fence list mutation in fifo_down vmw_fence_fifo_down() drops fman->lock to wait on a fence and, on timeout, mutates fman->fence_list via list_del_init() and signals the fence without re-acquiring the lock. __vmw_fences_update() walks and removes entries from the same list under fman->lock from any other waiter, the fence-IRQ thread, or vmw_fences_update(), so the unlocked list_del_init() can corrupt the list head. Re-take fman->lock before manipulating fence->head and use dma_fence_signal_locked(). Wrap the locked signalling in dma_fence_begin_signalling() / dma_fence_end_signalling() so the lockdep annotation that dma_fence_signal() previously provided is preserved (the same pattern as __vmw_fences_update()). dma_fence_put() is moved outside the lock to avoid a recursive acquire from vmw_fence_obj_destroy(), which also takes fman->lock. Fixes: ae2a104058e2 ("vmwgfx: Implement fence objects") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-5-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 4ef84ff9b638..384c6736cf6b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -367,13 +367,24 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman) ret = vmw_fence_obj_wait(fence, false, false, VMW_FENCE_WAIT_TIMEOUT); + spin_lock(&fman->lock); if (unlikely(ret != 0)) { + bool cookie = dma_fence_begin_signalling(); + list_del_init(&fence->head); - dma_fence_signal(&fence->base); + if (fence->waiter_added) { + vmw_seqno_waiter_remove(fman->dev_priv); + fence->waiter_added = false; + } + dma_fence_signal_locked(&fence->base); + dma_fence_end_signalling(cookie); } BUG_ON(!list_empty(&fence->head)); + spin_unlock(&fman->lock); + dma_fence_put(&fence->base); + spin_lock(&fman->lock); } spin_unlock(&fman->lock); -- cgit v1.2.3 From f739416dc555fa205a785e5135d73fa39b26f35d Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:26 -0400 Subject: drm/vmwgfx: drop dma_buf reference on foreign-fd prime import ttm_prime_fd_to_handle() returns -ENOSYS when the imported fd's dma_buf->ops do not match the ttm_object_device's ops, but does so without releasing the reference acquired by dma_buf_get(). Any unprivileged renderD client passing a non-vmwgfx prime fd through the DRM_VMW_GB_SURFACE_REF{,_EXT} path leaks one dma_buf reference per call and indefinitely pins the foreign exporter's GEM resources. Funnel the error path through the existing dma_buf_put() so the reference is always dropped. Fixes: 65981f7681ab ("drm/ttm: Add a minimal prime implementation for ttm base objects") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-6-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/ttm_object.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c index 2421b0dd057c..f9042bafdc93 100644 --- a/drivers/gpu/drm/vmwgfx/ttm_object.c +++ b/drivers/gpu/drm/vmwgfx/ttm_object.c @@ -547,14 +547,17 @@ int ttm_prime_fd_to_handle(struct ttm_object_file *tfile, if (IS_ERR(dma_buf)) return PTR_ERR(dma_buf); - if (dma_buf->ops != &tdev->ops) - return -ENOSYS; + if (dma_buf->ops != &tdev->ops) { + ret = -ENOSYS; + goto out; + } prime = (struct ttm_prime_object *) dma_buf->priv; base = &prime->base; *handle = base->handle; ret = ttm_ref_object_add(tfile, base, NULL, false); +out: dma_buf_put(dma_buf); return ret; -- cgit v1.2.3 From 85891d174707d8bddcec7a888fb4e1d17def34f3 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:27 -0400 Subject: drm/vmwgfx: validate DRAW_PRIMITIVES header size before division vmw_cmd_draw() computes maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl); where header->size is u32 and is taken straight from the user-supplied command stream. When header->size is less than sizeof(cmd->body) the unsigned subtraction wraps to nearly 4 GiB, producing a huge maxnum. Any user-controlled cmd->body.numVertexDecls then passes the bound and the loop dereferences decl[i] far past the end of the kernel command bounce buffer, producing an out-of-bounds read of kernel memory. Reject undersized headers up front. Fixes: 7a73ba7469cb ("drm/vmwgfx: Use TTM handles instead of SIDs as user-space surface handles.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-7-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index b07f052474d0..2410d53a75aa 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1571,11 +1571,17 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv, uint32_t maxnum; int ret; + cmd = container_of(header, typeof(*cmd), header); + + if (unlikely(header->size < sizeof(cmd->body))) { + VMW_DEBUG_USER("Illegal DRAW_PRIMITIVES header size.\n"); + return -EINVAL; + } + ret = vmw_cmd_cid_check(dev_priv, sw_context, header); if (unlikely(ret != 0)) return ret; - cmd = container_of(header, typeof(*cmd), header); maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl); if (unlikely(cmd->body.numVertexDecls > maxnum)) { -- cgit v1.2.3 From f4f1db96bfd68b81053693ba53405b6f510ac16c Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:28 -0400 Subject: drm/vmwgfx: bound DMA command body size against suffix pointer vmw_cmd_dma() locates the DMA suffix at (unsigned long) &cmd->body + header->size - sizeof(*suffix) without checking that header->size is large enough to contain both cmd->body and the suffix. An undersized header makes the suffix pointer underflow back into the previous command in the bounce buffer. The verifier later writes suffix->maximumOffset, clobbering verified fields of an already-relocated earlier command -- a TOCTOU on the device-visible command stream that lets one command rewrite another's GMR id, surface id, or other authenticated fields. Reject the command if the body is too small for the suffix to fit. Fixes: 4e4ddd477743 ("drm/vmwgfx: Fix queries if no dma buffer thrashing is occuring.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-8-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 2410d53a75aa..a9136a6523cb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1510,6 +1510,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, bool dirty; cmd = container_of(header, typeof(*cmd), header); + + if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) { + VMW_DEBUG_USER("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n"); + return -EINVAL; + } + suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body + header->size - sizeof(*suffix)); -- cgit v1.2.3 From 05eaa887e7b4f40fba425f8a1d7a5a8a043092a6 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:29 -0400 Subject: drm/vmwgfx: avoid destroy_workqueue(NULL) on vkms init failure Two paths through vmw_vkms_init() can leave vmw->crc_workq NULL while still leaving the rest of the driver in a state that calls vmw_vkms_cleanup() at module unload: 1. vmw_host_get_guestinfo(GUESTINFO_VBLANK, ...) failing or returning an oversized buffer -- the common case on hosts without a VBLANK guestinfo entry -- early-returned before the workqueue allocation. 2. alloc_ordered_workqueue() returning NULL on memory pressure. vmw_vkms_cleanup() then calls destroy_workqueue(NULL), which dereferences wq->name and panics. Fix the first case by removing the early return: vmw->vkms_enabled is already false on the rpci-failure path so no work will ever be queued, and allocating the workqueue unconditionally keeps the control flow simple. Fix the second case by guarding the cleanup with a NULL check, since alloc_ordered_workqueue() can still fail under low memory. Fixes: 7b0062036c3b ("drm/vmwgfx: Implement virtual crc generation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-9-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c index 7b8163b5e501..3d0d5dfa869f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c @@ -206,14 +206,14 @@ vmw_vkms_init(struct vmw_private *vmw) vmw->vkms_enabled = false; ret = vmw_host_get_guestinfo(GUESTINFO_VBLANK, buffer, &buf_len); - if (ret || buf_len > max_buf_len) - return; - buffer[buf_len] = '\0'; + if (!ret && buf_len <= max_buf_len) { + buffer[buf_len] = '\0'; - ret = kstrtobool(buffer, &vmw->vkms_enabled); - if (!ret && vmw->vkms_enabled) { - ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS); - vmw->vkms_enabled = (ret == 0); + ret = kstrtobool(buffer, &vmw->vkms_enabled); + if (!ret && vmw->vkms_enabled) { + ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS); + vmw->vkms_enabled = (ret == 0); + } } vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0); @@ -228,7 +228,8 @@ vmw_vkms_init(struct vmw_private *vmw) void vmw_vkms_cleanup(struct vmw_private *vmw) { - destroy_workqueue(vmw->crc_workq); + if (vmw->crc_workq) + destroy_workqueue(vmw->crc_workq); } bool -- cgit v1.2.3 From d5ed8749168ad13c0dbaa8300f68d854b6076966 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:30 -0400 Subject: drm/vmwgfx: enforce cursor size limits for MOB cursors vmw_cursor_plane_atomic_check() bounds cursor width and height only on the legacy update path; the SVGA_CAP2_CURSOR_MOB path -- the default on modern hosts -- accepts any size. When the requested size exceeds SVGA_REG_CURSOR_MAX_DIMENSION or SVGA_REG_MOB_MAX_SIZE, vmw_cursor_mob_get() returns -EINVAL and leaves vps->cursor.mob NULL. Its return value is then discarded in vmw_cursor_plane_prepare_fb(), so the subsequent vmw_cursor_update_mob() calls vmw_bo_map_and_cache(NULL) and oopses inside vmw_bo_map_and_cache_size() on the tbo.base.size load. Reachable from any DRM master via DRM_IOCTL_MODE_CURSOR2 with a sufficiently large width or height (e.g. cursor_max_dim + 1). Reject oversized cursors in atomic_check for both MOB-backed cursor update types. The MOB byte-size limit only applies to the SVGA_CAP2_CURSOR_MOB path (vmw_cursor_mob_size() returns 0 for GB_ONLY); compute the required MOB size in 64-bit to avoid overflow when very large dimensions are requested. In prepare_fb only call vmw_cursor_mob_get()/_map() for VMW_CURSOR_UPDATE_MOB -- the GB_ONLY path uses bo->map.virtual directly and would otherwise be silently downgraded to NONE on hosts without SVGA_CAP2_CURSOR_MOB (where vmw_cursor_mob_get() always returns -EINVAL). Degrade the update to NONE if vmw_cursor_mob_get() or vmw_cursor_mob_map() fails so the update path does not run with a NULL backing MOB. Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-10-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c | 49 +++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c index b010fc7ca68e..d1e7df500190 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c @@ -432,6 +432,7 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps) u32 size = vmw_cursor_mob_size(vps->cursor.update_type, vps->base.crtc_w, vps->base.crtc_h); struct vmw_bo *vbo = vps->cursor.mob; + void *map; if (!vbo) return -EINVAL; @@ -446,11 +447,15 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps) if (unlikely(ret != 0)) return -ENOMEM; - vmw_bo_map_and_cache(vbo); + map = vmw_bo_map_and_cache(vbo); + if (!map) { + vmw_bo_unmap(vbo); + ret = -ENOMEM; + } ttm_bo_unreserve(&vbo->tbo); - return 0; + return ret; } /** @@ -663,9 +668,15 @@ int vmw_cursor_plane_prepare_fb(struct drm_plane *plane, !vmw_cursor_buffer_changed(vps, old_vps)) { vps->cursor.update_type = VMW_CURSOR_UPDATE_NONE; - } else { - vmw_cursor_mob_get(vcp, vps); - vmw_cursor_mob_map(vps); + } else if (vps->cursor.update_type == + VMW_CURSOR_UPDATE_MOB && + (vmw_cursor_mob_get(vcp, vps) || + vmw_cursor_mob_map(vps))) { + /* + * Reset the cursor to avoid crashes later. + */ + vps->cursor.update_type = + VMW_CURSOR_UPDATE_NONE; } } } @@ -732,6 +743,34 @@ int vmw_cursor_plane_atomic_check(struct drm_plane *plane, "surface not suitable for cursor\n"); return -EINVAL; } + } else if (update_type == VMW_CURSOR_UPDATE_GB_ONLY || + update_type == VMW_CURSOR_UPDATE_MOB) { + u32 cursor_max_dim = + vmw_read(vmw, SVGA_REG_CURSOR_MAX_DIMENSION); + + if (new_state->crtc_w > cursor_max_dim || + new_state->crtc_h > cursor_max_dim) { + drm_warn(&vmw->drm, + "Cursor dimensions (%d, %d) exceed device max %u\n", + new_state->crtc_w, new_state->crtc_h, + cursor_max_dim); + return -EINVAL; + } + + if (update_type == VMW_CURSOR_UPDATE_MOB) { + u32 mob_max_size = + vmw_read(vmw, SVGA_REG_MOB_MAX_SIZE); + u64 mob_size = (u64)new_state->crtc_w * + new_state->crtc_h * sizeof(u32) + + sizeof(SVGAGBCursorHeader); + + if (mob_size > mob_max_size) { + drm_warn(&vmw->drm, + "Cursor MOB size %llu exceeds device max %u\n", + mob_size, mob_max_size); + return -EINVAL; + } + } } return 0; -- cgit v1.2.3 From e5c3e484e0d84744a9bd9349469cd41dc8666a2f Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:31 -0400 Subject: drm/vmwgfx: skip hash_del_rcu when validation context has no hash table vmw_validation_add_resource() calls hash_add_rcu() only when ctx->sw_context is non-NULL, but the doomed-resource error path calls hash_del_rcu() unconditionally. The validation contexts declared with DECLARE_VAL_CONTEXT(_, NULL, 0) in vmwgfx_kms.c, vmwgfx_scrn.c, vmwgfx_stdu.c and vmwgfx_execbuf.c consequently reach a delete for a node that was never added to any hash chain. That is harmless today, but only incidentally so. hash_del_rcu() is hlist_del_init_rcu(), which is guarded by hlist_unhashed(), and vmw_validation_mem_alloc() hands out memory from __GFP_ZERO pages that are never recycled within a context's lifetime, so node->hash.head.pprev is always NULL and the delete does nothing. Neither property is apparent at the call site, and the asymmetry with the add side invites a real bug the first time either one changes. Mirror the condition from the add side so the node is only unlinked when it was actually linked. No functional change. Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-11-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_validation.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c index 35dc94c3db39..45fde7ec514f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c @@ -309,7 +309,8 @@ int vmw_validation_add_resource(struct vmw_validation_context *ctx, } node->res = vmw_resource_reference_unless_doomed(res); if (!node->res) { - hash_del_rcu(&node->hash.head); + if (ctx->sw_context) + hash_del_rcu(&node->hash.head); return -ESRCH; } -- cgit v1.2.3 From 54d56d5b42d2e4c72ba6e365e9774da90698aa22 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:32 -0400 Subject: drm/vmwgfx: use check_add_overflow for shader size+offset bound vmw_shader_define() validates the user-supplied shader window against its backing buffer with (u64)buffer->tbo.base.size < (u64)size + (u64)offset drm_vmw_shader_create_arg::offset is __u64 in the uapi; when it is near U64_MAX the unsigned addition wraps and the resulting tiny value passes the check. The unbounded offset is then stored in res->guest_memory_offset and forwarded to host SVGA shader-create commands. Use check_add_overflow() to detect the wrap and compare the resulting endpoint against the buffer size. Fixes: 668b206601c5 ("drm/vmwgfx: Stop using raw ttm_buffer_object's") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-12-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index eca4e3e97eb4..39811cf19db1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -25,6 +25,8 @@ * **************************************************************************/ +#include + #include #include "vmwgfx_binding.h" @@ -685,7 +687,7 @@ int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data, static int vmw_user_shader_alloc(struct vmw_private *dev_priv, struct vmw_bo *buffer, size_t shader_size, - size_t offset, + u64 offset, SVGA3dShaderType shader_type, uint8_t num_input_sig, uint8_t num_output_sig, @@ -739,7 +741,7 @@ out: static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv, struct vmw_bo *buffer, size_t shader_size, - size_t offset, + u64 offset, SVGA3dShaderType shader_type) { struct vmw_shader *shader; @@ -768,7 +770,7 @@ out_err: static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv, enum drm_vmw_shader_type shader_type_drm, - u32 buffer_handle, size_t size, size_t offset, + u32 buffer_handle, size_t size, u64 offset, uint8_t num_input_sig, uint8_t num_output_sig, uint32_t *shader_handle) { @@ -779,13 +781,16 @@ static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv, int ret; if (buffer_handle != SVGA3D_INVALID_ID) { + u64 end; + ret = vmw_user_bo_lookup(file_priv, buffer_handle, &buffer); if (unlikely(ret != 0)) { VMW_DEBUG_USER("Couldn't find buffer for shader creation.\n"); return ret; } - if ((u64)buffer->tbo.base.size < (u64)size + (u64)offset) { + if (check_add_overflow((u64)size, (u64)offset, &end) || + end > buffer->tbo.base.size) { VMW_DEBUG_USER("Illegal buffer- or shader size.\n"); ret = -EINVAL; goto out_bad_arg; -- cgit v1.2.3 From 706c93c5813caabbb0d0a576c017d15aeec2c113 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:33 -0400 Subject: drm/vmwgfx: validate external BO copy bounds for both stride paths vmw_external_bo_copy() trusts caller-supplied offsets, strides, and heights and operates on imported dma-buf vmaps: - The equal-stride memcpy() bound was clamped after subtracting the offsets from dst_size and src_size; an offset larger than the BO size wraps the unsigned subtraction to a huge value and the resulting memcpy() runs off the end of the vmap. dst_stride * height is also a u32 multiplication that can overflow. - The non-equal-stride row-by-row path had no bound at all. The loop touches bytes through offset + (height - 1) * stride + width_in_bytes, with only a WARN_ON(dst_stride < width_in_bytes), and could likewise step past the end of either mapping. The offsets and strides are derived from STDU/SOU plane state, so a configured CRTC submitting a crafted atomic commit on an imported framebuffer can reach this path. Validate the exact row-copy endpoint against each BO's size up front using check_mul_overflow() and check_add_overflow(). Use the bulk memcpy() path only when width_in_bytes covers the whole stride; otherwise copy one row at a time so partial-row updates near the bottom of a framebuffer remain valid. Also reject zero strides and stride < width_in_bytes, both of which the row-by-row path cannot represent safely. Fixes: 50f119925091 ("drm/vmwgfx: Fix prime with external buffers") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-13-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_blit.c | 39 ++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c index 135b75a3e013..56f965ec99dc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c @@ -30,6 +30,7 @@ #include "vmwgfx_bo.h" #include +#include /* * Template that implements find_first_diff() for a generic @@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, container_of(dst->tbo.bdev, struct vmw_private, bdev); size_t dst_size = dst->tbo.resource->size; size_t src_size = src->tbo.resource->size; + size_t dst_end, src_end; struct iosys_map dst_map = {0}; struct iosys_map src_map = {0}; + bool dst_mapped = false; + bool src_mapped = false; int ret, i; int x_in_bytes; u8 *vsrc; u8 *vdst; + if (!height || !width_in_bytes) + return 0; + + if (!dst_stride || !src_stride) + return -EINVAL; + if (dst_stride < width_in_bytes || src_stride < width_in_bytes) + return -EINVAL; + if (check_mul_overflow((size_t)dst_stride, (size_t)height - 1, &dst_end) || + check_add_overflow(dst_end, (size_t)width_in_bytes, &dst_end) || + check_add_overflow((size_t)dst_offset, dst_end, &dst_end) || + dst_end > dst_size || + check_mul_overflow((size_t)src_stride, (size_t)height - 1, &src_end) || + check_add_overflow(src_end, (size_t)width_in_bytes, &src_end) || + check_add_overflow((size_t)src_offset, src_end, &src_end) || + src_end > src_size) { + drm_dbg_driver(&vmw->drm, "Out-of-bounds external BO copy\n"); + return -EINVAL; + } + vsrc = map_external(src, &src_map); if (!vsrc) { drm_dbg_driver(&vmw->drm, "Wasn't able to map src\n"); ret = -ENOMEM; goto out; } + src_mapped = true; vdst = map_external(dst, &dst_map); if (!vdst) { @@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, ret = -ENOMEM; goto out; } + dst_mapped = true; vsrc += src_offset; vdst += dst_offset; - if (src_stride == dst_stride) { - dst_size -= dst_offset; - src_size -= src_offset; - memcpy(vdst, vsrc, - min(dst_stride * height, min(dst_size, src_size))); + if (src_stride == dst_stride && width_in_bytes == dst_stride) { + memcpy(vdst, vsrc, dst_stride * (size_t)height); } else { - WARN_ON(dst_stride < width_in_bytes); for (i = 0; i < height; ++i) { memcpy(vdst, vsrc, width_in_bytes); vsrc += src_stride; @@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, ret = 0; out: - unmap_external(src, &src_map); - unmap_external(dst, &dst_map); + if (src_mapped) + unmap_external(src, &src_map); + if (dst_mapped) + unmap_external(dst, &dst_map); return ret; } -- cgit v1.2.3 From e65848e4ce352bac9e3465099354c8b8f845391f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 26 Jul 2026 09:50:25 -0500 Subject: ublk: reset kernel-owned dev_info fields in ublk_ctrl_add_dev() ublk_ctrl_add_dev() memcpy()s the userspace ublksrv_ctrl_dev_info into ub->dev_info and then fixes up the fields the driver owns, but misses ->state and ->ublksrv_pid. A device added with ->state = UBLK_S_DEV_LIVE passes the "->state != UBLK_S_DEV_DEAD" test that ublk_stop_dev_unlocked() uses as its proxy for "a disk is attached", while ->ub_disk is still NULL, so DEL_DEV right after ADD_DEV oopses in del_gendisk(). UBLK_S_DEV_QUIESCED plus UBLK_F_USER_RECOVERY dies one step earlier, in ublk_force_abort_dev(). A poisoned ->state also gets START_USER_RECOVERY and the char device read/write path onto a device that was never started, and wedges START_DEV at -EEXIST. A poisoned ->ublksrv_pid just makes GET_DEV_INFO report an unrelated task as the ublk server. Reset both after the memcpy(), as ublk_detach_disk() does. Userspace only ever reads these back, so correcting them silently breaks nothing. ADD_DEV has copied ->state in unsanitized since ublk was merged, but back then it was harmless: the gendisk was allocated during ADD_DEV, and both teardown and the START_DEV -EEXIST check keyed off disk_live() rather than ->state. The oops became reachable once the disk allocation moved to START_DEV and those checks switched to ->state. Fixes: 6d9e6dfdf3b2 ("ublk: defer disk allocation") Cc: stable@vger.kernel.org Signed-off-by: Ming Lei Reviewed-by: Caleb Sander Mateos Link: https://patch.msgid.link/20260726145025.1507383-1-tom.leiming@gmail.com Signed-off-by: Jens Axboe --- drivers/block/ublk_drv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 4ca6ec738c93..2a22f9dc1f2f 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -4764,6 +4764,15 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) /* update device id */ ub->dev_info.dev_id = ub->ub_number; + /* + * ->state and ->ublksrv_pid are owned by the driver and only read back + * by userspace, but they come from the copied-in dev_info, so reset + * them. Otherwise a device added with ->state != DEAD looks live while + * ->ub_disk is still NULL. + */ + ub->dev_info.state = UBLK_S_DEV_DEAD; + ub->dev_info.ublksrv_pid = -1; + /* * 64bit flags will be copied back to userspace as feature * negotiation result, so have to clear flags which driver -- cgit v1.2.3 From 4ecef577d1ceddaa906720c71c06672433aba318 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:30 +0100 Subject: ASoC: SDCA: Rename sdca_irq_allocate() to include devm Make it more clear sdca_irq_allocate() uses devm allocations by adding it into the name. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 5 +++-- sound/soc/sdca/sdca_class.c | 4 ++-- sound/soc/sdca/sdca_interrupts.c | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index a515cc3df097..28fd44eb9334 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -86,8 +86,9 @@ int sdca_irq_populate(struct sdca_function_data *function, void sdca_irq_cleanup(struct device *dev, struct sdca_function_data *function, struct sdca_interrupt_info *info); -struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, - struct regmap *regmap, int irq); + +struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *dev, + struct regmap *regmap, int irq); void sdca_irq_enable_early(struct sdca_function_data *function, struct sdca_interrupt_info *info); diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 8d7b007a068f..d7444f442c71 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -111,8 +111,8 @@ static void class_boot_work(struct work_struct *work) regcache_cache_only(drv->dev_regmap, false); - drv->irq_info = sdca_irq_allocate(drv->dev, drv->dev_regmap, - drv->sdw->irq); + drv->irq_info = devm_sdca_irq_allocate(drv->dev, drv->dev_regmap, + drv->sdw->irq); if (IS_ERR(drv->irq_info)) goto err; diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 4539a52a8e32..1e4efc0609d9 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -592,7 +592,7 @@ void sdca_irq_cleanup(struct device *dev, EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA"); /** - * sdca_irq_allocate - allocate an SDCA interrupt structure for a device + * devm_sdca_irq_allocate - allocate an SDCA interrupt structure for a device * @sdev: Device pointer against which things should be allocated. * @regmap: regmap to be used for accessing the SDCA IRQ registers. * @irq: The interrupt number. @@ -604,8 +604,8 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA"); * Return: A pointer to the allocated sdca_interrupt_info struct, or an * error code. */ -struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev, - struct regmap *regmap, int irq) +struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *sdev, + struct regmap *regmap, int irq) { struct sdca_interrupt_info *info; int ret, i; @@ -634,7 +634,7 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev, return info; } -EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA"); +EXPORT_SYMBOL_NS_GPL(devm_sdca_irq_allocate, "SND_SOC_SDCA"); static void irq_enable_flags(struct sdca_function_data *function, struct sdca_interrupt_info *info, bool early) -- cgit v1.2.3 From f18e97fa7f125e0e7da130b4d0ff1edd48d7510a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:31 +0100 Subject: ASoC: SDCA: Add sdca_irq_cleanup_late() The SDCA IRQs are split into two groups, those registered at bus probe time (basically just FDL) and those registered at component time. There currently exists only a single cleanup function, if the FDL IRQ is freed at component time, then nothing would re-register it if the component is probed again. But the IRQs depending on a component need to be freed if the card is destroyed so they can't use stale components. Split the clean up into two functions one for the component level and one for the bus level. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 5 ++++ sound/soc/sdca/sdca_class_function.c | 2 +- sound/soc/sdca/sdca_interrupts.c | 53 ++++++++++++++++++++++++++++-------- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 28fd44eb9334..38c6c58c2cc7 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -33,6 +33,7 @@ struct sdca_function_data; * @priv: Pointer to private data for use by the handler. * @irq: IRQ number allocated to this interrupt, also used internally to track * the IRQ being assigned. + * @early_request: Flag to indicate this IRQ was requested at bus probe time. */ struct sdca_interrupt { const char *name; @@ -48,6 +49,7 @@ struct sdca_interrupt { void *priv; int irq; + bool early_request; }; /** @@ -86,6 +88,9 @@ int sdca_irq_populate(struct sdca_function_data *function, void sdca_irq_cleanup(struct device *dev, struct sdca_function_data *function, struct sdca_interrupt_info *info); +void sdca_irq_cleanup_late(struct device *dev, + struct sdca_function_data *function, + struct sdca_interrupt_info *info); struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *dev, struct regmap *regmap, int irq); diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 1496a15f7d2a..5ae6c727c796 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -408,7 +408,7 @@ static void class_function_remove(struct auxiliary_device *auxdev) { struct class_function_drv *drv = auxiliary_get_drvdata(auxdev); - sdca_irq_cleanup(drv->dev, drv->function, drv->core->irq_info); + sdca_irq_cleanup_late(drv->dev, drv->function, drv->core->irq_info); } static int class_function_runtime_suspend(struct device *dev) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 1e4efc0609d9..d86884a89c40 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -456,6 +456,8 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, if (ret) return ret; + interrupt->early_request = true; + ret = sdca_fdl_alloc_state(interrupt); if (ret) return ret; @@ -562,17 +564,10 @@ int sdca_irq_populate(struct sdca_function_data *function, } EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA"); -/** - * sdca_irq_cleanup - Free all the individual IRQs for an SDCA Function - * @dev: Device pointer against which the sdca_interrupt_info was allocated. - * @function: Pointer to the SDCA Function. - * @info: Pointer to the SDCA interrupt info for this device. - * - * Typically this would be called from the driver for a single SDCA Function. - */ -void sdca_irq_cleanup(struct device *dev, - struct sdca_function_data *function, - struct sdca_interrupt_info *info) +static void sdca_irq_cleanup_flags(struct device *dev, + struct sdca_function_data *function, + struct sdca_interrupt_info *info, + bool late_cleanup) { int i; @@ -584,13 +579,49 @@ void sdca_irq_cleanup(struct device *dev, if (interrupt->function != function || !interrupt->irq) continue; + if (interrupt->early_request && !late_cleanup) + continue; + sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); kfree(interrupt->name); } } + +/** + * sdca_irq_cleanup - Free the regular IRQs for an SDCA Function + * @dev: Device pointer against which the sdca_interrupt_info was allocated. + * @function: Pointer to the SDCA Function. + * @info: Pointer to the SDCA interrupt info for this device. + * + * Typically this would be called from the driver for a single SDCA Function + * from component remove. + */ +void sdca_irq_cleanup(struct device *dev, + struct sdca_function_data *function, + struct sdca_interrupt_info *info) +{ + sdca_irq_cleanup_flags(dev, function, info, false); +} EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA"); +/** + * sdca_irq_cleanup_late - Free the early IRQs for an SDCA Function + * @dev: Device pointer against which the sdca_interrupt_info was allocated. + * @function: Pointer to the SDCA Function. + * @info: Pointer to the SDCA interrupt info for this device. + * + * Typically this would be called from the driver for a single SDCA Function + * from bus remove. + */ +void sdca_irq_cleanup_late(struct device *dev, + struct sdca_function_data *function, + struct sdca_interrupt_info *info) +{ + sdca_irq_cleanup_flags(dev, function, info, true); +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup_late, "SND_SOC_SDCA"); + /** * devm_sdca_irq_allocate - allocate an SDCA interrupt structure for a device * @sdev: Device pointer against which things should be allocated. -- cgit v1.2.3 From 0880082c27b6251cc3fea307dffa6899ad163e8b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:32 +0100 Subject: ASoC: SDCA: Remove devm from primary IRQ cleanup To provide greater flexibility on when the IRQs are requested for client drivers don't use devm for the primary IRQ request/cleanup helper functions. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_fdl.h | 2 ++ include/sound/sdca_interrupts.h | 2 ++ include/sound/sdca_jack.h | 2 ++ sound/soc/sdca/sdca_fdl.c | 13 +++++++++++-- sound/soc/sdca/sdca_interrupts.c | 8 ++++++++ sound/soc/sdca/sdca_jack.c | 13 +++++++++++-- 6 files changed, 36 insertions(+), 4 deletions(-) diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h index fbaf4b384c8a..dc33927b82bd 100644 --- a/include/sound/sdca_fdl.h +++ b/include/sound/sdca_fdl.h @@ -67,6 +67,8 @@ struct fdl_state { #if IS_ENABLED(CONFIG_SND_SOC_SDCA_FDL) int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt); +void sdca_fdl_free_state(struct sdca_interrupt *interrupt); + int sdca_fdl_process(struct sdca_interrupt *interrupt); int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function, struct sdca_interrupt_info *info); diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 38c6c58c2cc7..8a44c19e917c 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -31,6 +31,7 @@ struct sdca_function_data; * @entity: Pointer to the Entity that the interrupt is associated with. * @control: Pointer to the Control that the interrupt is associated with. * @priv: Pointer to private data for use by the handler. + * @free_priv: Pointer to a function that can be used to free the priv data. * @irq: IRQ number allocated to this interrupt, also used internally to track * the IRQ being assigned. * @early_request: Flag to indicate this IRQ was requested at bus probe time. @@ -47,6 +48,7 @@ struct sdca_interrupt { struct sdca_control *control; void *priv; + void (*free_priv)(struct sdca_interrupt *interrupt); int irq; bool early_request; diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h index 181541f0f4d8..59de40b7d7d0 100644 --- a/include/sound/sdca_jack.h +++ b/include/sound/sdca_jack.h @@ -28,6 +28,8 @@ struct jack_state { }; int sdca_jack_alloc_state(struct sdca_interrupt *interrupt); +void sdca_jack_free_state(struct sdca_interrupt *interrupt); + int sdca_jack_process(struct sdca_interrupt *interrupt); int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack); int sdca_jack_report(struct sdca_interrupt *interrupt); diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index 994821a6df61..82e09d960c12 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -481,10 +481,9 @@ EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA"); */ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) { - struct device *dev = interrupt->dev; struct fdl_state *fdl_state; - fdl_state = devm_kzalloc(dev, sizeof(*fdl_state), GFP_KERNEL); + fdl_state = kzalloc_obj(*fdl_state); if (!fdl_state) return -ENOMEM; @@ -499,3 +498,13 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) return 0; } EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA"); + +/** + * sdca_fdl_free_state - free state for an FDL interrupt + * @interrupt: SDCA interrupt structure. + */ +void sdca_fdl_free_state(struct sdca_interrupt *interrupt) +{ + kfree(interrupt->priv); +} +EXPORT_SYMBOL_NS_GPL(sdca_fdl_free_state, "SND_SOC_SDCA"); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index d86884a89c40..cd2c5d49bb95 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -457,6 +457,7 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, return ret; interrupt->early_request = true; + interrupt->free_priv = sdca_fdl_free_state; ret = sdca_fdl_alloc_state(interrupt); if (ret) @@ -530,6 +531,8 @@ int sdca_irq_populate(struct sdca_function_data *function, handler = function_status_handler; break; case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + interrupt->free_priv = sdca_jack_free_state; + ret = sdca_jack_alloc_state(interrupt); if (ret) return ret; @@ -537,6 +540,8 @@ int sdca_irq_populate(struct sdca_function_data *function, handler = detected_mode_handler; break; case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): + interrupt->free_priv = sdca_fdl_free_state; + ret = sdca_fdl_alloc_state(interrupt); if (ret) return ret; @@ -584,6 +589,9 @@ static void sdca_irq_cleanup_flags(struct device *dev, sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); + if (interrupt->free_priv) + interrupt->free_priv(interrupt); + kfree(interrupt->name); } } diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index ae9636622a84..ffa870964924 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -132,10 +132,9 @@ EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA"); */ int sdca_jack_alloc_state(struct sdca_interrupt *interrupt) { - struct device *dev = interrupt->dev; struct jack_state *jack_state; - jack_state = devm_kzalloc(dev, sizeof(*jack_state), GFP_KERNEL); + jack_state = kzalloc_obj(*jack_state); if (!jack_state) return -ENOMEM; @@ -145,6 +144,16 @@ int sdca_jack_alloc_state(struct sdca_interrupt *interrupt) } EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA"); +/** + * sdca_jack_free_state - free state for a jack interrupt + * @interrupt: SDCA interrupt structure. + */ +void sdca_jack_free_state(struct sdca_interrupt *interrupt) +{ + kfree(interrupt->priv); +} +EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA"); + static int type_get_mask(enum sdca_terminal_type type) { switch (type) { -- cgit v1.2.3 From 050406cbd676615ba71081bce69df710754d27e4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:33 +0100 Subject: ASoC: SDCA: Populate IRQ data earlier Currently, the IRQ data (attached Entity/Control/etc) is populated as the IRQ is requested. However, this can cause issues as occasionally the setup process wants to access specifics of an IRQ before the IRQ is actually enabled. To facilitate this cache all the IRQ data during sdca_irq_populate_early() and make sdca_irq_populate() simply request the outstanding IRQs. This also has the advantage that sdca_irq_populate() can now just iterate through the IRQ array which is much smaller/faster than going through every Entity in the Function for Controls. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 2 + sound/soc/sdca/sdca_interrupts.c | 115 +++++++++++++++------------------------ 2 files changed, 47 insertions(+), 70 deletions(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 8a44c19e917c..3b30146e21db 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -30,6 +30,7 @@ struct sdca_function_data; * @function: Pointer to the Function that the interrupt is associated with. * @entity: Pointer to the Entity that the interrupt is associated with. * @control: Pointer to the Control that the interrupt is associated with. + * @handler: Handler function to be called for the IRQ. * @priv: Pointer to private data for use by the handler. * @free_priv: Pointer to a function that can be used to free the priv data. * @irq: IRQ number allocated to this interrupt, also used internally to track @@ -46,6 +47,7 @@ struct sdca_interrupt { struct sdca_function_data *function; struct sdca_entity *entity; struct sdca_control *control; + irq_handler_t handler; void *priv; void (*free_priv)(struct sdca_interrupt *interrupt); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index cd2c5d49bb95..6f0d8c0fe622 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -421,7 +421,8 @@ static struct sdca_interrupt *get_interrupt_data(struct device *dev, int irq, * * This is intended to be used as part of the Function boot process. It * can be called before the soundcard is registered (ie. doesn't depend - * on component) and will register the FDL interrupts. + * on component) and will populate all the required IRQ data, as well as + * registering the FDL interrupts to start booting the device. * * Return: Zero on success, and a negative error code on failure. */ @@ -448,24 +449,36 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, else if (!interrupt) continue; + ret = sdca_irq_data_populate(dev, regmap, NULL, function, + entity, control, interrupt); + if (ret) + return ret; + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { - case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): - ret = sdca_irq_data_populate(dev, regmap, NULL, - function, entity, - control, interrupt); + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS): + interrupt->handler = function_status_handler; + break; + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + interrupt->handler = detected_mode_handler; + interrupt->free_priv = sdca_jack_free_state; + + ret = sdca_jack_alloc_state(interrupt); if (ret) return ret; - - interrupt->early_request = true; + break; + case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): + interrupt->handler = fdl_owner_handler; interrupt->free_priv = sdca_fdl_free_state; ret = sdca_fdl_alloc_state(interrupt); if (ret) return ret; + interrupt->early_request = true; + ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, - fdl_owner_handler, + interrupt->handler, interrupt); if (ret) { dev_err(dev, "failed to request irq %s: %d\n", @@ -473,7 +486,11 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, return ret; } break; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + interrupt->handler = hid_handler; + break; default: + interrupt->handler = base_handler; break; } } @@ -498,70 +515,26 @@ int sdca_irq_populate(struct sdca_function_data *function, struct sdca_interrupt_info *info) { struct device *dev = component->dev; - int i, j; + int i, ret; guard(mutex)(&info->irq_lock); - for (i = 0; i < function->num_entities; i++) { - struct sdca_entity *entity = &function->entities[i]; - - for (j = 0; j < entity->num_controls; j++) { - struct sdca_control *control = &entity->controls[j]; - int irq = control->interrupt_position; - struct sdca_interrupt *interrupt; - irq_handler_t handler; - int ret; - - interrupt = get_interrupt_data(dev, irq, info); - if (IS_ERR(interrupt)) - return PTR_ERR(interrupt); - else if (!interrupt) - continue; - - ret = sdca_irq_data_populate(dev, NULL, component, - function, entity, control, - interrupt); - if (ret) - return ret; - - handler = base_handler; - - switch (SDCA_CTL_TYPE(entity->type, control->sel)) { - case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS): - handler = function_status_handler; - break; - case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): - interrupt->free_priv = sdca_jack_free_state; - - ret = sdca_jack_alloc_state(interrupt); - if (ret) - return ret; - - handler = detected_mode_handler; - break; - case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): - interrupt->free_priv = sdca_fdl_free_state; + for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { + struct sdca_interrupt *interrupt = &info->irqs[i]; + int irq; - ret = sdca_fdl_alloc_state(interrupt); - if (ret) - return ret; + if (interrupt->function != function || interrupt->irq) + continue; - handler = fdl_owner_handler; - break; - case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): - handler = hid_handler; - break; - default: - break; - } + interrupt->component = component; - ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, - handler, interrupt); - if (ret) { - dev_err(dev, "failed to request irq %s: %d\n", - interrupt->name, ret); - return ret; - } + irq = interrupt->control->interrupt_position; + ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, + interrupt->handler, interrupt); + if (ret) { + dev_err(dev, "failed to request irq %s: %d\n", + interrupt->name, ret); + return ret; } } @@ -581,13 +554,15 @@ static void sdca_irq_cleanup_flags(struct device *dev, for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { struct sdca_interrupt *interrupt = &info->irqs[i]; - if (interrupt->function != function || !interrupt->irq) + if (interrupt->function != function || + (interrupt->early_request && !late_cleanup)) continue; - if (interrupt->early_request && !late_cleanup) - continue; + if (interrupt->irq) + sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); - sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); + if (!late_cleanup) + continue; if (interrupt->free_priv) interrupt->free_priv(interrupt); -- cgit v1.2.3 From 3e81e2fb216327a73510a7aa5318023c379d479a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:34 +0100 Subject: ASoC: Add a component fixup_controls callback A card level fixup_controls callback was added in: commit df4d27b19b89 ("ASoC: Introduce 'fixup_controls' card method") This allowed the machine driver to take actions after all the card controls have been added. However, there are times when a codec driver would also want to do things like obtain references to controls for later use, which require all the controls to be present. Add a component level fixup_controls callback, echoing the card level option. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 2 ++ sound/soc/soc-component.c | 10 ++++++++++ sound/soc/soc-core.c | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index aa423865dbe7..4b7d7954953d 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -78,6 +78,7 @@ struct snd_soc_component_driver { unsigned int num_dapm_routes; int (*probe)(struct snd_soc_component *component); + int (*fixup_controls)(struct snd_soc_component *component); void (*remove)(struct snd_soc_component *component); int (*suspend)(struct snd_soc_component *component); int (*resume)(struct snd_soc_component *component); @@ -380,6 +381,7 @@ void snd_soc_component_suspend(struct snd_soc_component *component); void snd_soc_component_resume(struct snd_soc_component *component); int snd_soc_component_is_suspended(struct snd_soc_component *component); int snd_soc_component_probe(struct snd_soc_component *component); +int snd_soc_component_fixup_controls(struct snd_soc_component *component); void snd_soc_component_remove(struct snd_soc_component *component); int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component, struct device_node *ep); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 21492d15833f..2ce24513fac5 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -310,6 +310,16 @@ int snd_soc_component_probe(struct snd_soc_component *component) return soc_component_ret(component, ret); } +int snd_soc_component_fixup_controls(struct snd_soc_component *component) +{ + int ret = 0; + + if (component->driver->fixup_controls) + ret = component->driver->fixup_controls(component); + + return soc_component_ret(component, ret); +} + void snd_soc_component_remove(struct snd_soc_component *component) { if (component->driver->remove) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7817beea5b3b..44f9bb4473f5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2162,6 +2162,11 @@ static int snd_soc_bind_card(struct snd_soc_card *card) goto probe_end; snd_soc_dapm_new_widgets(card); + for_each_card_components(card, component) { + ret = snd_soc_component_fixup_controls(component); + if (ret < 0) + goto probe_end; + } snd_soc_card_fixup_controls(card); ret = snd_card_register(card->snd_card); -- cgit v1.2.3 From b8f71f16134fadc287fbb14be2a42b707efb7a30 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:35 +0100 Subject: ASoC: SDCA: Switch to fixup_controls callback for IRQ registration Currently there are some race conditions around the boot of SDCA jack detection. The core creates DAPM widgets/routes quite a long time before it creates the associated ALSA control, and the jack detection IRQ is currently registered in component probe. At the time of component probe, the DAPM widgets exist, shortly after this the DAPM routes are added. At the time the DAPM routes are added the register value for the control is checked and the appropriate path is connected. The existing handling in the SDCA jack IRQ handles the case the control doesn't exist and updates the registers directly, which works until the DAPM routes are added. After the routes are added the DAPM graph has already set connected on a particular DAPM path, which will not be updated until an IRQ is received when the control is present. Thus those updates are usually not reflected in the resulting DAPM graph which can lead to the audio path being erroneously powered on/off. Switch to the new fixup_controls callback to register the IRQs, this is guaranteed to run after all the controls have been created. Which means we can avoid the aforementioned race condition and as a bonus no longer need to concern ourselves with a case where the IRQ handler runs and the ALSA control is unavailable. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class_function.c | 4 ++-- sound/soc/sdca/sdca_jack.c | 44 ++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 5ae6c727c796..2fb2b043c979 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -191,7 +191,7 @@ static const struct snd_soc_dai_ops class_function_sdw_ops = { .hw_free = class_function_sdw_remove_peripheral, }; -static int class_function_component_probe(struct snd_soc_component *component) +static int class_function_component_fixup_controls(struct snd_soc_component *component) { struct class_function_drv *drv = snd_soc_component_get_drvdata(component); struct sdca_class_drv *core = drv->core; @@ -217,7 +217,7 @@ static int class_function_set_jack(struct snd_soc_component *component, } static const struct snd_soc_component_driver class_function_component_drv = { - .probe = class_function_component_probe, + .fixup_controls = class_function_component_fixup_controls, .remove = class_function_component_remove, .endianness = 1, }; diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index ffa870964924..3c84d17244a2 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -41,6 +41,7 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) struct jack_state *state = interrupt->priv; struct snd_kcontrol *kctl = state->kctl; struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; + struct soc_enum *soc_enum; unsigned int reg, val; int ret; @@ -55,10 +56,12 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) return -ENOMEM; kctl = snd_soc_component_get_kcontrol(component, name); - if (!kctl) - dev_dbg(dev, "control not found: %s\n", name); - else - state->kctl = kctl; + if (!kctl) { + dev_err(dev, "control not found: %s\n", name); + return -ENODEV; + } + + state->kctl = kctl; } reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, @@ -96,30 +99,21 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) dev_dbg(dev, "%s: %#x\n", interrupt->name, val); - if (kctl) { - struct soc_enum *soc_enum = (struct soc_enum *)kctl->private_value; - - ucontrol = kzalloc_obj(*ucontrol); - if (!ucontrol) - return -ENOMEM; - - ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); + ucontrol = kzalloc_obj(*ucontrol); + if (!ucontrol) + return -ENOMEM; - ret = snd_soc_dapm_put_enum_double(kctl, ucontrol); - if (ret < 0) { - dev_err(dev, "failed to update selected mode: %d\n", ret); - return ret; - } + soc_enum = (struct soc_enum *)kctl->private_value; + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); - snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); - } else { - ret = regmap_write(interrupt->function_regmap, reg, val); - if (ret) { - dev_err(dev, "failed to write selected mode: %d\n", ret); - return ret; - } + ret = snd_soc_dapm_put_enum_double(kctl, ucontrol); + if (ret < 0) { + dev_err(dev, "failed to update selected mode: %d\n", ret); + return ret; } + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + return sdca_jack_report(interrupt); } EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA"); @@ -201,7 +195,7 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja struct sdca_control_range *range; struct jack_state *jack_state; - if (!interrupt->irq) + if (!interrupt->dev) continue; switch (SDCA_CTL_TYPE(entity->type, control->sel)) { -- cgit v1.2.3 From bf1b7821f85383a8804441f8fd5165be148f3ec8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:36 +0100 Subject: ASoC: SDCA: Move kcontrol search out of IRQ Now that the IRQs are always registered after all the ALSA controls are created it is possible to search for the control at the point the IRQ is requested. Move the control search out of the IRQ handler and do it at IRQ request time. This also fixes a potential issue when the card was torn down and reprobed without destroying the codec device, the kctl pointer stored by the IRQ handler would not be updated to the new control on the second probe. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-8-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_jack.h | 1 + sound/soc/sdca/sdca_interrupts.c | 12 +++++++++++ sound/soc/sdca/sdca_jack.c | 43 ++++++++++++++++++++++++---------------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h index 59de40b7d7d0..871ba2d8146a 100644 --- a/include/sound/sdca_jack.h +++ b/include/sound/sdca_jack.h @@ -28,6 +28,7 @@ struct jack_state { }; int sdca_jack_alloc_state(struct sdca_interrupt *interrupt); +int sdca_jack_init_state(struct sdca_interrupt *interrupt); void sdca_jack_free_state(struct sdca_interrupt *interrupt); int sdca_jack_process(struct sdca_interrupt *interrupt); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 6f0d8c0fe622..42fbd3af8a75 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -521,6 +521,8 @@ int sdca_irq_populate(struct sdca_function_data *function, for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { struct sdca_interrupt *interrupt = &info->irqs[i]; + struct sdca_control *control = interrupt->control; + struct sdca_entity *entity = interrupt->entity; int irq; if (interrupt->function != function || interrupt->irq) @@ -528,6 +530,16 @@ int sdca_irq_populate(struct sdca_function_data *function, interrupt->component = component; + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + ret = sdca_jack_init_state(interrupt); + if (ret) + return ret; + break; + default: + break; + } + irq = interrupt->control->interrupt_position; ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, interrupt->handler, interrupt); diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index 3c84d17244a2..73f8067906a9 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -47,23 +47,6 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) guard(rwsem_write)(rwsem); - if (!kctl) { - const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", - interrupt->entity->label, - SDCA_CTL_SELECTED_MODE_NAME); - - if (!name) - return -ENOMEM; - - kctl = snd_soc_component_get_kcontrol(component, name); - if (!kctl) { - dev_err(dev, "control not found: %s\n", name); - return -ENODEV; - } - - state->kctl = kctl; - } - reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, interrupt->control->sel, 0); @@ -148,6 +131,32 @@ void sdca_jack_free_state(struct sdca_interrupt *interrupt) } EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA"); +/** + * sdca_jack_init_state - Initialise transient state for a jack interrupt + * @interrupt: SDCA interrupt structure. + * + * Return: Zero on success or a negative error code. + */ +int sdca_jack_init_state(struct sdca_interrupt *interrupt) +{ + struct jack_state *jack_state = interrupt->priv; + const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", + interrupt->entity->label, + SDCA_CTL_SELECTED_MODE_NAME); + + if (!name) + return -ENOMEM; + + jack_state->kctl = snd_soc_component_get_kcontrol(interrupt->component, name); + if (!jack_state->kctl) { + dev_err(interrupt->dev, "control not found: %s\n", name); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_jack_init_state, "SND_SOC_SDCA"); + static int type_get_mask(enum sdca_terminal_type type) { switch (type) { -- cgit v1.2.3 From dd88cf6273de61f2f7206c2066af97798dbb38b0 Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Mon, 20 Jul 2026 16:16:16 +0800 Subject: ASoC: tas2781: Use correct calibration data for SINEGAIN2 register The SINEGAIN2_REG case in cali_reg_update() references t->sin_gn[] rather than t->sin_gn2[], causing the second pilot tone gain calibration to be programmed with the wrong register address. These are distinct fields in struct fct_param_address and are populated from separate firmware parameters by the parser in tas2781-fmwlib.c. Fixes: 84d6a465f211 ("ASoC: tas2781: Support dsp firmware Alpha and Beta seaies") Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260720081616.631413-1-wangdich9700@163.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 9e6f0ad5f05d..209067e98e1f 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1310,8 +1310,8 @@ static void cali_reg_update(struct bulk_reg_val *p, t->sin_gn[2]); break; case TAS2781_PRM_SINEGAIN2_REG: - reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1], - t->sin_gn[2]); + reg = TASDEVICE_REG(t->sin_gn2[0], t->sin_gn2[1], + t->sin_gn2[2]); break; default: reg = 0; -- cgit v1.2.3 From d9eadfce2fac49445db40808fe4d8259f20a9d2b Mon Sep 17 00:00:00 2001 From: Srikanth Boyapally Date: Mon, 20 Jul 2026 18:25:10 +0530 Subject: spi: spi-cadence: Move TX FIFO full busy-wait into FIFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SPI host transfers could intermittently stall with spi_transfer timeouts. The TXFULL condition was checked only once in cdns_transfer_one() before cdns_spi_process_fifo(), so if the FIFO became full again during refill, writes could be dropped and the transfer would never complete. Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so the 10µs back-off is applied per FIFO entry during filling, ensuring forward progress and eliminating spurious timeouts. Restrict the delay to host mode using spi_controller_is_target(), the controller is passed into cdns_spi_process_fifo() so the check is made at the point of use. In target mode this delay must not run as it causes the target to miss its transfer window and corrupt data. Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()") Signed-off-by: Srikanth Boyapally Reviewed-by: Radhey Shyam Pandey Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 9b4e5b7013ae..af1a05e78492 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi) /** * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO + * @ctlr: Pointer to the spi_controller structure * @xspi: Pointer to the cdns_spi structure * @ntx: Number of bytes to pack into the TX FIFO * @nrx: Number of bytes to drain from the RX FIFO */ -static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) +static void cdns_spi_process_fifo(struct spi_controller *ctlr, + struct cdns_spi *xspi, int ntx, int nrx) { ntx = clamp(ntx, 0, xspi->tx_bytes); nrx = clamp(nrx, 0, xspi->rx_bytes); @@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) } if (ntx) { + /* When xspi in busy condition, bytes may send failed, + * then spi control didn't work thoroughly, add one byte + * delay. Only in host mode; in target mode this delay + * causes data corruption as the target fails to prepare + * data in time. + */ + if (!spi_controller_is_target(ctlr) && + (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)) + udelay(10); + cdns_spi_writer(xspi); ntx--; } @@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) cdns_spi_write(xspi, CDNS_SPI_THLD, 1); if (xspi->tx_bytes) { - cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt); + cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt); } else { /* Fixed delay due to controller limitation with * RX_NEMPTY incorrect status * Xilinx AR:65885 contains more details */ udelay(10); - cdns_spi_process_fifo(xspi, 0, trans_cnt); + cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt); cdns_spi_write(xspi, CDNS_SPI_IDR, CDNS_SPI_IXR_DEFAULT); spi_finalize_current_transfer(ctlr); @@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr, cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1); } - /* When xspi in busy condition, bytes may send failed, - * then spi control didn't work thoroughly, add one byte delay - */ - if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) - udelay(10); - xspi->n_bytes = cdns_spi_n_bytes(transfer); xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); - cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); + cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0); cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); return transfer->len; -- cgit v1.2.3 From b0e8adb2ccb43009796897ced09f91636685c9d3 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 22 Jul 2026 07:14:36 -0700 Subject: hwmon: (nct6775-core) Fix number of temperature registers for NCT6116 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike NCT6106, NCT6116 only has three temperature registers, and with it only three temperature source and temperature source configuration registers. The register addresses match those of NCT6106 and can be re-used. The code used a separate array to list the temperature source registers for NCT6116, but used the size of the NCT6106 register array to set the number of registers. The NCT6106 register array provides six addresses, while the temperature source register array for NCT6116 only provides three addresses. This causes a KASAN report. BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 ... Call Trace: dump_stack+0x7d/0xa7 print_address_description.constprop.0+0x1c/0x220 ? __kasan_kmalloc.constprop.0+0xc9/0xd0 ? __kmalloc_node_track_caller+0x194/0x5b0 ? nct6775_probe+0x936/0x46f0 [nct6775] ? nct6775_probe+0x936/0x46f0 [nct6775] ... Fix the problem by hard-coding the number of temperature and temperature configuration registers to three for NCT6116. Drop the unnecessary NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE. Reported-by: Florian Bezdeka Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") Cc: Björn Gerhart Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index d668dc390def..51253acff4b0 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; -static const u16 NCT6116_REG_TEMP_SOURCE[] = { - 0xb0, 0xb1, 0xb2 }; static const u16 NCT6116_REG_CRITICAL_TEMP[] = { 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; @@ -3652,7 +3650,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, = NCT6106_CRITICAL_PWM_ENABLE_MASK; data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; + data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; @@ -3666,13 +3664,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp = NCT6106_REG_TEMP; reg_temp_mon = NCT6106_REG_TEMP_MON; - num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); + num_reg_temp = 3; num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); reg_temp_over = NCT6106_REG_TEMP_OVER; reg_temp_hyst = NCT6106_REG_TEMP_HYST; reg_temp_config = NCT6106_REG_TEMP_CONFIG; - num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); + num_reg_temp_config = 3; reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; reg_temp_crit = NCT6106_REG_TEMP_CRIT; reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; -- cgit v1.2.3 From a64a7e8a0b012ba81b0eadbd7afc84ab0dbfd70c Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Thu, 23 Jul 2026 17:44:56 +0200 Subject: hwmon: (pmbus/core) notify on the hwmon device, not the i2c client pmbus_notify() calls sysfs_notify() and kobject_uevent() on the i2c client's kobject, but the alarm attributes live on the hwmon class device registered by pmbus_do_probe(). Notifying the parent i2c device is a no-op for both poll(POLLPRI) waiters and udev listeners: the named attribute does not exist on that kobject. Notify the hwmon device instead, so poll() wakes up and "change" uevents fire on the inX_alarm/tempX_alarm attributes when SMBALERT# reports a fault. Fixes: f469bde9afd1 ("hwmon: (pmbus/core) Notify hwmon events") Cc: stable@vger.kernel.org # v6.4+ Signed-off-by: Vincent Jardin Link: https://lore.kernel.org/r/20260723-fix_hwmon_notify_v1-v1-1-5a24c528686d@free.fr Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 3143b9e0316c..0081f16c3a95 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2985,8 +2985,9 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags) if (reg == sreg && page == spage && (smask & flags)) { dev_dbg(data->dev, "sysfs notify: %s", da->attr.name); - sysfs_notify(&data->dev->kobj, NULL, da->attr.name); - kobject_uevent(&data->dev->kobj, KOBJ_CHANGE); + sysfs_notify(&data->hwmon_dev->kobj, NULL, + da->attr.name); + kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE); flags &= ~smask; } -- cgit v1.2.3 From e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 10 Jun 2026 07:46:16 -0700 Subject: hwmon: (ina2xx) Fix various overflow issues Sashiko reports several integer overflow problems in the ina2xx driver caused by unbounded multiplications and inadequate types for intermediate calculations. Specifically: - In ina2xx_get_value(), the return type is changed from int to long. Intermediate calculations for current are now performed using 64-bit types to prevent 32-bit integer overflow before the division by 1000. - When calculating power in ina2xx_get_value() and sy24655_average_power_read(), interim values are cast to u64 and clamped to LONG_MAX. This prevents overflow when regval or accumulator_24 is multiplied by power_lsb_uW. - In ina226_alert_to_reg(), the clamping logic is rewritten using min_t(). This safely avoids integer overflows when scaling user-provided values for shunt voltage, bus voltage, power, and current limits. Cc: Loic Poulain Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues") Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 61 +++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index c4742e84b999..449a72c6b40b 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -266,30 +267,34 @@ static u16 ina226_interval_to_reg(long interval) return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); } -static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, - unsigned int regval) +static long ina2xx_get_value(struct ina2xx_data *data, u8 reg, + unsigned int regval) { - int val; + s64 val64; + long val; switch (reg) { case INA2XX_SHUNT_VOLTAGE: /* signed register */ - val = (s16)regval >> data->config->shunt_voltage_shift; - val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); + val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift, + data->config->shunt_div); break; case INA2XX_BUS_VOLTAGE: - val = (regval >> data->config->bus_voltage_shift) * - data->config->bus_voltage_lsb; - val = DIV_ROUND_CLOSEST(val, 1000); + val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) * + data->config->bus_voltage_lsb, 1000); break; case INA2XX_POWER: - val = regval * data->power_lsb_uW; + val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX); break; case INA2XX_CURRENT: /* signed register, result in mA */ - val = ((s16)regval >> data->config->current_shift) * + val64 = (s64)((s16)regval >> data->config->current_shift) * data->current_lsb_uA; - val = DIV_ROUND_CLOSEST(val, 1000); + if (val64 < 0) + val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000); + else + val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000); + val = clamp_val(val64, LONG_MIN, LONG_MAX); break; case INA2XX_CALIBRATION: val = regval; @@ -378,27 +383,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) */ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) { + long limit; + switch (reg) { case INA2XX_SHUNT_VOLTAGE: - val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); - val *= data->config->shunt_div; - val <<= data->config->shunt_voltage_shift; - return clamp_val(val, 0, SHRT_MAX); + val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div)); + return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift, + SHRT_MAX); case INA2XX_BUS_VOLTAGE: - val = clamp_val(val, 0, 200000); - val = (val * 1000) << data->config->bus_voltage_shift; - val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); - return clamp_val(val, 0, USHRT_MAX); + val = min_t(long, val, 130000); + return min_t(long, + DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift, + data->config->bus_voltage_lsb), + USHRT_MAX); case INA2XX_POWER: - val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); - val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); - return clamp_val(val, 0, USHRT_MAX); + val = min_t(long, val, LONG_MAX - data->power_lsb_uW); + return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX); case INA2XX_CURRENT: - val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); + limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000; + val = min_t(long, val, limit); /* signed register, result in mA */ val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); - val <<= data->config->current_shift; - return clamp_val(val, SHRT_MIN, SHRT_MAX); + limit = SHRT_MAX >> data->config->current_shift; + return (u16)(min_t(long, val, limit) << data->config->current_shift); default: /* programmer goofed */ WARN_ON_ONCE(1); @@ -537,6 +544,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va u8 template[6]; int ret; long accumulator_24, sample_count; + u64 val64; /* 48-bit register read */ ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template); @@ -555,7 +563,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va return 0; } - *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; + val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; + *val = min_t(u64, val64, LONG_MAX); return 0; } -- cgit v1.2.3 From 00feb1cce93dab948a299b69753d99c681d45a0b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 5 Feb 2025 12:27:15 -0800 Subject: hwmon: (ltc4282) Fix reading the minimum alarm voltage Coverity reports an out-of-bounds access when reading the minimum alarm voltage for the VGPIO channel. Add the missing return statement to fix the problem. Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282") Cc: Nuno Sa Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4282.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index 39b9d3abca99..cc698803f8bf 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -374,8 +374,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val, channel, val); case hwmon_in_min_alarm: if (channel == LTC4282_CHAN_VGPIO) - ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, - LTC4282_GPIO_ALARM_L_MASK, val); + return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, + LTC4282_GPIO_ALARM_L_MASK, val); return ltc4282_vdd_source_read_alm(st, LTC4282_VSOURCE_ALARM_L_MASK, -- cgit v1.2.3 From f46d5ab43a572b84773015a76966f5da56fc1748 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jul 2026 09:34:46 -0700 Subject: hwmon: (sht3x) Fix unaligned accesses Sashiko reports: In sht3x_update_client(), the 16-bit temperature and humidity values are extracted from a stack-allocated byte array using be16_to_cpup(). The pointers passed to this function are calculated as buf and buf + 3. Since the difference between the two pointers is an odd number of bytes, at least one of them is guaranteed to be at an unaligned offset. This will trigger an alignment fault on strict-alignment architectures such as ARMv5 or SPARC, resulting in a kernel panic. Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(), and put_unaligned_be16() instead of cpu_to_be16(). Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors") Reported-by: Sashiko Signed-off-by: Guenter Roeck --- drivers/hwmon/sht3x.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index c2f6b73aa7f3..4d90f89a9929 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -21,6 +21,7 @@ #include #include #include +#include /* commands (high repeatability mode) */ static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 }; @@ -276,9 +277,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev) if (ret) goto out; - val = be16_to_cpup((__be16 *)buf); + val = get_unaligned_be16(buf); data->temperature = sht3x_extract_temperature(val); - val = be16_to_cpup((__be16 *)(buf + 3)); + val = get_unaligned_be16(buf + 3); data->humidity = sht3x_extract_humidity(val); data->last_update = jiffies; } @@ -336,7 +337,7 @@ static int limits_update(struct sht3x_data *data) if (ret) return ret; - raw = be16_to_cpup((__be16 *)buffer); + raw = get_unaligned_be16(buffer); temperature = sht3x_extract_temperature((raw & 0x01ff) << 7); humidity = sht3x_extract_humidity(raw & 0xfe00); data->temperature_limits[index] = temperature; @@ -389,7 +390,7 @@ static size_t limit_write(struct device *dev, raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7); raw |= ((humidity * 42950) >> 16) & 0xfe00; - *((__be16 *)position) = cpu_to_be16(raw); + put_unaligned_be16(raw, position); position += SHT3X_WORD_LEN; *position = crc8(sht3x_crc8_table, position - SHT3X_WORD_LEN, -- cgit v1.2.3 From aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jul 2026 15:27:28 -0700 Subject: hwmon: (lm90) Only report alarms if driver is ready Userspace can read sysfs attributes before driver registration is complete, immediately after devm_hwmon_device_register_with_info() has been called. At that time, data->hwmon_dev is not yet initialized. This can trigger a NULL pointer access since lm90_update_device() and with it lm90_update_alarms_locked() will be called. This call schedules report_work and lm90_report_alarms(), which passes the still-NULL data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer dereference. Fix the problem by only scheduling the report and alert workers data->hwmon_dev is set. Reported-by: Sashiko Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 4b9c0ccdf260..c6186505661f 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1194,7 +1194,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) check_enable = (client->irq || !(data->config_orig & 0x80)) && (data->config & 0x80); - if (force || check_enable) + if (data->hwmon_dev && (force || check_enable)) schedule_work(&data->report_work); /* @@ -1202,7 +1202,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) * alarms are all clear, and alerts are currently disabled. * Otherwise (re)schedule worker if needed. */ - if (check_enable) { + if (check_enable && data->hwmon_dev) { if (!(data->current_alarms & data->alert_alarms)) { dev_dbg(&client->dev, "Re-enabling ALERT#\n"); lm90_update_confreg(data, data->config & ~0x80); -- cgit v1.2.3 From 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Jul 2026 09:54:23 -0700 Subject: hwmon: (nzxt-smart2) DMA-align output buffer Sashiko reports: When send_output_report() calls hid_hw_output_report(), the underlying USB HID core calls usb_interrupt_msg() which maps this buffer directly for DMA. When the DMA mapping flushes or invalidates the cacheline, it will corrupt the adjacent variables (mutex, update_interval) that were modified concurrently by the CPU. This causes memory corruption due to cacheline sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings for this violation. Any operation that triggers send_output_report() (like setting a fan speed or updating the interval) causes the USB DMA mapping. On systems with non-coherent caches, this structural bug causes immediate and deterministic memory corruption. Align the output buffer to ARCH_DMA_MINALIGN to fix the problem. Reported-by: Sashiko Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") Cc: Aleksandr Mezin Signed-off-by: Guenter Roeck --- drivers/hwmon/nzxt-smart2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index e2316c46629d..ff0c0bee0e83 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -203,7 +203,7 @@ struct drvdata { */ struct mutex mutex; long update_interval; - u8 output_buffer[OUTPUT_REPORT_SIZE]; + u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN); }; static long scale_pwm_value(long val, long orig_max, long new_max) -- cgit v1.2.3 From cdac670237258c8ca063aa8a16998f680d81b80d Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 23 Jul 2026 10:11:40 +0800 Subject: i2c: spacemit: request IRQ after controller initialization spacemit_i2c_probe() requests the IRQ before it enables the clocks, resets the controller and runs init_completion(). If an interrupt is already pending, the handler runs too early: it reads registers while the clocks are still off and calls complete() on an uninitialized completion. Request the IRQ after the controller and completion are initialized, but still before the adapter is registered. Fixes: 5ea558473fa3 ("i2c: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Linmao Li Cc: # v6.15+ Reviewed-by: Troy Mitchell Reviewed-by: Alex Elder Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260723021140.2293844-1-lilinmao@kylinos.cn --- drivers/i2c/busses/i2c-k1.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 51a0c3d80fc9..3fe716cc153d 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -723,11 +723,6 @@ static int spacemit_i2c_probe(struct platform_device *pdev) if (i2c->irq < 0) return dev_err_probe(dev, i2c->irq, "failed to get irq resource"); - ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, - IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c); - if (ret) - return dev_err_probe(dev, ret, "failed to request irq"); - clk = devm_clk_get_enabled(dev, "func"); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock"); @@ -755,6 +750,11 @@ static int spacemit_i2c_probe(struct platform_device *pdev) init_completion(&i2c->complete); + ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, + IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq"); + platform_set_drvdata(pdev, i2c); ret = i2c_add_numbered_adapter(&i2c->adapt); -- cgit v1.2.3 From 82048795242f04275a3f49ffc66ad851b6120954 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 21 Jul 2026 23:41:47 +0900 Subject: i2c: amd-mp2: Unregister callback on adapter add failure amd_mp2_register_cb() stores the platform I2C context in the MP2 PCI driver's callback table before the adapter is registered. If i2c_add_adapter() fails, probe returns and devres frees the context, but the PCI driver can still dereference the stale pointer from its IRQ and system-sleep callbacks. Unregister the callback before returning the adapter registration error. Fixes: 529766e0a011 ("i2c: Add drivers for the AMD PCIe MP2 I2C controller") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Cc: # v5.2+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260721144147.31150-1-mhun512@gmail.com --- drivers/i2c/busses/i2c-amd-mp2-plat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c index 188e24cc4d35..9fdd6a5fb8b6 100644 --- a/drivers/i2c/busses/i2c-amd-mp2-plat.c +++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c @@ -316,8 +316,10 @@ static int i2c_amd_probe(struct platform_device *pdev) amd_mp2_pm_runtime_put(mp2_dev); - if (ret < 0) + if (ret < 0) { dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret); + amd_mp2_unregister_cb(&i2c_dev->common); + } return ret; } -- cgit v1.2.3 From dbc3791e3b2472e1ccc08947e0f83b443470ff4f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 24 Jul 2026 07:29:01 +0000 Subject: net: do not send ICMP/NDISC Redirects when peer allocation fails When inet_getpeer_v4() or inet_getpeer_v6() fails to allocate a peer entry under memory pressure or tree size caps, redirect handlers previously fell back to sending un-rate-limited ICMP/NDISC Redirect messages. In IPv4, ip_rt_send_redirect() called icmp_send() directly when peer == NULL. In IPv6, ip6_forward() and ndisc_send_redirect() passed a NULL peer into inet_peer_xrlim_allow(), which returned true when peer == NULL. Because ICMP/NDISC Redirects are not part of the default global rate limit mask (sysctl_icmp_ratemask), sending redirects when peer == NULL creates an un-rate-limited ICMP packet storm. Fix this by failing closed in ip_rt_send_redirect(), ip6_forward(), and ndisc_send_redirect() when peer is NULL. Fixes: 92d868292634 ("inetpeer: Move ICMP rate limiting state into inet_peer entries.") Signed-off-by: Eric Dumazet Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260724072901.1633601-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv4/route.c | 2 -- net/ipv6/ip6_output.c | 2 +- net/ipv6/ndisc.c | 2 ++ 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3f3de5164d6e..152d8cb28f65 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -892,8 +892,6 @@ void ip_rt_send_redirect(struct sk_buff *skb) peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif); if (!peer) { rcu_read_unlock(); - icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, - rt_nexthop(rt, ip_hdr(skb)->daddr)); return; } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 368e4fa3b43c..2c44e5ed6171 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -641,7 +641,7 @@ int ip6_forward(struct sk_buff *skb) /* Limit redirects both by destination (here) and by source (inside ndisc_send_redirect) */ - if (inet_peer_xrlim_allow(peer, 1*HZ)) + if (peer && inet_peer_xrlim_allow(peer, 1*HZ)) ndisc_send_redirect(skb, target); rcu_read_unlock(); } else { diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f867ec8d3d90..fe36b3f51285 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1707,6 +1707,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) } peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr); + if (!peer) + goto release; ret = inet_peer_xrlim_allow(peer, 1*HZ); if (!ret) -- cgit v1.2.3 From d99607c888f26e8a4e9fe9772860cef4aff86bb4 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Sun, 19 Jul 2026 22:19:43 +0200 Subject: i2c: jz4780: Cache host clock rate at probe to prevent CCF prepare_lock deadlock Fix a severe AB/BA deadlock between the Common Clock Framework (CCF) and the I2C adapter lock, which triggers when an I2C-controlled clock generator client (like the Si5351) is registered or modified under the CCF. During an i2c client clock (generator) frequency change, the CCF acquires its global 'prepare_lock' mutex and the driver calls i2c_transfer() to update the client's chip registers, stalling for the adapter's I2C bus lock. Concurrently, an independent, parallel transfer on the same bus (e.g., a GPIO expander handling LEDs) can hold the I2C adapter lock. Inside this parallel transfer path, jz4780_i2c_set_speed() calls clk_get_rate() on the host controller's input clock to calculate bus timings. This call attempts to acquire the blocked CCF 'prepare_lock', creating a circular dependency that freezes the system. The jz4780 host controller clock itself is static and never changes at runtime. However, calling clk_get_rate() inside the active transfer path introduces an unnecessary dependency on the CCF internal locks. Eliminate this synchronous clk_get_rate() call from the active transfer path by caching the static host peripheral clock rate once - inside the private jz4780_i2c structure during jz4780_i2c_probe(). Update jz4780_i2c_set_speed() to use this cached value, safely decoupling active I2C transactions from the CCF internal locks without any risk of stale timings. Assisted-by web based Google AI (pinpointing the bug and writing the message). Fixes: ba92222ed63a12 ("i2c: jz4780: Add i2c bus controller driver for Ingenic JZ4780") Signed-off-by: H. Nikolaus Schaller Cc: # v4.1+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/2db6fd233aceb7238474e4833f4d25ca681c3ffb.1784492382.git.hns@goldelico.com --- drivers/i2c/busses/i2c-jz4780.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index 664a5471d933..695be3b21460 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -141,6 +141,7 @@ struct jz4780_i2c { void __iomem *iomem; int irq; struct clk *clk; + unsigned long clk_rate_khz; struct i2c_adapter adap; const struct ingenic_i2c_config *cdata; @@ -246,7 +247,7 @@ static int jz4780_i2c_set_target(struct jz4780_i2c *i2c, unsigned char address) static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c) { - int dev_clk_khz = clk_get_rate(i2c->clk) / 1000; + int dev_clk_khz = i2c->clk_rate_khz; int cnt_high = 0; /* HIGH period count of the SCL clock */ int cnt_low = 0; /* LOW period count of the SCL clock */ int cnt_period = 0; /* period count of the SCL clock */ @@ -796,6 +797,8 @@ static int jz4780_i2c_probe(struct platform_device *pdev) if (IS_ERR(i2c->clk)) return PTR_ERR(i2c->clk); + i2c->clk_rate_khz = clk_get_rate(i2c->clk) / 1000; + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clk_freq); if (ret) { -- cgit v1.2.3 From a60f58eb70e4e4c2ba4ace8b292dea64e5b7b290 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Jul 2026 13:13:54 -0700 Subject: hwmon: (lm63) Mask PWM frequency multiplier to supported bits Sashiko is concerned that reading a PWM frequency multiplier outside the supported range of [1, 31] might result in bad PWM values written to the chip. Technically, the chip should never return a value with the upper 3 bits set, so this should never happen. However, it is unknown if there are LM63 variants where the upper bits of the register can be written. Mask the register value read from the chip to only accept the lower 5 bit when reading it from the chip to avoid the problem. Reported-by: Sashiko Signed-off-by: Guenter Roeck --- drivers/hwmon/lm63.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index e2a429e579ac..da09770c05e7 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -247,8 +247,7 @@ static struct lm63_data *lm63_update_device(struct device *dev) LM63_REG_TACH_LIMIT_MSB) << 8); } - data->pwm1_freq = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_FREQ); + data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ) & 0x1f; if (data->pwm1_freq == 0) data->pwm1_freq = 1; data->pwm1[0] = i2c_smbus_read_byte_data(client, @@ -1187,7 +1186,7 @@ static void lm63_init_client(struct lm63_data *data) data->config |= 0x04; /* We may need pwm1_freq before ever updating the client data */ - data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ); + data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ) & 0x1f; if (data->pwm1_freq == 0) data->pwm1_freq = 1; -- cgit v1.2.3 From d0b704e569ac3b8416d8e02270cdc9bf830ed395 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Jul 2026 13:35:37 -0700 Subject: hwmon: (nct6775-core) Prevent access to unsupported weight registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sashiko reports: During initialization of the nct6116 chip, the driver sets data->pwm_num to 5. However, it assigns several NCT6106 register arrays (such as NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. These arrays only contain 3 elements. In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If data->has_pwm has bits 3 or 4 set (which is structurally possible for nct6116), the loop attempts to read elements at index 3 and 4 from these 3-element arrays. This results in a global out-of-bounds read, which can be caught by KASAN. Furthermore, the driver uses these garbage out-of-bounds values as hardware register addresses for subsequent read and write operations. This leads to invalid hardware register access, potentially causing hardware misconfiguration or system crashes. The underlying problem is that the chip does support up to five fan control channels, but only the first three support weight control. Fix the problem by extending the affected weight register arrays with zeroed fields. The driver uses zeroed register addresses to determine if a register is supported or not, and skips accesses for unsupported registers. Reported-by: Sashiko Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") Cc: Björn Gerhart Cc: Florian Bezdeka Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index 51253acff4b0..94482c8092cd 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; -static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; -static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; -static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; +static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; -- cgit v1.2.3 From 8d2b10eef6f3b1336be2d1197db29b0c697f1d41 Mon Sep 17 00:00:00 2001 From: Surendra Singh Chouhan Date: Thu, 23 Jul 2026 07:56:34 +0530 Subject: watchdog: atcwdt200: fix return value when watchdog is enabled atcwdt_get_int_timer_type() returned TMR_UNKNOWN (enum value 3) when CTRL_WDT_EN was set in the control register. Because atcwdt_probe() checks "ret = atcwdt_get_int_timer_type(drv_data); if (ret) return ret;", returning TMR_UNKNOWN caused probe() to return 3 instead of a valid negative error code. Fix this by returning -EBUSY via dev_err_probe() when the watchdog is already enabled and update the kernel-doc description accordingly. Fixes: e4e0848ad046 ("watchdog: atcwdt200: Add driver for Andes ATCWDT200") Signed-off-by: Surendra Singh Chouhan Link: https://lore.kernel.org/r/20260723022634.8642-1-kr494167@gmail.com Signed-off-by: Guenter Roeck --- drivers/watchdog/atcwdt200_wdt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/atcwdt200_wdt.c b/drivers/watchdog/atcwdt200_wdt.c index 8e3b18aea368..7e757c217b0b 100644 --- a/drivers/watchdog/atcwdt200_wdt.c +++ b/drivers/watchdog/atcwdt200_wdt.c @@ -260,9 +260,9 @@ static void atcwdt_get_timeout_params(struct atcwdt_drv *drv_data, * register to determine the interrupt timer type supported by the hardware. * * Note: This function must only be called when the ATCWDT200 watchdog is - * disabled. If the watchdog is enabled, this function returns TMR_UNKNOWN. + * disabled. If the watchdog is enabled, this function returns -EBUSY. * - * Returns: The interrupt timer type supported by the hardware. + * Returns: 0 on success or negative error code on failure. */ static int atcwdt_get_int_timer_type(struct atcwdt_drv *drv_data) { @@ -274,7 +274,8 @@ static int atcwdt_get_int_timer_type(struct atcwdt_drv *drv_data) regmap_read(drv_data->regmap, REG_CTRL, &val); if (val & CTRL_WDT_EN) { spin_unlock(&drv_data->lock); - return TMR_UNKNOWN; + return dev_err_probe(dev, -EBUSY, + "Watchdog is enabled, cannot detect timer type\n"); } /* -- cgit v1.2.3 From 1395a676ec15a0a02a2a6d86602324f2d5fd41d5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:45 +0000 Subject: vxlan: re-fetch eth header after route_shortcircuit() Before route_shortcircuit(), the eth header pointer is cached from eth_hdr(skb). Inside route_shortcircuit(), pskb_may_pull() can be called, which may reallocate skb->head. In this case, returning to vxlan_xmit() leaves the cached eth pointer pointing to freed memory, leading to a use-after-free when dereferencing eth->h_dest. Fix this by updating eth = eth_hdr(skb) after calling route_shortcircuit(). Fixes: ae8840825605 ("VXLAN: Allow L2 redirection with L3 switching") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260723144249.759100-2-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index d834a4865aec..a05654a55bd6 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2796,6 +2796,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) (ntohs(eth->h_proto) == ETH_P_IP || ntohs(eth->h_proto) == ETH_P_IPV6)) { did_rsc = route_shortcircuit(dev, skb); + eth = eth_hdr(skb); if (did_rsc) f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni); } -- cgit v1.2.3 From 760d36e737f2b3867762f42af36c663f55babcc4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:46 +0000 Subject: vxlan: unclone skb head before modifying eth header in route_shortcircuit() When route_shortcircuit() performs L3 short-circuit routing, it modifies the Ethernet header of the skb in-place: memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, dev->addr_len); memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len); If the incoming skb is cloned (for example by packet sockets, tcpdump, or dev_queue_xmit), modifying the Ethernet header without uncloning can corrupt the packet header for other readers holding a reference to the cloned skb. Ensure the skb header is writable and unshared by calling skb_cow_head(skb, 0) prior to updating the Ethernet header. If skb_cow_head() fails, abort short-circuiting and return false to allow standard packet processing fallback. Fixes: e4f67addf158 ("add DOVE extensions for VXLAN") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260723144249.759100-3-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index a05654a55bd6..e831fe203442 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2163,6 +2163,10 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha); if (diff) { + if (skb_cow_head(skb, 0)) { + neigh_release(n); + return false; + } memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, dev->addr_len); memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len); -- cgit v1.2.3 From 8eca411347e1d38964f9ed2c8d3b6ab0e7e4473d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:47 +0000 Subject: vxlan: use neigh_ha_snapshot() in route_shortcircuit() The neighbour hardware address n->ha can be updated asynchronously by the neighbour subsystem, protected by n->ha_lock seqlock. Reading n->ha without holding the seqlock loop can lead to torn reads or reading a partially updated MAC address. Use neigh_ha_snapshot() in route_shortcircuit() to safely copy n->ha under read_seqbegin()/read_seqretry() lock protection before using it. Note that arp_reduce() and neigh_reduce() seem to have the same issue left for future patches. Fixes: e4f67addf158 ("add DOVE extensions for VXLAN") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260723144249.759100-4-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index e831fe203442..be3c2bc2cd9a 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2159,9 +2159,11 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) } if (n) { + u8 haddr[ETH_ALEN]; bool diff; - diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha); + neigh_ha_snapshot(haddr, n, dev); + diff = !ether_addr_equal_unaligned(eth_hdr(skb)->h_dest, haddr); if (diff) { if (skb_cow_head(skb, 0)) { neigh_release(n); @@ -2169,7 +2171,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) } memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, dev->addr_len); - memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len); + memcpy(eth_hdr(skb)->h_dest, haddr, dev->addr_len); } neigh_release(n); return diff; -- cgit v1.2.3 From 26bb2dd0a8839617e2c79ffbbe1923f8e4bab9fb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:48 +0000 Subject: vxlan: use pskb_network_may_pull() in route_shortcircuit() route_shortcircuit() currently calls pskb_may_pull(skb, sizeof(struct iphdr)) (or ipv6hdr), which checks if bytes are available starting from skb->data. However, in vxlan_xmit(), skb->data points to the MAC header, so skb_network_offset(skb) is ETH_HLEN (14 bytes). Using pskb_may_pull(skb, 20) only checks 20 bytes from skb->data (which is 14 bytes MAC header + 6 bytes of IP header), leaving the rest of the IP header potentially un-pulled in non-linear frags. Subsequent dereferences of ip_hdr(skb)->daddr can read beyond the pulled linear buffer length. Fix this by using pskb_network_may_pull(), which adds skb_network_offset(skb) to the length check to ensure the full network header is present in the linear buffer. Fixes: e4f67addf158 ("add DOVE extensions for VXLAN") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260723144249.759100-5-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index be3c2bc2cd9a..2163e2687db0 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2111,7 +2111,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) { struct iphdr *pip; - if (!pskb_may_pull(skb, sizeof(struct iphdr))) + if (!pskb_network_may_pull(skb, sizeof(struct iphdr))) return false; pip = ip_hdr(skb); n = neigh_lookup(&arp_tbl, &pip->daddr, dev); @@ -2137,7 +2137,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) */ if (!ipv6_mod_enabled()) return false; - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) return false; pip6 = ipv6_hdr(skb); n = neigh_lookup(&nd_tbl, &pip6->daddr, dev); -- cgit v1.2.3 From b9553558b48db54ac9273e6b98d7263ef5c1a329 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 23 Jul 2026 14:42:49 +0000 Subject: vxlan: use pskb_network_may_pull() for transmit path header pulls In vxlan_xmit(), arp_reduce(), and vxlan_mdb_entry_skb_get(), pskb_may_pull() was being called to verify the availability of network layer headers (ARP, IPv6/ND, IP/IPv6 MDB keys). However, during transmit skb->data points to the MAC header, so skb_network_offset(skb) is ETH_HLEN (14 bytes). Using pskb_may_pull(skb, len) only checks len bytes from skb->data rather than skb_network_offset(skb) + len, which can leave part of the network header in non-linear frags. Replace these remaining pskb_may_pull() calls with pskb_network_may_pull() to properly account for the MAC header offset. Fixes: e4f67addf158 ("add DOVE extensions for VXLAN") Fixes: f564f45c4518 ("vxlan: add ipv6 proxy support") Fixes: 0f83e69f44bf ("vxlan: Add MDB data path support") Signed-off-by: Eric Dumazet Cc: stable@vger.kernel.org Reviewed-by: Vadim Fedorenko Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260723144249.759100-6-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_core.c | 6 +++--- drivers/net/vxlan/vxlan_mdb.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 2163e2687db0..1ded27768a97 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1850,7 +1850,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) if (dev->flags & IFF_NOARP) goto out; - if (!pskb_may_pull(skb, arp_hdr_len(dev))) { + if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) { dev_dstats_tx_dropped(dev); vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); @@ -2763,8 +2763,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) return arp_reduce(dev, skb, vni); #if IS_ENABLED(CONFIG_IPV6) else if (ntohs(eth->h_proto) == ETH_P_IPV6 && - pskb_may_pull(skb, sizeof(struct ipv6hdr) + - sizeof(struct nd_msg)) && + pskb_network_may_pull(skb, sizeof(struct ipv6hdr) + + sizeof(struct nd_msg)) && ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) { struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1); diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index af7a0d7f95a5..9a9038ae90c1 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -1631,7 +1631,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, switch (skb->protocol) { case htons(ETH_P_IP): - if (!pskb_may_pull(skb, sizeof(struct iphdr))) + if (!pskb_network_may_pull(skb, sizeof(struct iphdr))) return NULL; group.dst.sa.sa_family = AF_INET; group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr; @@ -1640,7 +1640,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, break; #if IS_ENABLED(CONFIG_IPV6) case htons(ETH_P_IPV6): - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) return NULL; group.dst.sa.sa_family = AF_INET6; group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr; -- cgit v1.2.3 From b4f1719dfea023220e0e6bd892b087d76b2a6a49 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Fri, 24 Jul 2026 00:38:41 +0800 Subject: tipc: avoid use-after-free in poll trace queue dumps TIPC socket tracepoints dump queue state through tipc_sk_dump(). Most queue-dump callsites already serialize that walk under the socket lock or sk->sk_lock.slock, but tipc_poll() calls trace_tipc_sk_poll(..., TIPC_DUMP_ALL, ...) without holding either lock. That lets the poll trace path reach tipc_list_dump() and backlog head/tail dumping while another context dequeues and frees an skb, leaving the trace helper dereferencing a stale queue entry. Stop the unlocked poll trace site from requesting queue dumps. Other queue dump trace callsites keep their existing output under the locking they already provide, while poll still emits the event itself without walking live queue members from an unlocked context. Fixes: b4b9771bcbbd ("tipc: enable tracepoints in tipc") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/f8119abd5e5ecc400597de667ae9d39656de56d0.1784794294.git.zihanx@nebusec.ai Signed-off-by: Jakub Kicinski --- net/tipc/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 185c24003b82..d5d70eb230b5 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -796,7 +796,7 @@ static __poll_t tipc_poll(struct file *file, struct socket *sock, __poll_t revents = 0; sock_poll_wait(file, sock, wait); - trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_ALL, " "); + trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_NONE, " "); if (sk->sk_shutdown & RCV_SHUTDOWN) revents |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; -- cgit v1.2.3 From 6aea62e433fe1b586202a5fee8b5807ce635e1d7 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Fri, 24 Jul 2026 00:48:52 +0800 Subject: net: ipv6: clear suppressed fib6 rule result fib6_rule_suppress() drops a suppressed route with ip6_rt_put_flags(), but leaves res->rt6 pointing at the released rt6_info. If no later rule supplies a replacement, fib6_rule_lookup() still sees res.rt6 and returns that stale dst to its caller. A suppressing rule can therefore leak a released route back to rt6_lookup(), and the next put hits rcuref_put_slowpath() from dst_release(). Clear res->rt6 when suppressing the route so suppressed lookups fall through to the null dst instead of reusing the released one. Fixes: cdef485217d3 ("ipv6: fix memory leak in fib6_rule_suppress") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/4b8acb7787d54e440155585dd32ebdf0bef7d122.1784710966.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/ipv6/fib6_rules.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index e1b2b4fa6e18..89ee3c969ca7 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -308,6 +308,7 @@ INDIRECT_CALLABLE_SCOPE bool fib6_rule_suppress(struct fib_rule *rule, suppress_route: ip6_rt_put_flags(rt, flags); + res->rt6 = NULL; return true; } -- cgit v1.2.3 From a39789f211b8a4125f0c70e05b30cf715f4f187d Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Fri, 24 Jul 2026 00:52:48 +0800 Subject: net: bridge: stop fast-leave after deleting a port group br_multicast_leave_group() iterates mp->ports with pp = &p->next in its fast-leave path. After br_multicast_del_pg() removes p, continuing the loop advances pp through the deleted entry. If multicast-to-unicast was enabled, the bridge can hold multiple port groups for the same port and group with different source MAC addresses. Once multicast-to-unicast is disabled, br_port_group_equal() matches those entries by port only. A fast leave can then delete one entry and continue from its stale next pointer, leaving mp->ports pointing at a deleted port group. Fast leave only needs to remove one matching port group. Break after br_multicast_del_pg() so the loop stops before dereferencing the removed entry. Fixes: 6db6f0eae605 ("bridge: multicast to unicast") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/1cf0898872ef7c72d5f4c0304414a192c6dac591.1784707712.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/bridge/br_multicast.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 6b3ac473fd22..00aa9b2879d6 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -3687,6 +3687,7 @@ br_multicast_leave_group(struct net_bridge_mcast *brmctx, p->flags |= MDB_PG_FLAGS_FAST_LEAVE; br_multicast_del_pg(mp, p, pp); + break; } goto out; } -- cgit v1.2.3 From 9d8da8e0a9bce4a340af60dd0446bc7eb8d07587 Mon Sep 17 00:00:00 2001 From: Yuxiang Yang Date: Thu, 23 Jul 2026 22:56:23 +0000 Subject: sctp: reject stale cookies with mismatched verification tags sctp_unpack_cookie() skips cookie expiration checks whenever an association already exists. This is broader than the exception in RFC 9260 Section 5.2.4. For an existing association, Section 5.2.4 permits an expired State Cookie only when both Verification Tags in the cookie match the current association. Otherwise, the packet SHOULD be discarded and a Stale Cookie ERROR MUST be sent. The broad check lets an expired Action A restart cookie reach sctp_sf_do_dupcook_a(). In a runtime test with the default 60 second cookie lifetime, replaying such a cookie after 65 seconds returned a COOKIE-ACK and restarted the association. Check cookie expiration unless both Verification Tags match. This preserves the Action D exception for a lost COOKIE ACK while rejecting expired cookies in all other cases. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Yuxiang Yang Acked-by: Xin Long Link: https://patch.msgid.link/20260723225623.2658868-1-yangyx22@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- net/sctp/sm_make_chunk.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index c02809264075..a1c0334a1038 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1802,9 +1802,9 @@ struct sctp_association *sctp_unpack_cookie( goto fail; } - /* Check to see if the cookie is stale. If there is already - * an association, there is no need to check cookie's expiration - * for init collision case of lost COOKIE ACK. + /* Check to see if the cookie is stale. RFC 9260 Section 5.2.4 + * exempts an expired cookie only when both Verification Tags match + * the current association. * If skb has been timestamped, then use the stamp, otherwise * use current time. This introduces a small possibility that * a cookie may be considered expired, but this would only slow @@ -1815,7 +1815,10 @@ struct sctp_association *sctp_unpack_cookie( else kt = ktime_get_real(); - if (!asoc && ktime_before(bear_cookie->expiration, kt)) { + if ((!asoc || + asoc->c.my_vtag != bear_cookie->my_vtag || + asoc->c.peer_vtag != bear_cookie->peer_vtag) && + ktime_before(bear_cookie->expiration, kt)) { suseconds_t usecs = ktime_to_us(ktime_sub(kt, bear_cookie->expiration)); __be32 n = htonl(usecs); -- cgit v1.2.3 From 9388e7c820e33d3a7dfde9a9c16ad4ac60f29d37 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Sat, 4 Jul 2026 13:27:14 +0800 Subject: smb: client: free partially allocated transform folio queue netfs_alloc_folioq_buffer() may leave a partially allocated folio queue attached to the caller's buffer pointer when it returns an error. smb3_init_transform_rq() stores the buffer in the request only after allocation succeeds, so the common error path cannot free a partial allocation. Store the buffer pointer before checking the return value so err_free releases it. Signed-off-by: Yichong Chen Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index cbd51a08e97e..192649fec25d 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4745,10 +4745,10 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, size_t cur_size = 0; rc = netfs_alloc_folioq_buffer(NULL, &buffer, &cur_size, size, GFP_NOFS); + new->rq_buffer = buffer; if (rc < 0) goto err_free; - new->rq_buffer = buffer; iov_iter_folio_queue(&new->rq_iter, ITER_SOURCE, buffer, 0, 0, size); -- cgit v1.2.3 From bd0e9289e2642f6a5c54faad304ce0f41e926d22 Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Sat, 25 Jul 2026 03:21:06 +0000 Subject: sctp: prevent peer transport count overflow sctp_assoc_add_peer() increments the association's 16-bit transport_count for every new unique peer. Adding the 65,536th transport wraps the count to zero. SCTP sock_diag uses transport_count to reserve the INET_DIAG_PEERS payload, then copies one sockaddr_storage for every entry in transport_addr_list. After the wrap, a diagnostic dump reserves an empty payload and writes 8 MiB of peer addresses past the skb tail. Reject a new unique peer when transport_count has reached U16_MAX. Perform the check after the existing-peer lookup so a duplicate address continues to return its existing transport at the limit. Fixes: 8f840e47f190 ("sctp: add the sctp_diag.c file") Cc: stable@vger.kernel.org Signed-off-by: Asim Viladi Oglu Manizada Acked-by: Xin Long Link: https://patch.msgid.link/20260725032053.521705-1-manizada@pm.me Signed-off-by: Jakub Kicinski --- net/sctp/associola.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 62d3cc155809..b6ac0966420a 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -614,6 +614,9 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, return peer; } + if (asoc->peer.transport_count == U16_MAX) + return NULL; + peer = sctp_transport_new(asoc->base.net, addr, gfp); if (!peer) return NULL; -- cgit v1.2.3 From 5546da86894d5906f131b05890705a7abf949d84 Mon Sep 17 00:00:00 2001 From: David Corvaglia Date: Sun, 26 Jul 2026 06:26:05 +0000 Subject: net: bridge: mrp: fix Option TLV length in MRP_Test frames oui is a pointer, so sizeof(oui) is the pointer size. The MRA Option TLV thus advertises a wrong length (15 vs 10 on x86_64), causing misparsing of the frame on peers. Fix is to replace with sizeof(*oui). Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") Signed-off-by: David Corvaglia Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260726062605.2746-1-david@corvaglia.dev Signed-off-by: Jakub Kicinski --- net/bridge/br_mrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index 3f7126a7d720..179d2470b724 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, struct br_mrp_oui_hdr *oui = NULL; u8 length; - length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + + length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(*oui) + MRP_OPT_PADDING; br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); -- cgit v1.2.3 From f47064c970d3e920a27435454c02df310695f6e1 Mon Sep 17 00:00:00 2001 From: Surendra Singh Chouhan Date: Mon, 27 Jul 2026 11:08:04 +0530 Subject: ASoC: sophgo: return 1 on volume change in cv1800b_adc_volume_set() cv1800b_adc_volume_set() serves as the .put callback for the "Internal I2S Capture Volume" control. ALSA mixer control callbacks must return 1 when the register value is modified, 0 if unchanged, or a negative error code on failure. Returning 0 unconditionally causes ALSA core to assume the value was unchanged, suppressing SNDRV_CTL_EVENT_MASK_VALUE change notifications to userspace sound servers (e.g. PipeWire/PulseAudio). Fix this by comparing the new register value with the existing register value. If unchanged, return 0; otherwise, write the updated value and return 1. Fixes: 4cf8752a03e6 ("ASoC: sophgo: add CV1800B internal ADC codec driver") Signed-off-by: Surendra Singh Chouhan Link: https://patch.msgid.link/20260727053804.25599-1-kr494167@gmail.com Signed-off-by: Mark Brown --- sound/soc/sophgo/cv1800b-sound-adc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c index b66761156b99..bd93e261bdd1 100644 --- a/sound/soc/sophgo/cv1800b-sound-adc.c +++ b/sound/soc/sophgo/cv1800b-sound-adc.c @@ -251,16 +251,22 @@ static int cv1800b_adc_volume_set(struct snd_kcontrol *kcontrol, u32 v_left = clamp_t(u32, ucontrol->value.integer.value[0], 0, 24); u32 v_right = clamp_t(u32, ucontrol->value.integer.value[1], 0, 24); - u32 val; + u32 val, old_val; val = readl(priv->regs + CV1800B_RXADC_ANA0); + old_val = val; + val = u32_replace_bits(val, cv1800b_gains[v_left], REG_COMB_LEFT_VOLUME); val = u32_replace_bits(val, cv1800b_gains[v_right], REG_COMB_RIGHT_VOLUME); + + if (val == old_val) + return 0; + writel(val, priv->regs + CV1800B_RXADC_ANA0); - return 0; + return 1; } static DECLARE_TLV_DB_SCALE(cv1800b_volume_tlv, 0, 200, 0); -- cgit v1.2.3 From b476724a6d9b2e7db43cb7a59defd396f94cf77c Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Mon, 27 Jul 2026 20:20:35 +0300 Subject: smb: client: simplify cifs_fscache_get_super_cookie() Avoid redundant 'strlen()' and use the convenient 'strreplace()' to simplify 'cifs_fscache_get_super_cookie()'. Signed-off-by: Dmitry Antipov Signed-off-by: Steve French --- fs/smb/client/fscache.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c index 01424a5cdb99..9f66bbcd3801 100644 --- a/fs/smb/client/fscache.c +++ b/fs/smb/client/fscache.c @@ -38,7 +38,6 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) struct TCP_Server_Info *server = tcon->ses->server; struct fscache_volume *vcookie; const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; - size_t slen, i; char *sharename; char *key; int ret = -ENOMEM; @@ -73,10 +72,7 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) return PTR_ERR(sharename); } - slen = strlen(sharename); - for (i = 0; i < slen; i++) - if (sharename[i] == '/') - sharename[i] = ';'; + strreplace(sharename, '/', ';'); key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); if (!key) -- cgit v1.2.3 From 9ad4c2331d2330ab280180a337c7d30656496c6d Mon Sep 17 00:00:00 2001 From: Jackie Dong Date: Mon, 27 Jul 2026 12:14:51 +0800 Subject: ASoC: amd: acp: Add DMI quirk for Lenovo Legion 7 15ASH11 Lenovo Legion 7 15ASH11 with AMD RYZEN AI MAX+ 392 (Strix Halo, ACP 7.0) uses Realtek ALC287 series codec and no any DMIC connected by ACP. All DMICs directly connet with ALC codec. Without this quirk, Input Device of Gnome Sound settings shows Internal Stereo Microphone and Digital Microphone by default. In fact, Digital Microphone of ACP doesn't work due to no connecting with ALC287 codec, the Internal Stereo Microphone as analog device based on snd_hda_intel driver can work well. Add a DMI quirk to override the flag to 0, consistent with the existing entry for the Lenovo Yoga Pro 7 15ASH11. Signed-off-by: Jackie Dong Link: https://patch.msgid.link/20260727041452.16701-1-xy-jackie@139.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 051946562725..88e4230d66c3 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -44,6 +44,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "83W5"), }, }, + { + /* Lenovo Legion 7 15ASH11 (Strix Halo, ACP 7.0) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83V9"), + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), -- cgit v1.2.3 From 0e3ea5445c228048f937ad5a944c27859a78f971 Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Fri, 24 Jul 2026 11:30:35 -0500 Subject: cifs: consolidate time_last_write stamp into _cifsFileInfo_put() The time_last_write stamp was scattered across cifs_close(), smb2_deferred_work_close(), and the three drain functions in misc.c. This missed the case where background I/O holds the final reference after userspace close() returns, and required explicit maintenance at each close-path site. Move the smp_store_release() into _cifsFileInfo_put(), immediately before releasing open_file_lock. This single location covers all close paths unconditionally: normal close, background I/O dropping the final reference, deferred close via timer or external drain. The spinlock's store-release/load-acquire pairing with is_inode_writable() already provides the ordering guarantee documented in is_size_safe_to_change(). Remove the now-redundant stamps from cifs_close(), smb2_deferred_work_close(), and all six stamp sites in the misc.c deferred-close drain functions. Fixes: e8a8d54c2d50 ("cifs: prevent readdir from changing file size due to stale directory metadata") Signed-off-by: Frank Sorenson Reviewed-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/file.c | 37 ++++++++++++++++--------------------- fs/smb/client/misc.c | 51 ++++++--------------------------------------------- 2 files changed, 22 insertions(+), 66 deletions(-) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index b279a44be729..ac89c1ba56b1 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -915,6 +915,14 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, cifs_set_oplock_level(cifsi, 0); } + if (OPEN_FMODE(cifs_file->f_flags) & FMODE_WRITE) { + /* Stamp while open_file_lock is held; covers all close paths + * including background I/O. Pairs with smp_load_acquire() in + * is_size_safe_to_change(). + */ + smp_store_release(&cifsi->time_last_write, jiffies); + } + spin_unlock(&cifsi->open_file_lock); spin_unlock(&tcon->open_file_lock); @@ -1429,15 +1437,6 @@ void smb2_deferred_work_close(struct work_struct *work) cifs_del_deferred_close(cfile); cfile->deferred_close_scheduled = false; spin_unlock(&cinode->deferred_lock); - /* - * Refresh time_last_write immediately before the actual server close - * so the protection window is anchored to the real close time, not - * the earlier userspace close time stored by cifs_close(). - */ - if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&cinode->time_last_write, jiffies); - } _cifsFileInfo_put(cfile, true, false); } @@ -1467,10 +1466,6 @@ int cifs_close(struct inode *inode, struct file *file) if (file->private_data != NULL) { cfile = file->private_data; file->private_data = NULL; - if (file->f_mode & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&cinode->time_last_write, jiffies); - } dclose = kmalloc_obj(struct cifs_deferred_close); if ((cfile->status_file_deleted == false) && (smb2_can_defer_close(inode, dclose))) { @@ -3276,13 +3271,13 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, * No writable handles open. Check whether we are within the attribute * cache validity window of a recent local modification. * - * For the close() path: cifs_close() calls smp_store_release() on - * time_last_write before _cifsFileInfo_put() removes the handle under - * open_file_lock. That spin_unlock() is a store-release that pairs - * with the spin_lock() (load-acquire) in is_inode_writable() above, - * so if is_inode_writable() returned false the smp_load_acquire() - * below is guaranteed to observe any time_last_write update from a - * concurrent close(). + * For the close() path: _cifsFileInfo_put() stamps time_last_write + * (via smp_store_release()) before releasing open_file_lock. That + * spin_unlock() is a store-release that pairs with the spin_lock() + * (load-acquire) in is_inode_writable() above, so if + * is_inode_writable() returned false the smp_load_acquire() below is + * guaranteed to observe any time_last_write update from a concurrent + * close(), covering all close paths including background I/O. * * For the setattr/truncate paths: those callers use smp_store_release() * directly; the smp_load_acquire() below pairs with that store. There @@ -3297,7 +3292,7 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, * jiffies is still close to INITIAL_JIFFIES on 32-bit systems. */ if (from_readdir) { - /* Pairs with smp_store_release() at close and truncate sites. */ + /* Pairs with smp_store_release() in _cifsFileInfo_put() and setattr. */ tlw = smp_load_acquire(&cifsInode->time_last_write); if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax)) return false; diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 6edebc0807ea..46e1382e8e04 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -525,24 +525,11 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) } spin_unlock(&cifs_inode->open_file_lock); - if (failed_cfile) { - if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, - jiffies); - } + if (failed_cfile) _cifsFileInfo_put(failed_cfile, false, false); - } list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - struct cifsFileInfo *cfile = tmp_list->cfile; - - if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, - jiffies); - } - _cifsFileInfo_put(cfile, false, false); + _cifsFileInfo_put(tmp_list->cfile, false, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -576,24 +563,11 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon) } spin_unlock(&tcon->open_file_lock); - if (failed_cfile) { - if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, - jiffies); - } + if (failed_cfile) _cifsFileInfo_put(failed_cfile, true, false); - } list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - struct cifsFileInfo *cfile = tmp_list->cfile; - - if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, - jiffies); - } - _cifsFileInfo_put(cfile, true, false); + _cifsFileInfo_put(tmp_list->cfile, true, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -663,24 +637,11 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, } spin_unlock(&tcon->open_file_lock); - if (failed_cfile) { - if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, - jiffies); - } + if (failed_cfile) _cifsFileInfo_put(failed_cfile, true, false); - } list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - struct cifsFileInfo *cfile = tmp_list->cfile; - - if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, - jiffies); - } - _cifsFileInfo_put(cfile, true, false); + _cifsFileInfo_put(tmp_list->cfile, true, false); list_del(&tmp_list->list); kfree(tmp_list); } -- cgit v1.2.3 From ecababf08905958ba8c125979c4e39fc2f1a8a05 Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Fri, 24 Jul 2026 11:30:36 -0500 Subject: cifs: fix time_last_write stamp placement in setattr/truncate paths cifs_file_set_size() calls cifs_setsize() on success, which calls i_size_write(), updating i_size to the new value. The subsequent check attrs->ia_size != i_size_read() in both cifs_setattr_unix() and cifs_setattr_nounix() therefore always evaluates false after a successful cifs_file_set_size(), making the smp_store_release() of time_last_write dead code. The truncate path was unprotected against stale readdir size updates. Move the stamp to before the cifs_file_set_size() RPC call, guarded by attrs->ia_size != i_size_read() to exclude no-op same-size ftruncate(2) calls from stamping time_last_write unnecessarily. On the error path the stamp remains rather than being restored: restoring a stale snapshot (prev_tlw) could silently erase a concurrent _cifsFileInfo_put() close stamp if that close arrived between the READ_ONCE and the smp_store_release. readdir is suppressed until the stamp expires, which extends beyond one acregmax if the caller retries failed truncations. stat() is unaffected: the cifs_revalidate_dentry_attr() path calls cifs_fattr_to_inode() with from_readdir=false, which bypasses the time_last_write check in is_size_safe_to_change() entirely and always writes the authoritative QUERY_INFO result to i_size. Remove the now-unreachable stamp from the dead block in both functions. Fixes: e8a8d54c2d50 ("cifs: prevent readdir from changing file size due to stale directory metadata") Signed-off-by: Frank Sorenson Reviewed-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/inode.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index b2806371bfde..808085eb0cdc 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3190,6 +3190,17 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) rc = 0; if (attrs->ia_valid & ATTR_SIZE) { + if (attrs->ia_size != i_size_read(inode)) { + /* Stamp before RPC. On failure the stamp remains: restoring a + * stale snapshot could silently erase a concurrent + * _cifsFileInfo_put() close stamp. readdir is suppressed + * until the stamp expires; stat() bypasses this via the + * from_readdir=false path in is_size_safe_to_change() and + * always returns an authoritative QUERY_INFO result. + * Pairs with smp_load_acquire() in is_size_safe_to_change(). + */ + smp_store_release(&cifsInode->time_last_write, jiffies); + } rc = cifs_file_set_size(xid, direntry, full_path, open_file, attrs->ia_size); if (rc != 0) @@ -3279,8 +3290,6 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if ((attrs->ia_valid & ATTR_SIZE) && attrs->ia_size != i_size_read(inode)) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&cifsInode->time_last_write, jiffies); truncate_setsize(inode, attrs->ia_size); netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true); fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); @@ -3370,6 +3379,17 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) } if (attrs->ia_valid & ATTR_SIZE) { + if (attrs->ia_size != i_size_read(inode)) { + /* Stamp before RPC. On failure the stamp remains: restoring a + * stale snapshot could silently erase a concurrent + * _cifsFileInfo_put() close stamp. readdir is suppressed + * until the stamp expires; stat() bypasses this via the + * from_readdir=false path in is_size_safe_to_change() and + * always returns an authoritative QUERY_INFO result. + * Pairs with smp_load_acquire() in is_size_safe_to_change(). + */ + smp_store_release(&cifsInode->time_last_write, jiffies); + } rc = cifs_file_set_size(xid, direntry, full_path, cfile, attrs->ia_size); if (rc != 0) @@ -3482,8 +3502,6 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if ((attrs->ia_valid & ATTR_SIZE) && attrs->ia_size != i_size_read(inode)) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&cifsInode->time_last_write, jiffies); truncate_setsize(inode, attrs->ia_size); netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true); fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); -- cgit v1.2.3 From 22666ba1420164753d7b0f5a841986b25ace5435 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 23 Jul 2026 17:26:37 +0800 Subject: forcedeth: fix UAF of txrx_stats in nv_remove nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, and nv_close()/drain may still access txrx_stats, leading to a use-after-free. Free the stats only after unregister_netdev(). Fixes: f4b633b911fd ("forcedeth: use per cpu to collect xmit/recv statistics") Signed-off-by: Chenguang Zhao Reviewed-by: Vadim Fedorenko Reviewed-by: Zhu Yanjun Link: https://patch.msgid.link/20260723092637.2135095-1-chenguang.zhao@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 5b0435d7bc39..58d3e55def48 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -6187,10 +6187,10 @@ static void nv_remove(struct pci_dev *pci_dev) struct net_device *dev = pci_get_drvdata(pci_dev); struct fe_priv *np = netdev_priv(dev); - free_percpu(np->txrx_stats); - unregister_netdev(dev); + free_percpu(np->txrx_stats); + nv_restore_mac_addr(pci_dev); /* restore any phy related changes */ -- cgit v1.2.3 From 625a2c02a1c04571232a746fe188b4d9a8d63edd Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:17 -0300 Subject: hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors During adt7470_read_temperatures(), the driver temporarily switches the PWM channels to manual mode, performs the temperature collection, and then restores the original configuration registers. However, if an I2C transaction fails at any point after entering manual mode, the function aborts and returns immediately. This leaves the configuration registers un-restored, permanently trapping the fans in manual mode. Introduce a recovery path to ensure that the original PWM configuration registers are always restored, even when intermediate I2C operations fail. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 664349756dc2..481d51617f4b 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in /* Probe for temperature sensors. Assumes lock is held */ static int adt7470_read_temperatures(struct adt7470_data *data) { - unsigned long res; + struct device *dev = regmap_get_device(data->regmap); + u8 pwm[ADT7470_FAN_COUNT]; unsigned int pwm_cfg[2]; - int err; + unsigned long res; + int err, err2; int i; - u8 pwm[ADT7470_FAN_COUNT]; /* save pwm[1-4] config register */ err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); @@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), ADT7470_PWM_AUTO_MASK, 0); if (err < 0) - return err; + goto out_restore; /* write pwm control to whatever it was */ err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], ADT7470_PWM_COUNT); if (err < 0) - return err; + goto out_restore; /* start reading temperature sensors */ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); if (err < 0) - return err; + goto out_restore; /* Delay is 200ms * number of temp sensors. */ res = msleep_interruptible((data->num_temp_sensors >= 0 ? @@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ADT7470_T05_STB_MASK, 0); if (err < 0) - return err; + goto out_restore; +out_restore: /* restore pwm[1-4] config registers */ - err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); - if (err < 0) - return err; - err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); + if (err2 < 0) { + dev_warn_ratelimited(dev, + "failed to restore PWM{1,2} config (%d)\n", + err2); + + if (!err) + err = err2; + } + + err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + if (err2 < 0) { + dev_warn_ratelimited(dev, + "failed to restore PWM{3,4} config (%d)\n", + err2); + + if (!err) + err = err2; + } + if (err < 0) return err; -- cgit v1.2.3 From 05270bd38d9bf88a2f4c212246a8fa29f4032078 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:18 -0300 Subject: hwmon: (adt7470) Fix cache updated before hardware write on I2C error adt7470_temp_write() and adt7470_pwm_write() update the driver's cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing the corresponding regmap_write(), and never check whether the write succeeded before committing that update. If the I2C transaction fails, the function correctly propagates the error to the caller, but the cache silently keeps the new value, which was never actually applied to the hardware. Subsequent reads then report a value that does not match the device state. Reorder both write paths to update the cache only after a successful regmap_write(), so the cache always reflects what was actually written to the hardware. Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 481d51617f4b..62ec68ea0a40 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va switch (attr) { case hwmon_temp_min: mutex_lock(&data->lock); - data->temp_min[channel] = val; err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); + if (!err) + data->temp_min[channel] = val; mutex_unlock(&data->lock); break; case hwmon_temp_max: mutex_lock(&data->lock); - data->temp_max[channel] = val; err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); + if (!err) + data->temp_max[channel] = val; mutex_unlock(&data->lock); break; default: @@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val case hwmon_pwm_input: val = clamp_val(val, 0, 255); mutex_lock(&data->lock); - data->pwm[channel] = val; err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), - data->pwm[channel]); + val); + if (!err) + data->pwm[channel] = val; mutex_unlock(&data->lock); break; case hwmon_pwm_enable: @@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val val--; mutex_lock(&data->lock); - data->pwm_automatic[channel] = val; err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), pwm_auto_reg_mask, val ? pwm_auto_reg_mask : 0); + if (!err) + data->pwm_automatic[channel] = val; mutex_unlock(&data->lock); break; case hwmon_pwm_freq: -- cgit v1.2.3 From cb0b7f9c43b0abbd422a7e4c2c85e91db429207c Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:19 -0300 Subject: hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread When userspace configures 'auto_update_interval' to 0 via sysfs, the background kthread executes schedule_timeout_interruptible(0), which returns immediately. If 'num_temp_sensors' is concurrently or previously set to 0, the msleep_interruptible() delay inside adt7470_read_temperatures() also becomes 0. This combination forces the background thread into a tight, unbounded busy-loop, hogging the CPU and flooding the I2C bus with a continuous stream of transactions. Fix this vulnerability by raising the lower limit of the clamp_val in auto_update_interval_store() from 0 to 500 milliseconds. This guarantees a reasonable minimum sleep window between sensor updates, protecting the system from intentional or accidental I2C bus denial of service. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 62ec68ea0a40..0b19b0925d1c 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = clamp_val(temp, 0, 60000); + temp = clamp_val(temp, 500, 60000); mutex_lock(&data->lock); data->auto_update_interval = temp; -- cgit v1.2.3 From 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:21 -0300 Subject: hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() During the conversion the alarm callback started interpreting the channel index as an alarm bitmask, resulting in incorrect alarm reporting. Compute the proper alarm bit instead. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 0b19b0925d1c..428bd1d91e70 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ALARM2(x) ((x) << 8) +/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and + * 0x72, or BIT(0)..BIT(6) of data->alarm. + * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and + * 0x73, or BIT(8)..BIT(10) of data->alarm. + */ +#define TEMP_ALARM_BIT(ch) ({ \ + typeof(ch) _ch = (ch); \ + (1 << (_ch < 7 ? _ch : _ch + 1)); \ +}) + +/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in + * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. + */ +#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) + #define ADT7470_VENDOR 0x41 #define ADT7470_DEVICE 0x70 /* datasheet only mentions a revision 2 */ @@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va *val = 1000 * data->temp_max[channel]; break; case hwmon_temp_alarm: - *val = !!(data->alarm & channel); + *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); break; default: return -EOPNOTSUPP; @@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val *val = 0; break; case hwmon_fan_alarm: - *val = !!(data->alarm & (1 << (12 + channel))); + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); break; default: return -EOPNOTSUPP; -- cgit v1.2.3 From a3850231521b06bbbb18c8ebea100320c14a08be Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:20 -0300 Subject: hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are currently defined with swapped bit values. According to Table 22 of the ADT7470 datasheet, the Fan Control Mode Configuration for register 0x69 follows the exact same bit position layout as register 0x68: - 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 - 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 - 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 - 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. This typo did not cause any functional bugs because these specific macros are never referenced in the driver code. Instead, the driver correctly applies the configuration by relying on the modulo parity of the channel index (e.g., `channel % 2`) to selectively apply either ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). Since the bit layout is identical between the two configuration registers, the hardware is currently configured correctly. Fix the macro definitions to reflect the datasheet accurately and prevent future bugs or confusion during code review and refactoring. As this is a purely cosmetic fix with no functional impact, a backport to stable kernels is not necessary. Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 428bd1d91e70..c6fc7d38d698 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_PWM1_AUTO_MASK 0x80 #define ADT7470_PWM_AUTO_MASK 0xC0 #define ADT7470_REG_PWM34_CFG 0x69 -#define ADT7470_PWM3_AUTO_MASK 0x40 -#define ADT7470_PWM4_AUTO_MASK 0x80 +#define ADT7470_PWM4_AUTO_MASK 0x40 +#define ADT7470_PWM3_AUTO_MASK 0x80 #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E -- cgit v1.2.3 From 60677cd4c28f44d5b307d3029dccece38fcce90f Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:22 -0300 Subject: hwmon: (adt7470) Use cached PWM frequency value adt7470_pwm_read() currently ignores failures returned by pwm1_freq_get(). If the register read fails, the negative error code is returned through *val while the function itself reports success, potentially exposing a negative PWM frequency through sysfs. Fix this by using the cached PWM frequency maintained by the driver, eliminating the register access from the read path. Apart from the corrected error propagation and using the cached value, no functional change is intended. Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index c6fc7d38d698..1fbca4869b7b 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -182,6 +182,7 @@ struct adt7470_data { u8 pwm_min[ADT7470_PWM_COUNT]; s8 pwm_tmin[ADT7470_PWM_COUNT]; u8 pwm_auto_temp[ADT7470_PWM_COUNT]; + u32 pwm_freq; struct task_struct *auto_update; unsigned int auto_update_interval; @@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, } /* These are the valid PWM frequencies to the nearest Hz */ -static const int adt7470_freq_map[] = { +static const u32 adt7470_freq_map[] = { 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 }; @@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val *val = 1 + data->pwm_automatic[channel]; break; case hwmon_pwm_freq: - *val = pwm1_freq_get(dev); + *val = data->pwm_freq; break; default: return -EOPNOTSUPP; @@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq) { struct adt7470_data *data = dev_get_drvdata(dev); unsigned int low_freq = ADT7470_CFG_LF; + u32 closest_freq; int index; int err; /* Round the user value given to the closest available frequency */ index = find_closest(freq, adt7470_freq_map, ARRAY_SIZE(adt7470_freq_map)); + closest_freq = adt7470_freq_map[index]; if (index >= 8) { index -= 8; @@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq) err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, ADT7470_FREQ_MASK, index << ADT7470_FREQ_SHIFT); + if (err < 0) + goto out; + + data->pwm_freq = closest_freq; out: mutex_unlock(&data->lock); @@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client) struct device *dev = &client->dev; struct adt7470_data *data; struct device *hwmon_dev; + int freq_val; int err; data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); @@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client) if (err < 0) return err; + freq_val = pwm1_freq_get(dev); + if (freq_val <= 0) { + err = freq_val < 0 ? freq_val : -EINVAL; + return err; + } + + data->pwm_freq = (u32)freq_val; + /* Register sysfs hooks */ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &adt7470_chip_info, -- cgit v1.2.3 From 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:23 -0300 Subject: hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read If the fan data becomes 0 between the FAN_DATA_VALID() check and the FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash due to a race with a concurrent update of the cached fan value. Fix a TOCTOU issue by reading fan data once. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 1fbca4869b7b..772d2a409bb5 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) { struct adt7470_data *data = adt7470_update_device(dev); + u16 fan_data; if (IS_ERR(data)) return PTR_ERR(data); switch (attr) { case hwmon_fan_input: - if (FAN_DATA_VALID(data->fan[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan[channel]); break; case hwmon_fan_min: - if (FAN_DATA_VALID(data->fan_min[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan_min[channel]); break; case hwmon_fan_max: - if (FAN_DATA_VALID(data->fan_max[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan_max[channel]); break; case hwmon_fan_alarm: *val = !!(data->alarm & FAN_ALARM_BIT(channel)); - break; + return 0; default: return -EOPNOTSUPP; } + if (FAN_DATA_VALID(fan_data)) + *val = FAN_PERIOD_TO_RPM(fan_data); + else + *val = 0; + return 0; } -- cgit v1.2.3 From 92413f439d1ec5e55b73ede8d66a7b971cbd1ced Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:24 -0300 Subject: hwmon: (adt7470) Fix PWM auto temp state array and bounds check In pwm_auto_temp_store(), the parsed user input was missing bounds checks, allowing values > 0xF to overflow into the adjacent channel's bits. Furthermore, the value was being incorrectly written to the pwm_automatic state array instead of pwm_auto_temp. Fix this by rejecting values > 0xF with -EINVAL, and assigning the value to the correct array only after a successful I2C write. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 772d2a409bb5..c45b984c02e6 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, if (temp < 0) return temp; + if (temp > 0xF) + return -EINVAL; + mutex_lock(&data->lock); - data->pwm_automatic[attr->index] = temp; if (!(attr->index % 2)) { mask = 0xF0; @@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, } err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); + if (!err) + data->pwm_auto_temp[attr->index] = temp; + mutex_unlock(&data->lock); return err < 0 ? err : count; -- cgit v1.2.3 From d211028bac1bd0fff0026bfa2a8328e5b78cd0e6 Mon Sep 17 00:00:00 2001 From: Aswin Karuvally Date: Thu, 23 Jul 2026 16:00:50 +0200 Subject: s390/qeth: Check CAP_NET_ADMIN for private ioctls Gate the SIOCDEVPRIVATE ioctl commands SIOC_QETH_ADP_SET_SNMP_CONTROL, SIOC_QETH_GET_CARD_TYPE and SIOC_QETH_QUERY_OAT with CAP_NET_ADMIN capable check to ensure unprivileged users cannot invoke them. Fixes: 18787eeebd71 ("qeth: use ndo_siocdevprivate") Cc: stable@vger.kernel.org Suggested-by: Christian Borntraeger Reviewed-by: Christian Borntraeger Reviewed-by: Alexandra Winter Signed-off-by: Aswin Karuvally Link: https://patch.msgid.link/20260723140050.762991-1-aswin@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/s390/net/qeth_core_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 20fb0d2e02a9..f18eed9df3c7 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6525,6 +6525,9 @@ int qeth_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *d struct qeth_card *card = dev->ml_priv; int rc = 0; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + switch (cmd) { case SIOC_QETH_ADP_SET_SNMP_CONTROL: rc = qeth_snmp_command(card, data); -- cgit v1.2.3 From 6fb7b769d6ed6d1d2e02af4a80e57a2477f35086 Mon Sep 17 00:00:00 2001 From: Yun Lu Date: Tue, 21 Jul 2026 10:38:36 +0800 Subject: rtase: fix double free of multi-frag skb on DMA map failure In rtase_start_xmit(), when the head buffer DMA mapping fails after rtase_xmit_frags() has mapped all fragments, the error path clears the fragment descriptors with rtase_tx_clear_range(), which frees the skb through the last-frag slot and accounts tx_dropped. Control then falls through to the common error label, which frees the same skb a second time and counts it again. Return right after clearing the fragments when the skb owns frags; the no-frag case still drops through and frees the head skb once. Fixes: d6e882b89fdf ("rtase: Implement .ndo_start_xmit function") Signed-off-by: Yun Lu Reviewed-by: Jacob Keller Reviewed-by: Justin Lai Link: https://patch.msgid.link/20260721023836.6691-1-luyun_611@163.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/realtek/rtase/rtase_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index 4168ad9e48ea..e3cd4f7c1380 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -1623,6 +1623,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, err_dma_1: ring->skbuff[entry] = NULL; rtase_tx_clear_range(ring, ring->cur_idx + 1, frags); + if (frags) + /* the frags were cleared above, along with the skb */ + return NETDEV_TX_OK; err_dma_0: tp->stats.tx_dropped++; -- cgit v1.2.3 From 97ac08560d236ca17f6606d9e671118e5eae5721 Mon Sep 17 00:00:00 2001 From: Eric Joyner Date: Wed, 22 Jul 2026 21:13:42 -0700 Subject: ethtool: Embed FEC hist ranges as buffer in struct When a driver's .get_fec_stats() handler is called and the driver supports FEC histogram stats, the driver supplies the histogram bin ranges via a pointer. This pointer is assigned while under the netdev ops lock in fec_prepare_data(), but the actual data is only read after the lock is released; so this allows the driver to change the ranges (e.g. from another .get_fec_stats() call) while the current call chain is reading them in fec_fill_reply(). Fix this by adding an ethtool core-owned buffer, ranges_buf, to struct ethtool_fec_hist. Drivers whose ranges are built dynamically (currently just mlx5) fill ranges_buf and then point the existing ranges pointer at it, giving ethtool a consistent copy that stays valid after the netdev ops lock is dropped and later in fec_fill_reply(). Drivers whose ranges are compile-time constants (bnxt, netdevsim) are unaffected by the potential race and keep setting the existing ranges pointer to their constant array, without making copies. Fixes: cc2f08129925 ("ethtool: add FEC bins histogram report") Signed-off-by: Eric Joyner Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260723041342.39238-1-eric.joyner@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 1 - drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 7 ------- drivers/net/ethernet/mellanox/mlx5/core/en_stats.c | 19 +++++++++---------- include/linux/ethtool.h | 1 + 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index d507289096c2..6867a5aed42c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -984,7 +984,6 @@ struct mlx5e_priv { struct mlx5e_mqprio_rl *mqprio_rl; struct dentry *dfs_root; struct mlx5_devcom_comp_dev *devcom; - struct ethtool_fec_hist_range *fec_ranges; }; static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index c1acb9012d3f..7d47a1da8b6b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -6415,14 +6415,8 @@ int mlx5e_priv_init(struct mlx5e_priv *priv, if (!priv->channel_stats) goto err_free_tx_rates; - priv->fec_ranges = kzalloc_objs(*priv->fec_ranges, ETHTOOL_FEC_HIST_MAX); - if (!priv->fec_ranges) - goto err_free_channel_stats; - return 0; -err_free_channel_stats: - kfree(priv->channel_stats); err_free_tx_rates: kfree(priv->tx_rates); err_free_txq2sq_stats: @@ -6447,7 +6441,6 @@ void mlx5e_priv_cleanup(struct mlx5e_priv *priv) if (!priv->mdev) return; - kfree(priv->fec_ranges); for (i = 0; i < priv->stats_nch; i++) kvfree(priv->channel_stats[i]); kfree(priv->channel_stats); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index de38b60806c2..e7e6db7f6bf1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -1550,7 +1550,7 @@ static bool fec_rs_validate_hist_type(int mode, int hist_type) static u8 fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode, - const struct ethtool_fec_hist_range **ranges) + struct ethtool_fec_hist_range *ranges) { struct mlx5_core_dev *mdev = priv->mdev; u32 out[MLX5_ST_SZ_DW(pphcr_reg)] = {0}; @@ -1558,8 +1558,6 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode, int sz = MLX5_ST_SZ_BYTES(pphcr_reg); u8 hist_type, num_of_bins; - memset(priv->fec_ranges, 0, - ETHTOOL_FEC_HIST_MAX * sizeof(*priv->fec_ranges)); MLX5_SET(pphcr_reg, in, local_port, 1); if (mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPHCR, 0, 0)) return 0; @@ -1575,12 +1573,11 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode, for (int i = 0; i < num_of_bins; i++) { void *bin_range = MLX5_ADDR_OF(pphcr_reg, out, bin_range[i]); - priv->fec_ranges[i].high = MLX5_GET(bin_range_layout, bin_range, - high_val); - priv->fec_ranges[i].low = MLX5_GET(bin_range_layout, bin_range, - low_val); + ranges[i].high = MLX5_GET(bin_range_layout, bin_range, + high_val); + ranges[i].low = MLX5_GET(bin_range_layout, bin_range, + low_val); } - *ranges = priv->fec_ranges; return num_of_bins; } @@ -1622,10 +1619,12 @@ static void fec_set_histograms_stats(struct mlx5e_priv *priv, int mode, case MLX5E_FEC_LLRS_272_257_1: case MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD: num_of_bins = - fec_rs_histogram_fill_ranges(priv, mode, &hist->ranges); - if (num_of_bins) + fec_rs_histogram_fill_ranges(priv, mode, hist->ranges_buf); + if (num_of_bins) { + hist->ranges = hist->ranges_buf; return fec_rs_histogram_fill_stats(priv, num_of_bins, hist); + } break; default: return; diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 5d491a98265e..12683b5d125e 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -562,6 +562,7 @@ struct ethtool_fec_hist { u64 per_lane[ETHTOOL_MAX_LANES]; } values[ETHTOOL_FEC_HIST_MAX]; const struct ethtool_fec_hist_range *ranges; + struct ethtool_fec_hist_range ranges_buf[ETHTOOL_FEC_HIST_MAX]; }; /** * struct ethtool_fec_stats - statistics for IEEE 802.3 FEC -- cgit v1.2.3 From fa724e235cfdb0fb0bb427d0f9dfe864ae27403e Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Sat, 25 Jul 2026 21:04:44 +0000 Subject: cifs: add fscache_resize_cookie() to cifs_setsize() Several code paths update the VFS inode size by calling netfs_resize_file() and cifs_setsize(), but omit the corresponding fscache_resize_cookie() call, leaving the fscache cookie out of sync with the actual file size: - cifs_file_set_size() in inode.c: server-side truncation via setattr - cifs_do_truncate() in file.c: truncates to zero on O_TRUNC open - smb2_duplicate_extents() in smb2ops.c: file clone extending EOF - smb3_simple_falloc() in smb2ops.c: two branches that extend EOF via write-range and SMB2_set_eof respectively Since every caller of cifs_setsize() must resize the fscache cookie, add the call to cifs_setsize() itself, consistent with how truncate_pagecache() is already consolidated there. Fixes: 70431bfd825d ("cifs: Support fscache indexing rewrite") Fixes: 93a43155127f ("cifs: Fix missing set of remote_i_size") Fixes: 110fee6b9bb5 ("smb: client: fix missing timestamp updates with O_TRUNC") Fixes: 7a06d3b816d7 ("smb/client: emulate small EOF-extending mode 0 fallocate ranges") Cc: stable@vger.kernel.org Cc: David Howells Cc: Paulo Alcantara Cc: Huiwen He Signed-off-by: Frank Sorenson Reviewed-by: Paulo Alcantara Signed-off-by: Steve French --- fs/smb/client/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 808085eb0cdc..0afff761aab9 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3059,6 +3059,7 @@ void cifs_setsize(struct inode *inode, loff_t offset) inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); truncate_pagecache(inode, offset); netfs_wait_for_outstanding_io(inode); + fscache_resize_cookie(cifs_inode_cookie(inode), offset); } int cifs_file_set_size(const unsigned int xid, struct dentry *dentry, -- cgit v1.2.3 From aec49a34d928a0ecdea85bd9f6b4114b668b68b8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 24 Jul 2026 18:52:09 -0700 Subject: ata: libata: avoid kernel-doc warnings Modify comments to prevent kernel-doc warnings: - use "/*" for a non-kernel-doc comment - add a Returns: section for ata_id_major_version() Warning: include/linux/ata.h:770 Cannot find identifier on line: * Warning: include/linux/ata.h:782 function parameter 'id' not described in 'ata_id_sct_data_tables' Warning: include/linux/ata.h:782 expecting prototype for Word(). Prototype was for ata_id_sct_data_tables() instead Warning: include/linux/ata.h:820 No description found for return value of 'ata_id_major_version' Signed-off-by: Randy Dunlap Signed-off-by: Damien Le Moal --- include/linux/ata.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/ata.h b/include/linux/ata.h index 8fd48bcb2a46..7daad4cad985 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -762,8 +762,7 @@ static inline bool ata_id_sense_reporting_enabled(const u16 *id) return id[ATA_ID_COMMAND_SET_4] & BIT(6); } -/** - * +/* * Word: 206 - SCT Command Transport * 15:12 - Vendor Specific * 11:6 - Reserved @@ -810,8 +809,9 @@ static inline bool ata_id_sct_supported(const u16 *id) * * The practical impact of this is that ata_id_major_version cannot * reliably report on drives below ATA3. + * + * Returns: major version of ATA drive level or %0 if unknown */ - static inline unsigned int ata_id_major_version(const u16 *id) { unsigned int mver; -- cgit v1.2.3 From 1e024d2b41ee32bc06818f7f09a3562c58842cf9 Mon Sep 17 00:00:00 2001 From: Matt Vollrath Date: Fri, 24 Jul 2026 03:39:42 -0400 Subject: ata: libata-eh: Increase STANDBY IMMEDIATE timeout Correct a previous change (see Fixes) which reduced the standby timeout from 30 to 5 seconds. Increase it to 15 seconds. I was troubleshooting an error spotted during system suspend: [ 1217.152867] ata1.00: Entering standby power mode [ 1222.322948] ata1.00: qc timeout after 5000 msecs (cmd 0xe0) [ 1222.324010] ata1.00: STANDBY IMMEDIATE failed (err_mask=0x4) This drive is a Samsung 870 EVO SSD in good SMART standing, and I wasn't aware of any reason it should be taking so long to standby. The issue is intermittent, but I observed it sometimes taking 7 seconds to manually standby. I assume this was interruption of background maintenance after a power outage. As a desktop user, I would prefer to wait the extra 2 seconds at suspend to let the drive finish its business rather than drop the rails from under it. The change from 30 to 5 seconds was implicit when switching suspend from START STOP UNIT to an internal command with no timeout table entry. No reason was stated for the change. Fixes: aa3998dbeb3a ("ata: libata-scsi: Disable scsi device manage_system_start_stop") Cc: stable@vger.kernel.org Signed-off-by: Matt Vollrath Assisted-by: Claude:claude-5-fable Signed-off-by: Damien Le Moal --- drivers/ata/libata-eh.c | 8 ++++++++ include/linux/libata.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 05df7ea6954a..46947ed0c657 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -106,6 +106,12 @@ static const unsigned int ata_eh_flush_timeouts[] = { UINT_MAX, }; +static const unsigned int ata_eh_standby_timeouts[] = { + 15000, /* Some drives may be slow to standby */ + /* but don't hold up a suspend too long waiting for them */ + UINT_MAX, +}; + static const unsigned int ata_eh_other_timeouts[] = { 5000, /* same rationale as identify timeout */ 10000, /* ditto */ @@ -147,6 +153,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = { .timeouts = ata_eh_other_timeouts, }, { .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT), .timeouts = ata_eh_flush_timeouts }, + { .commands = CMDS(ATA_CMD_STANDBYNOW1), + .timeouts = ata_eh_standby_timeouts }, { .commands = CMDS(ATA_CMD_VERIFY), .timeouts = ata_eh_reset_timeouts }, }; diff --git a/include/linux/libata.h b/include/linux/libata.h index 96e626d6a7ca..6c0d385b2cde 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -425,7 +425,7 @@ enum { /* This should match the actual table size of * ata_eh_cmd_timeout_table in libata-eh.c. */ - ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 8, + ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 9, /* User visible DMA mask for DMA control. DO NOT renumber. */ ATA_DMA_MASK_ATA = (1 << 0), /* DMA on ATA Disk */ -- cgit v1.2.3 From 810d07fb4cf7577847f85a6fd6273b69cad8d580 Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Wed, 8 Jul 2026 07:28:00 +0530 Subject: powerpc/pseries: Ensure vpa,slb_shadow & dtl are unregistered during crash Currently pseries_kexec_cpu_down() skips unregistering vpa, slb_shadow and dtl areas during a crash and kexec shutdown path. It was done to avoid doing an HCALL while crashing. However recently Anushree reported that during kernel crash while the kdump kernel was coming up, Hypervisor reported invalid values for 'vpa.yield_count' while it dispatching L2-KVM Guest vcpus. The error manifested as debug build Hypervisor assert triggering to indicate possible VPA corruption. Looking at the kexec cpu offline path it was discovered that during crash kernel doesn't unregister the VPA/SLB-Shadow/DTL area with Hypervisor. Instead it re-allocates and re-registers these areas for cpus during boot. During kexec boot the previously allocated areas can get overwritten with new content without hypervisor knowledge. This creates a small window where while kexec kernel boots and the L2-VCPUs are being dispatched, Hypervisor may try to read/write to a wrong memory area which previously belonged to older VPA. Fix this possible race and memory corruption by updating pseries_kexec_cpu_down() to also unregister vpa,slb_shadow & dtl areas during a kernel crash. Signed-off-by: Vaibhav Jain Tested-by: Anushree Mathur Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260708015802.274271-1-vaibhav@linux.ibm.com --- arch/powerpc/platforms/pseries/kexec.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c index 431be156ca9b..29f7c97ff193 100644 --- a/arch/powerpc/platforms/pseries/kexec.c +++ b/arch/powerpc/platforms/pseries/kexec.c @@ -20,12 +20,15 @@ void pseries_kexec_cpu_down(int crash_shutdown, int secondary) { /* - * Don't risk a hypervisor call if we're crashing - * XXX: Why? The hypervisor is not crashing. It might be better - * to at least attempt unregister to avoid the hypervisor stepping - * on our memory. + * Ensure vpa/slb_shadow/dtl cleanup even while we are crashing. + * Why? The hypervisor is not crashing so at least attempt unregister to + * avoid the hypervisor stepping on our memory. If hypervisor or kexec + * kernel steps on the old memory allocated to these areas before the + * new kexec-kernel happens to allocate and register new areas, + * the hypervisor will see invalid content which may cause + * unexpected behavior. */ - if (firmware_has_feature(FW_FEATURE_SPLPAR) && !crash_shutdown) { + if (firmware_has_feature(FW_FEATURE_SPLPAR)) { int ret; int cpu = smp_processor_id(); int hwcpu = hard_smp_processor_id(); -- cgit v1.2.3 From 8f45abd50aaa4155a72ec539f371dafb039786df Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Wed, 8 Jul 2026 07:28:40 +0530 Subject: powerpc/pseries: Skip vpa_init() for boot cpu in smp_setup_cpu() During pSeries_setup_arch(), VPA for boot-cpu is first to be initialized. However later in the boot, smp_setup_cpu() is called for setting up VPA on boot and secondary cpus that were brought online. This results in vpa_init() being called twice for boot-cpu and three redundant H_REGISTER_VPA hcalls being made to the hypervisor. Fix this by adding an extra condition in smp_set_cpu() to call vpa_init() only on non boot-cpus. Signed-off-by: Vaibhav Jain Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260708015842.274690-1-vaibhav@linux.ibm.com --- arch/powerpc/platforms/pseries/smp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index db99725e752b..bf3d7ed3be01 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -128,7 +128,12 @@ static void smp_setup_cpu(int cpu) else if (cpu != boot_cpuid) xics_setup_cpu(); - if (firmware_has_feature(FW_FEATURE_SPLPAR)) + /* + * Initialize VPA on non-boot cpus since boot-cpu vpa was + * already initialized in pSeries_setup_arch() + */ + if (firmware_has_feature(FW_FEATURE_SPLPAR) && + cpu != boot_cpuid) vpa_init(cpu); cpumask_clear_cpu(cpu, of_spin_mask); -- cgit v1.2.3 From bddf7540099bf653eaea339e886add6f62555cf3 Mon Sep 17 00:00:00 2001 From: "Mukesh Kumar Chaurasiya (IBM)" Date: Tue, 7 Jul 2026 22:54:30 +0530 Subject: powerpc/970: fix nap return address corruption on async interrupt exit On PowerMac G5 (PPC970, CONFIG_PPC_970_NAP) the system panics shortly after boot with symptoms including instruction fetch faults, kernel data access faults, and stack corruption, predominantly on SMP and always somewhere inside softirq processing. The PPC970 idle path works by setting _TLF_NAPPING in the current thread's local flags before entering the MSR_POW nap loop. When any async interrupt wakes the CPU, nap_adjust_return() is expected to detect _TLF_NAPPING, clear it, and rewrite regs->NIP to power4_idle_nap_return so that the interrupt returns cleanly to the caller of power4_idle_nap() rather than back into the nap spin loop. DEFINE_INTERRUPT_HANDLER_ASYNC generates the following sequence: irq_enter_rcu(); ____func(regs); /* timer_interrupt / do_IRQ body */ irq_exit_rcu(); /* softirqs run here, irqs re-enabled */ arch_interrupt_async_exit_prepare(regs); /* nap_adjust_return was here */ irqentry_exit(regs, state); irq_exit_rcu() calls invoke_softirq() -> do_softirq_own_stack(), which runs softirqs with hardware interrupts re-enabled. A nested async interrupt can therefore arrive while _TLF_NAPPING is still set. That nested interrupt reaches nap_adjust_return() in its own arch_interrupt_async_exit_prepare() call, finds _TLF_NAPPING set, and redirects *its own* regs->NIP to power4_idle_nap_return. Returning via that blr with an unrelated LR on the softirq stack jumps to a garbage address, causing the observed crashes. The comment that previously lived in arch_interrupt_async_exit_prepare() even described this exact hazard ("must come before irq_exit()"), but nap_adjust_return() was placed after irq_exit_rcu() in the macro, so the protection was never effective. Fix this by calling nap_adjust_return() inside DEFINE_INTERRUPT_HANDLER_ASYNC immediately before irq_exit_rcu(), ensuring _TLF_NAPPING is cleared and regs->NIP is adjusted before any code that can re-enable interrupts or invoke softirqs runs. Move the explanatory comment into nap_adjust_return() itself and remove it from arch_interrupt_async_exit_prepare(). Fixes: bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") Closes: https://lore.kernel.org/all/87wlvazrdy.fsf@igel.home/ Reported-by: Andreas Schwab Signed-off-by: Mukesh Kumar Chaurasiya (IBM) Tested-by: John Ogness Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260707172430.790040-1-mkchauras@gmail.com --- arch/powerpc/include/asm/entry-common.h | 15 +++++++-------- arch/powerpc/include/asm/interrupt.h | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h index fc636c42e89a..c5adb5006361 100644 --- a/arch/powerpc/include/asm/entry-common.h +++ b/arch/powerpc/include/asm/entry-common.h @@ -66,6 +66,13 @@ static inline void srr_regs_clobbered(void) static inline void nap_adjust_return(struct pt_regs *regs) { #ifdef CONFIG_PPC_970_NAP + /* + * Adjust the nap return address before irq_exit_rcu(). irq_exit_rcu() + * may invoke softirqs with interrupts re-enabled, allowing a nested + * async interrupt to arrive. If _TLF_NAPPING is still set at that + * point, the nested interrupt would erroneously redirect its own + * return address to power4_idle_nap_return, corrupting the stack. + */ if (unlikely(test_thread_local_flags(_TLF_NAPPING))) { /* Can avoid a test-and-clear because NMIs do not call this */ clear_thread_local_flags(_TLF_NAPPING); @@ -286,14 +293,6 @@ static inline void arch_interrupt_async_enter_prepare(struct pt_regs *regs) static inline void arch_interrupt_async_exit_prepare(struct pt_regs *regs) { - /* - * Adjust at exit so the main handler sees the true NIA. This must - * come before irq_exit() because irq_exit can enable interrupts, and - * if another interrupt is taken before nap_adjust_return has run - * here, then that interrupt would return directly to idle nap return. - */ - nap_adjust_return(regs); - arch_interrupt_exit_prepare(regs); } diff --git a/arch/powerpc/include/asm/interrupt.h b/arch/powerpc/include/asm/interrupt.h index fb42a664ae54..1b45a49e9bed 100644 --- a/arch/powerpc/include/asm/interrupt.h +++ b/arch/powerpc/include/asm/interrupt.h @@ -246,6 +246,7 @@ interrupt_handler void func(struct pt_regs *regs) \ instrumentation_begin(); \ irq_enter_rcu(); \ ____##func (regs); \ + nap_adjust_return(regs); \ irq_exit_rcu(); \ instrumentation_end(); \ arch_interrupt_async_exit_prepare(regs); \ -- cgit v1.2.3 From 263e5159e00aa46bf26f3496ff7aae1fc9a6c826 Mon Sep 17 00:00:00 2001 From: "Mukesh Kumar Chaurasiya (IBM)" Date: Fri, 24 Jul 2026 01:18:09 +0530 Subject: powerpc: Fix exit_flags field placement in pt_regs for ptrace Commit d7a6797e0bc1 ("powerpc: add exit_flags field in pt_regs") added the exit_flags field to struct pt_regs to pass internal exit control flags (e.g. _TIF_RESTOREALL) from syscall_exit_prepare() to the low-level assembly exit path. However, the field was placed in a way that was visible to userspace tools such as strace via PTRACE_GETREGS, or caused a struct layout or size regression observable through ptrace. The field is purely kernel-internal and must not be exposed beyond the user_pt_regs boundary. Move exit_flags into struct thread_info where it is only accessible to the kernel, and keep it out of the ptrace-visible register window entirely. Fixes: d7a6797e0bc1 ("powerpc: add exit_flags field in pt_regs") Reported-by: Dmitry V. Levin Closes: https://lore.kernel.org/all/20260722070155.GA11808@strace.io/ Signed-off-by: Mukesh Kumar Chaurasiya (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260723194809.4046600-1-mkchauras@gmail.com --- arch/powerpc/include/asm/ptrace.h | 3 --- arch/powerpc/include/asm/thread_info.h | 1 + arch/powerpc/include/uapi/asm/ptrace.h | 14 +++++--------- arch/powerpc/kernel/interrupt.c | 32 +++++++++++++++++++------------- arch/powerpc/kernel/ptrace/ptrace.c | 1 - arch/powerpc/kernel/signal.c | 2 +- 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index fdeb97421785..d53c4dd4d8b6 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -53,9 +53,6 @@ struct pt_regs unsigned long esr; }; unsigned long result; - unsigned long exit_flags; - /* Maintain 16 byte interrupt stack alignment */ - unsigned long __pt_regs_pad[3]; }; }; #if defined(CONFIG_PPC64) || defined(CONFIG_PPC_KUAP) diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h index ee3b9adb5b67..0487e94d3416 100644 --- a/arch/powerpc/include/asm/thread_info.h +++ b/arch/powerpc/include/asm/thread_info.h @@ -57,6 +57,7 @@ struct thread_info { #ifdef CONFIG_SMP unsigned int cpu; #endif + unsigned long exit_flags; /* Exit Flags for entry/exit */ unsigned long syscall_work; /* SYSCALL_WORK_ flags */ unsigned long local_flags; /* private flags for thread */ #ifdef CONFIG_LIVEPATCH_64 diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h index a393b7f2760a..01e630149d48 100644 --- a/arch/powerpc/include/uapi/asm/ptrace.h +++ b/arch/powerpc/include/uapi/asm/ptrace.h @@ -55,8 +55,6 @@ struct pt_regs unsigned long dar; /* Fault registers */ unsigned long dsisr; /* on 4xx/Book-E used for ESR */ unsigned long result; /* Result of a system call */ - unsigned long exit_flags; /* System call exit flags */ - unsigned long __pt_regs_pad[3]; /* Maintain 16 byte interrupt stack alignment */ }; #endif /* __ASSEMBLER__ */ @@ -116,12 +114,10 @@ struct pt_regs #define PT_DAR 41 #define PT_DSISR 42 #define PT_RESULT 43 -#define PT_EXIT_FLAGS 44 -#define PT_PAD 47 /* 3 times */ -#define PT_DSCR 48 -#define PT_REGS_COUNT 48 +#define PT_DSCR 44 +#define PT_REGS_COUNT 44 -#define PT_FPR0 (PT_REGS_COUNT + 4) /* each FP reg occupies 2 slots in this space */ +#define PT_FPR0 48 /* each FP reg occupies 2 slots in this space */ #ifndef __powerpc64__ @@ -133,7 +129,7 @@ struct pt_regs #define PT_FPSCR (PT_FPR0 + 32) /* each FP reg occupies 1 slot in 64-bit space */ -#define PT_VR0 (PT_FPSCR + 2) /* <82> each Vector reg occupies 2 slots in 64-bit */ +#define PT_VR0 82 /* each Vector reg occupies 2 slots in 64-bit */ #define PT_VSCR (PT_VR0 + 32*2 + 1) #define PT_VRSAVE (PT_VR0 + 33*2) @@ -141,7 +137,7 @@ struct pt_regs /* * Only store first 32 VSRs here. The second 32 VSRs in VR0-31 */ -#define PT_VSR0 (PT_VRSAVE + 2) /* each VSR reg occupies 2 slots in 64-bit */ +#define PT_VSR0 150 /* each VSR reg occupies 2 slots in 64-bit */ #define PT_VSR31 (PT_VSR0 + 2*31) #endif /* __powerpc64__ */ diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index f04978080837..5b88bf72786c 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c @@ -89,15 +89,17 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, long scv) { unsigned long ti_flags; + unsigned long ret = 0; bool is_not_scv = !IS_ENABLED(CONFIG_PPC_BOOK3S_64) || !scv; kuap_assert_locked(); regs->result = r3; - regs->exit_flags = 0; - ti_flags = read_thread_flags(); + /* Clear exit_flags so only flags set during this exit are visible */ + current->thread_info.exit_flags = 0; + ti_flags = read_thread_flags(); if (unlikely(r3 >= (unsigned long)-MAX_ERRNO) && is_not_scv) { if (likely(!(ti_flags & (_TIF_NOERROR | _TIF_RESTOREALL)))) { r3 = -r3; @@ -107,7 +109,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, if (unlikely(ti_flags & _TIF_PERSYSCALL_MASK)) { if (ti_flags & _TIF_RESTOREALL) - regs->exit_flags = _TIF_RESTOREALL; + ret = _TIF_RESTOREALL; else regs->gpr[3] = r3; clear_bits(_TIF_PERSYSCALL_MASK, ¤t_thread_info()->flags); @@ -116,7 +118,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, } if (unlikely(ti_flags & _TIF_SYSCALL_DOTRACE)) { - regs->exit_flags |= _TIF_RESTOREALL; + ret |= _TIF_RESTOREALL; } syscall_exit_to_user_mode(regs); @@ -132,17 +134,19 @@ again: /* Restore user access locks last */ kuap_user_restore(regs); - + ret |= current->thread_info.exit_flags; #ifdef CONFIG_PPC64 - regs->exit_result = regs->exit_flags; + regs->exit_result = ret; #endif - return regs->exit_flags; + return ret; } #ifdef CONFIG_PPC64 notrace unsigned long syscall_exit_restart(unsigned long r3, struct pt_regs *regs) { + unsigned long ret; + /* * This is called when detecting a soft-pending interrupt as well as * an alternate-return interrupt. So we can't just have the alternate @@ -167,9 +171,11 @@ again: } kuap_user_restore(regs); - regs->exit_result |= regs->exit_flags; + ret = current_thread_info()->exit_flags & _TIF_RESTOREALL; + current_thread_info()->exit_flags &= ~_TIF_RESTOREALL; + regs->exit_result |= ret; - return regs->exit_result; + return ret; } #endif @@ -186,8 +192,10 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs) */ kuap_assert_locked(); + /* Clear exit_flags so only flags set during this exit are visible */ + current_thread_info()->exit_flags = 0; + local_irq_disable(); - regs->exit_flags = 0; again: check_return_regs_valid(regs); user_enter_irqoff(); @@ -200,9 +208,7 @@ again: /* Restore user access locks last */ kuap_user_restore(regs); - - ret = regs->exit_flags; - + ret = current_thread_info()->exit_flags & _TIF_RESTOREALL; #ifdef CONFIG_PPC64 regs->exit_result = ret; #endif diff --git a/arch/powerpc/kernel/ptrace/ptrace.c b/arch/powerpc/kernel/ptrace/ptrace.c index 316d4f5ead8e..6cd180bc36ab 100644 --- a/arch/powerpc/kernel/ptrace/ptrace.c +++ b/arch/powerpc/kernel/ptrace/ptrace.c @@ -291,7 +291,6 @@ void __init pt_regs_check(void) CHECK_REG(PT_DAR, dar); CHECK_REG(PT_DSISR, dsisr); CHECK_REG(PT_RESULT, result); - CHECK_REG(PT_EXIT_FLAGS, exit_flags); #undef CHECK_REG BUILD_BUG_ON(PT_REGS_COUNT != sizeof(struct user_pt_regs) / sizeof(unsigned long)); diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index bb42a8b6c642..cc6498501610 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -356,6 +356,6 @@ void signal_fault(struct task_struct *tsk, struct pt_regs *regs, void arch_do_signal_or_restart(struct pt_regs *regs) { BUG_ON(regs != current->thread.regs); - regs->exit_flags |= _TIF_RESTOREALL; + current_thread_info()->exit_flags |= _TIF_RESTOREALL; do_signal(current); } -- cgit v1.2.3 From c824ab65685bb119c6c6a3a200b3428c72862d5a Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 2 Jul 2026 23:15:55 +0200 Subject: powerpc/boot: Fix simpleboot CPU node lookup check fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly. Fixes: d2477b5cc8ca ("[POWERPC] bootwrapper: Add a firmware-independent simpleboot target.") Signed-off-by: Thorsten Blum Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260702211554.56923-4-thorsten.blum@linux.dev --- arch/powerpc/boot/simpleboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/simpleboot.c b/arch/powerpc/boot/simpleboot.c index c80691d83880..27591df41e9e 100644 --- a/arch/powerpc/boot/simpleboot.c +++ b/arch/powerpc/boot/simpleboot.c @@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, /* finally, setup the timebase */ node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", "cpu", sizeof("cpu")); - if (!node) + if (node < 0) fatal("Cannot find cpu node\n"); timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); if (timebase && (size == 4)) -- cgit v1.2.3 From 43863f6575d2211e8c5157fefb83ad0ad046aab4 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 2 Jul 2026 23:15:56 +0200 Subject: powerpc/boot: Fix treeboot-currituck CPU node lookup check fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly. Fixes: 228d55053397 ("powerpc/47x: Add support for the new IBM currituck platform") Signed-off-by: Thorsten Blum Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260702211554.56923-5-thorsten.blum@linux.dev --- arch/powerpc/boot/treeboot-currituck.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/treeboot-currituck.c b/arch/powerpc/boot/treeboot-currituck.c index d53e8a592f81..5b5363b74f9f 100644 --- a/arch/powerpc/boot/treeboot-currituck.c +++ b/arch/powerpc/boot/treeboot-currituck.c @@ -102,7 +102,7 @@ void platform_init(void) node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", "cpu", sizeof("cpu")); - if (!node) + if (node < 0) fatal("Cannot find cpu node\n"); timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); if (timebase && (size == 4)) -- cgit v1.2.3 From b24fc8278b70a9d27ec801a427ab4de9b769d69a Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 2 Jul 2026 23:15:57 +0200 Subject: powerpc/boot: Fix treeboot-akebono CPU node lookup check fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly. Fixes: 2a2c74b2efcb ("IBM Akebono: Add the Akebono platform") Signed-off-by: Thorsten Blum Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260702211554.56923-6-thorsten.blum@linux.dev --- arch/powerpc/boot/treeboot-akebono.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/treeboot-akebono.c b/arch/powerpc/boot/treeboot-akebono.c index e3cc2599869c..1b529037480f 100644 --- a/arch/powerpc/boot/treeboot-akebono.c +++ b/arch/powerpc/boot/treeboot-akebono.c @@ -146,7 +146,7 @@ void platform_init(char *userdata) node = fdt_node_offset_by_prop_value(_dtb_start, -1, "device_type", "cpu", sizeof("cpu")); - if (!node) + if (node < 0) fatal("Cannot find cpu node\n"); timebase = fdt_getprop(_dtb_start, node, "timebase-frequency", &size); if (timebase && (size == 4)) -- cgit v1.2.3 From 41bf83c1b10781fcb992ff0680db771b7b833308 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 13 Jul 2026 11:17:33 +0200 Subject: powerpc/ps3: Remove unused struct table in setup_areas() The local table structure is not used - remove it. Reviewed-by: Geert Uytterhoeven Signed-off-by: Thorsten Blum Reviewed-by: Christophe Leroy (CS GROUP) Reviewed-by: Ritesh Harjani (IBM) Reviewed-by: Amit Machhiwal Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260713091731.97212-3-thorsten.blum@linux.dev --- arch/powerpc/platforms/ps3/spu.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index e4e0b45e1b9d..a71c0b0cfb07 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c @@ -191,8 +191,6 @@ static void spu_unmap(struct spu *spu) static int __init setup_areas(struct spu *spu) { - struct table {char* name; unsigned long addr; unsigned long size;}; - spu_pdata(spu)->shadow = ioremap_prot(spu_pdata(spu)->shadow_addr, sizeof(struct spe_shadow), pgprot_noncached_wc(PAGE_KERNEL_RO)); -- cgit v1.2.3 From 0bb024f11d120abff3e8db9144a585b9d7fb8459 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 11 Jul 2026 15:09:32 +0200 Subject: powerpc/ps3: Fix map failure path in dma_ioc0_map_pages() If lv1_put_iopte() fails in dma_ioc0_map_pages(), the error path decrements iopage but keeps using the failed mapping's offset. As a result, it repeatedly tries to invalidate the failed IOPTE slot and leaves the already installed IOPTEs valid. Recompute offset and invalidate the installed IOPTEs instead. Fixes: 6bb5cf102541 ("[POWERPC] PS3: System-bus rework") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Ritesh Harjani (IBM) Reviewed-by: Geert Uytterhoeven Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260711130931.740719-3-thorsten.blum@linux.dev --- arch/powerpc/platforms/ps3/mm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index 20fc5b68faee..315a32fd75b1 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c @@ -615,6 +615,7 @@ static int dma_ioc0_map_pages(struct ps3_dma_region *r, unsigned long phys_addr, fail_map: for (iopage--; 0 <= iopage; iopage--) { + offset = (1 << r->page_size) * iopage; lv1_put_iopte(0, c->bus_addr + offset, c->lpar_addr + offset, -- cgit v1.2.3 From d71b3ca5231750fecdefb3390595e7db30979ac4 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 4 Jul 2026 14:13:54 +0200 Subject: powerpc/perf: Use strstarts() to simplify is_thread_imc_pmu() Replace the open-coded implementation with strstarts() to simplify is_thread_imc_pmu(). Signed-off-by: Thorsten Blum Reviewed-by: Athira Rajeev Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260704121353.201583-3-thorsten.blum@linux.dev --- arch/powerpc/perf/imc-pmu.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c index e3822f36c419..1d2db6d3e226 100644 --- a/arch/powerpc/perf/imc-pmu.c +++ b/arch/powerpc/perf/imc-pmu.c @@ -1023,10 +1023,7 @@ static int thread_imc_event_init(struct perf_event *event) static bool is_thread_imc_pmu(struct perf_event *event) { - if (!strncmp(event->pmu->name, "thread_imc", strlen("thread_imc"))) - return true; - - return false; + return strstarts(event->pmu->name, "thread_imc"); } static __be64 *get_event_base_addr(struct perf_event *event) -- cgit v1.2.3 From 86f057c19fbe089ec8a2020ef5f1579b99d87ac7 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 23 Jun 2026 17:38:25 +0200 Subject: powerpc/serial: Fix include guard comment Replace _PPC64_SERIAL_H with _ASM_POWERPC_SERIAL_H to match the actual macro name. Remove an empty comment while at it. Signed-off-by: Thorsten Blum Reviewed-by: Mukesh Kumar Chaurasiya (IBM) Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260623153825.403819-2-thorsten.blum@linux.dev --- arch/powerpc/include/asm/serial.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/serial.h b/arch/powerpc/include/asm/serial.h index cd6c18d0e66e..c3eb4a64b3f5 100644 --- a/arch/powerpc/include/asm/serial.h +++ b/arch/powerpc/include/asm/serial.h @@ -1,6 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - */ #ifndef _ASM_POWERPC_SERIAL_H #define _ASM_POWERPC_SERIAL_H @@ -18,4 +16,4 @@ extern void find_legacy_serial_ports(void); #define find_legacy_serial_ports() do { } while (0) #endif -#endif /* _PPC64_SERIAL_H */ +#endif /* _ASM_POWERPC_SERIAL_H */ -- cgit v1.2.3 From d568a43f6dbba3ba006304d95fd09862bd482a2f Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:46 +0100 Subject: afs: Fix afs_fs_fetch_data() to set call->async Fix afs_fs_fetch_data() to set call->async on an async operation as does afs_fs_fetch_data64(). Fixes: eddf51f2bb2c ("afs: Make {Y,}FS.FetchData an asynchronous operation") Link: https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-2-dhowells@redhat.com cc: Marc Dionne cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/fsclient.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index a2ffd60889f8..626e1d37b915 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -477,6 +477,9 @@ void afs_fs_fetch_data(struct afs_operation *op) if (!call) return afs_op_nomem(op); + if (op->flags & AFS_OPERATION_ASYNC) + call->async = true; + /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHDATA); -- cgit v1.2.3 From 222052c6be186f2074b3a4d741d5de200f654c43 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:47 +0100 Subject: afs: Fix afs_fs_fetch_data() to subtract transferred from len Fix afs_fs_fetch_data() to subtract subreq->transferred from subreq->len rather than adding it. Fixes: f28fc2010d62 ("afs: Eliminate afs_read") Link: https://sashiko.dev/#/patchset/20260713081022.2186481-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-3-dhowells@redhat.com cc: Marc Dionne cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/fsclient.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 626e1d37b915..1a3f186a6a11 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -487,7 +487,7 @@ void afs_fs_fetch_data(struct afs_operation *op) bp[2] = htonl(vp->fid.vnode); bp[3] = htonl(vp->fid.unique); bp[4] = htonl(lower_32_bits(subreq->start + subreq->transferred)); - bp[5] = htonl(lower_32_bits(subreq->len + subreq->transferred)); + bp[5] = htonl(lower_32_bits(subreq->len - subreq->transferred)); call->fid = vp->fid; trace_afs_make_fs_call(call, &vp->fid); -- cgit v1.2.3 From 4af1ec68d54b3871155914d584fb10669c41a861 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:48 +0100 Subject: afs: Fix UAF when sending a message In afs_make_call(), there's a race with async call reception and destruction. If a call is dispatched that doesn't have call->write_iter set (used to specify the data content for FS.StoreData), then the first rxrpc_kernel_send_data() will not set MSG_MORE in the msghdr. Once rxrpc_send_data() queues the last request packet, the response could come in at any time and cause the call to be completed and put. However, afs_make_call() will look at the call again to see it ->write_iter should be handled - something it's only allowed to do if it has its own ref on the call. Whilst this is the case for synchronous calls, it isn't true for async calls such as FS.FetchData. There's also a potential UAF in afs_make_call() in the event that an asynchronous call is being sent, but the call fails in some way (e.g. it gets aborted from the server). The problem there is that afs_make_call() tries to abort a call if the rxrpc send fails, but the asynchronous notification from rxrpc may have caused the afs_call to be torn down. generic/650 plays games with randomly taking CPUs offline, and can interject a significant delay such that the call is deallocated before afs_make_call() gets to check call->write_iter - and a UAF ensues (caught by KASAN). BUG: KASAN: slab-use-after-free in afs_make_call+0x1c90/0x2210 [kafs] Read of size 8 at addr ffff888035e050e8 by task fsstress/1409 Fix this by making afs_make_op_call() give the op->call its own ref rather than transferring the caller's ref to it and then dropping the ref when afs_make_call() returns. This also means that the afs_make_call() func never loses its ref on the call now. Fixes: eddf51f2bb2c ("afs: Make {Y,}FS.FetchData an asynchronous operation") Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept") Link: https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com Reported-by: Marc Dionne Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-4-dhowells@redhat.com cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/internal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 601f01e5c15f..290873bac89b 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1421,7 +1421,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c { struct afs_addr_list *alist = op->estate->addresses; - op->call = call; + op->call = afs_get_call(call, afs_call_trace_get); op->type = call->type; call->op = op; call->key = op->key; @@ -1429,6 +1429,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c call->peer = rxrpc_kernel_get_peer(alist->addrs[op->addr_index].peer); call->service_id = op->server->service_id; afs_make_call(call, gfp); + afs_put_call(call); } static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size) -- cgit v1.2.3 From 1c3e23e78862493e8cf1adad02b10ffcb8b9921c Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 19 Jul 2026 08:06:31 +0500 Subject: staging: rtl8723bs: fix OOB read in rtw_get_wpa_ie() rtw_get_wpa_ie() reads bytes at fixed offsets into a vendor-specific information element without checking that the element is long enough, causing an out-of-bounds read for a short trailing IE. The function locates a vendor-specific IE (EID 221) with rtw_get_ie() and then compares a 4-byte OUI+type at pbuf + 2 and reads a 2-byte version word at pbuf + 6. Those accesses require the IE body to be at least 6 bytes, but rtw_get_ie() only guarantees that the element fits within the buffer; it does not enforce a minimum body length. A vendor-specific IE whose length byte is 0 to 5, placed at the end of the buffer, therefore makes these reads run past the end of the IE and past the end of the buffer itself. The buffer holds information elements taken from received management frames and from the IE blob passed to rtw_cfg80211_set_wpa_ie(), which is kmemdup'd to its exact length, so the read can run off the end of the allocation. The sibling helpers rtw_get_sec_ie(), rtw_get_wapi_ie() and rtw_get_wps_ie() in this file already reject too-short vendor-specific IEs before their OUI memcmp(); rtw_get_wpa_ie() was never brought in line with them, and needs a minimum of 6 rather than 4 bytes because of the version word. Add the missing length check. Fixes: 554c0a3abf216 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260719030631.88254-1-meatuni001@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 863ddf846218..e60ed3359866 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -371,6 +371,9 @@ unsigned char *rtw_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit) pbuf = rtw_get_ie(pbuf, WLAN_EID_VENDOR_SPECIFIC, &len, limit_new); if (pbuf) { + if (len < 6) + goto check_next_ie; + /* check if oui matches... */ if (memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type))) goto check_next_ie; -- cgit v1.2.3 From ae21407350151bddfd4fea7aa39bd0643c0ca9d3 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Sun, 19 Jul 2026 09:15:09 +0500 Subject: staging: rtl8723bs: fix OOB read in WMM_param_handler() WMM_param_handler() copies a fixed-size WMM parameter element out of a received information element without checking that the element is long enough, causing an out-of-bounds read for a short WMM IE. The handler reads sizeof(struct WMM_para_element) (18) bytes at pIE->data + 6, so it requires pIE->length to be at least 24 (WLAN_WMM_LEN), but it never validates the length. Two of its three callers reach it after matching only the WMM OUI: OnAssocRsp() in rtw_mlme_ext.c matches a 6-byte OUI, and join_cmd_hdl() matches a 4-byte OUI, before calling the handler. A vendor-specific IE carrying the WMM OUI but a length between 6 and 23, placed in an association response or in the IE blob handed to join_cmd_hdl(), passes the OUI check and then makes the memcmp() and memcpy() at pIE->data + 6 read past the end of the element. OnAssocRsp() parses a frame received from the AP, so this is reachable from a remote peer. The remaining caller in rtw_wlan_util.c already guards the handler with "pIE->length == WLAN_WMM_LEN". Move the equivalent check into the handler itself so every caller is covered; the sibling IE handlers in the same parsing loop (HT_caps_handler(), HT_info_handler(), ERP_IE_handler()) likewise bound their accesses by pIE->length. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Link: https://patch.msgid.link/20260719041509.97894-1-meatuni001@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c index a4de538722b5..c614c0b4c792 100644 --- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c +++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c @@ -666,6 +666,9 @@ int WMM_param_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE) return false; } + if (pIE->length != WLAN_WMM_LEN) + return false; + if (!memcmp(&(pmlmeinfo->WMM_param), (pIE->data + 6), sizeof(struct WMM_para_element))) return false; -- cgit v1.2.3 From 2c56ef658ac8c6bca36bc5574715e8f717207c6c Mon Sep 17 00:00:00 2001 From: Panagiotis Petrakopoulos Date: Mon, 20 Jul 2026 11:24:09 +0300 Subject: staging: rtl8723bs: fix missing shared-key auth challenge length check The WEP shared-key authentication handler uses the challenge-text element's attacker-controlled length without checking it against the fixed 128-byte chg_txt buffer. In OnAuthClient() the length from rtw_get_ie() - up to 255 - is used to perform memcpy() into the 128-byte pmlmeinfo->chg_txt, so a malicious AP sending a malformed WLAN_EID_CHALLENGE element can overflow/underfill chg_txt by up to 127 bytes. It is reachable over the air, before association, during shared-key authentication. In the case of an overflow, the driver can write out of bounds. In the case of an underfill, the driver can echo stale buffer memory. The challenge text is defined to be exactly 128 octets, which is already provided as the WLAN_AUTH_CHALLENGE_LEN define; require the element to be exactly that length before use. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Panagiotis Petrakopoulos Link: https://patch.msgid.link/20260720082409.168379-1-npetrakopoulos2003@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index a443b3530fb9..a9382dc1294b 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -879,7 +879,7 @@ unsigned int OnAuthClient(struct adapter *padapter, union recv_frame *precv_fram p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + _AUTH_IE_OFFSET_, WLAN_EID_CHALLENGE, (int *)&len, pkt_len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_); - if (!p) + if (!p || len != WLAN_AUTH_CHALLENGE_LEN) goto authclnt_fail; memcpy(pmlmeinfo->chg_txt, p + 2, len); -- cgit v1.2.3 From 6829665d050983907b560173e49dcc6c11cb2730 Mon Sep 17 00:00:00 2001 From: Mariano Baragiola Date: Mon, 27 Jul 2026 13:08:59 -0300 Subject: staging: rtl8723bs: validate monitor transmit frame lengths rtw_cfg80211_monitor_if_xmit_entry() removes the radiotap header and then reads the 802.11 frame control field without checking that a base 802.11 header remains. The data path also pulls the calculated 802.11, QoS and SNAP header span before confirming that the skb contains it. A truncated frame can therefore cause out-of-bounds reads or leave insufficient data for the Ethernet address writes. Reject frames that do not contain the base 802.11 header and data frames that do not contain their complete calculated header span. Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") Cc: stable Signed-off-by: Mariano Baragiola Link: https://patch.msgid.link/20260727160859.1917096-1-mbaragiola@linux.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 967cd1b34aed..9fa3131feb66 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -2033,6 +2033,8 @@ static netdev_tx_t rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struc /* Skip the ratio tap header */ skb_pull(skb, rtap_len); + if (skb->len < dot11_hdr_len) + goto fail; dot11_hdr = (struct ieee80211_hdr *)skb->data; frame_control = le16_to_cpu(dot11_hdr->frame_control); @@ -2045,6 +2047,8 @@ static netdev_tx_t rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struc qos_len = 2; if ((frame_control & 0x0300) == 0x0300) dot11_hdr_len += 6; + if (skb->len < dot11_hdr_len + qos_len + snap_len) + goto fail; memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); -- cgit v1.2.3 From 0a4bb2abc3e56d7be6e69b050c88ba52c87e22bf Mon Sep 17 00:00:00 2001 From: Hardik Prakash Date: Sat, 18 Jul 2026 11:13:31 +0530 Subject: i2c: designware: defer probe if child GpioInt controllers are not bound I2C controllers may have child devices with GpioInt resources that depend on GPIO controllers being fully initialized. If the I2C controller probes and enumerates children before the referenced GPIO controller has completed probe, GPIO interrupts may not be properly configured, leading to device failures. On Lenovo Yoga 7 14AGP11, the WACF2200 touchscreen (child of AMDI0010:02) has a GpioInt resource pointing to GPIO 157 on the pinctrl-amd controller (AMDI0030:00). When i2c-designware probes AMDI0010:02 before pinctrl-amd finishes initializing, I2C transactions fail with lost arbitration errors: 0.285952 amd_gpio_probe: registering gpiochip <- GPIO chip visible 0.287121 amd_gpio_probe: requesting parent IRQ <- probe still running 0.301454 AMDI0010:02 dw_i2c_plat_probe: start <- races here 2.348157 lost arbitration Add a dependency check that walks ACPI child devices and defers probe until any referenced GPIO controller is bound. Fixes: 3812a9e84265 ("pinctrl-amd: enable IRQ for WACF2200 touchscreen on Lenovo Yoga 7 14AGP11") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221494 Suggested-by: Mario Limonciello Suggested-by: Andy Shevchenko Signed-off-by: Hardik Prakash Assisted-by: Claude:claude-sonnet-5 Assisted-by: DeepSeek:deepseek-v4-pro Cc: # v7.1+ Acked-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260718054330.8975-2-hardikprakash.official@gmail.com --- drivers/i2c/busses/i2c-designware-platdrv.c | 80 +++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 6d6e81242f74..c8a203fff4d1 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -8,12 +8,14 @@ * Copyright (C) 2007 MontaVista Software Inc. * Copyright (C) 2009 Provigent Ltd. */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -130,6 +132,80 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) return 0; } +#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB) +/* + * Check whether an ACPI GpioInt resource's referenced GPIO controller + * has finished probing. Resources with no named controller (resource + * source string) are skipped, since they can't be resolved to a + * struct device. + */ +static int check_gpioint_resource(struct acpi_resource *ares, void *data) +{ + struct acpi_resource_gpio *agpio; + struct acpi_device *gpio_adev; + struct device *gpio_dev; + acpi_handle handle; + acpi_status status; + + if (!acpi_gpio_get_irq_resource(ares, &agpio)) + return 1; /* not a GpioInt resource, skip */ + + if (!agpio->resource_source.string_length) + return 1; /* no named controller, skip */ + + status = acpi_get_handle(NULL, agpio->resource_source.string_ptr, &handle); + if (ACPI_FAILURE(status)) + return 1; + + gpio_adev = acpi_fetch_acpi_dev(handle); + if (!gpio_adev) + return 1; + + struct gpio_device *gdev __free(gpio_device_put) = + gpio_device_find_by_fwnode(acpi_fwnode_handle(gpio_adev)); + if (!gdev) + return -EPROBE_DEFER; /* controller not registered yet: abort walk */ + + gpio_dev = gpio_device_to_device(gdev)->parent; + + guard(device)(gpio_dev); + if (!device_is_bound(gpio_dev)) + return -EPROBE_DEFER; /* controller not bound yet: abort walk */ + + return 1; /* bound, skip adding to resource list, continue walk */ +} + +static int check_child_gpioint(struct acpi_device *adev, void *data) +{ + LIST_HEAD(res_list); + int ret; + + ret = acpi_dev_get_resources(adev, &res_list, check_gpioint_resource, NULL); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&res_list); + + return 0; +} + +static int i2c_dw_check_gpio_dependencies(struct device *dev) +{ + struct acpi_device *adev; + + adev = ACPI_COMPANION(dev); + if (!adev) + return 0; + + return acpi_dev_for_each_child(adev, check_child_gpioint, NULL); +} +#else +static int i2c_dw_check_gpio_dependencies(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_ACPI && CONFIG_GPIOLIB */ + static int dw_i2c_plat_probe(struct platform_device *pdev) { u32 flags = (uintptr_t)device_get_match_data(&pdev->dev); @@ -138,6 +214,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) struct dw_i2c_dev *dev; int irq, ret; + ret = i2c_dw_check_gpio_dependencies(device); + if (ret) + return ret; + irq = platform_get_irq_optional(pdev, 0); if (irq == -ENXIO) flags |= ACCESS_POLLING; -- cgit v1.2.3 From b14361aca6350ff7907b0e9903c7b94dc7d5d4a0 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Wed, 22 Jul 2026 16:38:58 +0800 Subject: fou: Fix use-after-free in fou_create() fou_create() publishes struct fou through sk_user_data before adding the new FOU port to the per-netns list. If fou_add_to_port_list() fails, the error path frees fou while it is still reachable through sk_user_data. A concurrent receive can then dereference the freed object in fou_from_sock(). This ordering issue was previously noted in the linked discussion. The failure is reachable when local port 0 is requested. Each socket binds to a different ephemeral port, but fou_cfg_cmp() compares the requested port 0 and reports -EALREADY once an entry already exists. Release the tunnel socket before freeing fou so sk_user_data is cleared first, and defer reclamation with kfree_rcu() to protect concurrent RCU readers. This matches the lifetime handling in fou_release(). Fixes: 23461551c006 ("fou: Support for foo-over-udp RX path") Suggested-by: Kuniyuki Iwashima Link: https://lore.kernel.org/netdev/20260502031401.3557229-12-kuniyu@google.com/ Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Link: https://patch.msgid.link/20260722083858.182506-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni --- net/ipv4/fou_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/fou_core.c b/net/ipv4/fou_core.c index 865bd7205122..ab09dfcdecbd 100644 --- a/net/ipv4/fou_core.c +++ b/net/ipv4/fou_core.c @@ -629,9 +629,9 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, return 0; error: - kfree(fou); if (sock) udp_tunnel_sock_release(sock->sk); + kfree_rcu(fou, rcu); return err; } -- cgit v1.2.3 From 295dd295e2137e10e9a5b1891d97e0f08de76f03 Mon Sep 17 00:00:00 2001 From: Yehyeong Lee Date: Thu, 23 Jul 2026 10:08:29 +0900 Subject: net: mpls: initialize rtm_tos in mpls_getroute() mpls_getroute() builds the RTM_NEWROUTE reply to an RTM_GETROUTE request by filling a struct rtmsg allocated from an skb whose data area is not zeroed (alloc_skb(NLMSG_GOODSIZE, ...)). It sets every field of the header except rtm_tos: r = nlmsg_data(nlh); r->rtm_family = AF_MPLS; r->rtm_dst_len = 20; r->rtm_src_len = 0; r->rtm_table = RT_TABLE_MAIN; r->rtm_type = RTN_UNICAST; r->rtm_scope = RT_SCOPE_UNIVERSE; r->rtm_protocol = rt->rt_protocol; r->rtm_flags = 0; struct rtmsg has no padding, so the one uninitialised byte rtm_tos (offset 3) is copied straight to user space on recvmsg(), leaking a byte of uninitialised heap memory. This is in contrast to mpls_dump_route(), which fills the very same header and does set rtm_tos = 0. Initialize rtm_tos to 0, matching mpls_dump_route(). Reproduced with KMSAN by adding an MPLS route and issuing a non-RTM_F_FIB_MATCH RTM_GETROUTE for its label: BUG: KMSAN: kernel-infoleak in _copy_to_iter+0x36c/0x33f0 _copy_to_iter+0x36c/0x33f0 __skb_datagram_iter+0x196/0x12c0 skb_copy_datagram_iter+0x5b/0x210 netlink_recvmsg+0x37b/0xef0 ... Uninit was created at: __alloc_skb+0x8ca/0x10e0 mpls_getroute+0x1280/0x3a40 rtnetlink_rcv_msg+0x1138/0x15a0 ... Byte 19 of 64 is uninitialized (byte 19 = nlmsghdr(16) + rtmsg offset 3 = rtm_tos) Fixes: 397fc9e5cefe ("mpls: route get support") Signed-off-by: Yehyeong Lee Link: https://patch.msgid.link/20260723010830.289917-1-yhlee@isslab.korea.ac.kr Signed-off-by: Paolo Abeni --- net/mpls/af_mpls.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 4406c304b639..961be5054a03 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -2539,6 +2539,7 @@ static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, r->rtm_family = AF_MPLS; r->rtm_dst_len = 20; r->rtm_src_len = 0; + r->rtm_tos = 0; r->rtm_table = RT_TABLE_MAIN; r->rtm_type = RTN_UNICAST; r->rtm_scope = RT_SCOPE_UNIVERSE; -- cgit v1.2.3 From 687f39faccba29ab26de965411db37e849af8ec2 Mon Sep 17 00:00:00 2001 From: Hans Ulli Kroll Date: Sun, 19 Jul 2026 15:35:59 +0200 Subject: pinctrl: qcom: ipq806x: mark gpio as a GPIO pin function The qcom pinctrl core supports marking functions that represent GPIO mode via PINCTRL_GPIO_PINFUNCTION(), so that strict pinmuxing does not reject GPIO requests for pins that are muxed to the GPIO function. Add a IPQ_GPIO_PIN_FUNCTION() helper and use it for the ipq806x gpio function, matching how the msm-based qcom drivers handle this. This allows ipq806x to keep the GPIO-related configuration in DTS without tripping over strict pinmux ownership checks. Fixes: cc85cb96e2e4 ("pinctrl: qcom: make the pinmuxing strict") Signed-off-by: Hans Ulli Kroll Acked-by: Linus Walleij Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260719134548.8830-2-linux@ulli-kroll.de Signed-off-by: Bartosz Golaszewski --- drivers/pinctrl/qcom/pinctrl-ipq8064.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8064.c b/drivers/pinctrl/qcom/pinctrl-ipq8064.c index 78d320d56be6..e1c3c213559f 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq8064.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq8064.c @@ -480,7 +480,7 @@ static const char * const ps_hold_groups[] = { }; static const struct pinfunction ipq8064_functions[] = { - IPQ_PIN_FUNCTION(gpio), + IPQ_GPIO_PIN_FUNCTION(gpio), IPQ_PIN_FUNCTION(mdio), IPQ_PIN_FUNCTION(ssbi), IPQ_PIN_FUNCTION(spmi), diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 4fbff61de6bb..b94ba1a4177e 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -24,6 +24,11 @@ struct pinctrl_pin_desc; fname##_groups, \ ARRAY_SIZE(fname##_groups)) +#define IPQ_GPIO_PIN_FUNCTION(fname) \ + [IPQ_MUX_##fname] = PINCTRL_GPIO_PINFUNCTION(#fname, \ + fname##_groups, \ + ARRAY_SIZE(fname##_groups)) + #define MSM_PIN_FUNCTION(fname) \ [msm_mux_##fname] = PINCTRL_PINFUNCTION(#fname, \ fname##_groups, \ -- cgit v1.2.3 From fd46760956509f580f7d3d25db4de10e7c6f949b Mon Sep 17 00:00:00 2001 From: Hans Ulli Kroll Date: Sun, 19 Jul 2026 15:36:00 +0200 Subject: pinctrl: qcom: ipq806x: mark pci reset as a GPIO pin function The qcom pinctrl core supports marking functions that represent GPIO mode via PINCTRL_GPIO_PINFUNCTION(), so that strict pinmuxing does not reject GPIO requests for pins that are muxed to the GPIO function. Mark PCIe reset as GPIO pin function This allows ipq806x to keep the PCIe-reset related configuration in DTS without tripping over strict pinmux ownership checks. Fixes: cc85cb96e2e4 ("pinctrl: qcom: make the pinmuxing strict") Signed-off-by: Hans Ulli Kroll Acked-by: Linus Walleij Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260719134548.8830-3-linux@ulli-kroll.de Signed-off-by: Bartosz Golaszewski --- drivers/pinctrl/qcom/pinctrl-ipq8064.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq8064.c b/drivers/pinctrl/qcom/pinctrl-ipq8064.c index e1c3c213559f..0a50486337d3 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq8064.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq8064.c @@ -507,19 +507,19 @@ static const struct pinfunction ipq8064_functions[] = { IPQ_PIN_FUNCTION(usb2_hsic), IPQ_PIN_FUNCTION(rgmii2), IPQ_PIN_FUNCTION(sata), - IPQ_PIN_FUNCTION(pcie1_rst), + IPQ_GPIO_PIN_FUNCTION(pcie1_rst), IPQ_PIN_FUNCTION(pcie1_prsnt), IPQ_PIN_FUNCTION(pcie1_pwren_n), IPQ_PIN_FUNCTION(pcie1_pwren), IPQ_PIN_FUNCTION(pcie1_pwrflt), IPQ_PIN_FUNCTION(pcie1_clk_req), - IPQ_PIN_FUNCTION(pcie2_rst), + IPQ_GPIO_PIN_FUNCTION(pcie2_rst), IPQ_PIN_FUNCTION(pcie2_prsnt), IPQ_PIN_FUNCTION(pcie2_pwren_n), IPQ_PIN_FUNCTION(pcie2_pwren), IPQ_PIN_FUNCTION(pcie2_pwrflt), IPQ_PIN_FUNCTION(pcie2_clk_req), - IPQ_PIN_FUNCTION(pcie3_rst), + IPQ_GPIO_PIN_FUNCTION(pcie3_rst), IPQ_PIN_FUNCTION(pcie3_prsnt), IPQ_PIN_FUNCTION(pcie3_pwren_n), IPQ_PIN_FUNCTION(pcie3_pwren), -- cgit v1.2.3 From aef96eead2860cbfa371e4471d4f04412213b958 Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Thu, 23 Jul 2026 00:49:55 -0400 Subject: net/sched: cls_u32: validate offshift to prevent shift-out-of-bounds u32_change() copies the user-provided tc_u32_sel.offshift (unsigned char, 0-255) into the kernel knode object without bounds validation. When a packet later hits u32_classify() with TC_U32_VAROFFSET set, it evaluates `ntohs(offmask & *data) >> offshift` where the left operand is a 16-bit value promoted to a 32-bit int. Any offshift >= 32 is undefined behavior per C11 6.5.7p3, triggerable by an unprivileged user via user/network namespaces. UBSAN: shift-out-of-bounds in net/sched/cls_u32.c:236:43 shift exponent 32 is too large for 32-bit type int Fix this by rejecting offshift >= 16 during filter creation in u32_change(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: AutonomousCodeSecurity@microsoft.com Link: https://lore.kernel.org/all/20260720034514.23053-1-blbllhy@gmail.com Signed-off-by: Cen Zhang (Microsoft) Acked-by: Jamal Hadi Salim Tested-by: Jamal Hadi Salim Tested-by: Victor Nogueira Link: https://patch.msgid.link/20260723044955.89471-1-blbllhy@gmail.com Signed-off-by: Paolo Abeni --- net/sched/cls_u32.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 8f30cc82181d..ac98b1c2144a 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1107,6 +1107,13 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, goto erridr; } + if (s->offshift >= 16) { + NL_SET_ERR_MSG_MOD(extack, + "offshift must be less than 16"); + err = -EINVAL; + goto erridr; + } + n = kzalloc_flex(*n, sel.keys, s->nkeys); if (n == NULL) { err = -ENOBUFS; -- cgit v1.2.3 From f621d6ebeebb6374342571e4ddf45fdbc420f6cd Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Thu, 23 Jul 2026 18:54:54 +0800 Subject: net/smc: fix socket use-after-free during link group termination __smc_lgr_terminate() drops conns_lock after finding a connection in lgr->conns_all, but before taking a reference on its socket. The connection is embedded in the socket, and its registration reference protects it only while the connection remains in the tree. A concurrent close can unregister the connection and drop that reference, freeing the socket before the termination worker reaches sock_hold(). The race is reachable when close overlaps link group termination. Local stress testing reproduced the use-after-free and KASAN reported: BUG: KASAN: slab-use-after-free in __smc_lgr_terminate.part.0 [smc] Write of size 4 by task kworker/3:3 Workqueue: events smc_lgr_terminate_work [smc] __smc_lgr_terminate.part.0 [smc] The socket was allocated by smc_create(), freed through slab_free_after_rcu_debug(), and was followed by: refcount_t: addition on 0; use-after-free. __smc_lgr_terminate.part.0 [smc] Take the socket reference while conns_lock still protects the tree entry. The unregister path then cannot drop the last reference until termination has finished using the socket. Fixes: 69318b5215f2 ("net/smc: improve abnormal termination locking") Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Reviewed-by: Mahanta Jambigi Link: https://patch.msgid.link/20260723105454.87016-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni --- net/smc/smc_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index cf6b620fef05..b4208cb186c5 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1572,10 +1572,10 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft) read_lock_bh(&lgr->conns_lock); node = rb_first(&lgr->conns_all); while (node) { - read_unlock_bh(&lgr->conns_lock); conn = rb_entry(node, struct smc_connection, alert_node); smc = container_of(conn, struct smc_sock, conn); sock_hold(&smc->sk); /* sock_put below */ + read_unlock_bh(&lgr->conns_lock); lock_sock(&smc->sk); smc_conn_kill(conn, soft); release_sock(&smc->sk); -- cgit v1.2.3 From 88c17de85ddb459c3fe1e3c65d61fa366b1cf0a8 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Thu, 23 Jul 2026 14:04:45 +0800 Subject: bpf: lwt: Fix dst reference leak on reroute failure bpf_lwt_xmit_reroute() obtains a referenced dst from the route lookup. When skb_cow_head() fails before that dst is installed on the skb, the error path only frees the skb. The skb still owns its previous dst, so the newly looked up dst reference is leaked. Release the new dst reference before freeing the skb on this error path. Fixes: 3bd0b15281af ("bpf: add handling of BPF_LWT_REROUTE to lwt_bpf.c") Cc: stable@vger.kernel.org Signed-off-by: Xuanqiang Luo Link: https://patch.msgid.link/20260723060445.21926-1-xuanqiang.luo@linux.dev Signed-off-by: Paolo Abeni --- net/core/lwt_bpf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index bf588f508b79..652952d416f2 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -255,8 +255,10 @@ static int bpf_lwt_xmit_reroute(struct sk_buff *skb) * if there is enough header space in skb. */ err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); - if (unlikely(err)) + if (unlikely(err)) { + dst_release(dst); goto err; + } skb_dst_drop(skb); skb_dst_set(skb, dst); -- cgit v1.2.3 From 080695e6f005e2396f1207fd69d24c442cb230c6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 24 Jul 2026 09:11:37 +0000 Subject: net: udp_tunnel: fix memory leak in udp_tunnel_nic_unregister() syzbot reported a memory leak [1] in the UDP tunnel NIC offload code. When device registration fails (e.g. in register_netdevice()), netdev core unwinds by sending a single NETDEV_UNREGISTER notification. If work was queued during NETDEV_REGISTER (utn->work_pending is set), udp_tunnel_nic_unregister() returns early: if (utn->work_pending) return; Because failed registrations do not enter netdev_wait_allrefs_any(), no subsequent NETDEV_UNREGISTER rebroadcast will ever occur. As a result, the struct udp_tunnel_nic allocated in udp_tunnel_nic_alloc() is leaked permanently. Fix this by removing the early return. Instead, synchronously cancel any pending work with cancel_delayed_work_sync() before freeing @utn. To be able to call cancel_delayed_work_sync() while holding RTNL (the work also needs RTNL), switch udp_tunnel_nic_device_sync_work() to rtnl_trylock(). If RTNL is contended, requeue the work with a 1 jiffy delay (via queue_delayed_work()) to prevent high CPU contention while waiting for RTNL lock. The utn->work_pending bookkeeping is no longer needed and is removed, as the workqueue core already tracks the pending/running state of the work. [1] BUG: memory leak unreferenced object 0xffff888127d5f840 (size 96): comm "syz-executor", pid 5806, jiffies 4294942188 backtrace (crc 99fdb6c8): __kmalloc_noprof+0x3bf/0x550 udp_tunnel_nic_alloc net/ipv4/udp_tunnel_nic.c:756 [inline] udp_tunnel_nic_register net/ipv4/udp_tunnel_nic.c:833 [inline] udp_tunnel_nic_netdevice_event+0x804/0xab0 net/ipv4/udp_tunnel_nic.c:931 notifier_call_chain+0x59/0x160 kernel/notifier.c:85 call_netdevice_notifiers_info+0x7d/0xb0 net/core/dev.c:2250 register_netdevice+0xc10/0xeb0 net/core/dev.c:11478 Fixes: cc4e3835eff4 ("udp_tunnel: add central NIC RX port offload infrastructure") Reported-by: syzbot+eca845fb8c18dd6b44c1@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a632b15.dde6c935.cf6c8.0011.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260724091137.1792543-1-edumazet@google.com Signed-off-by: Paolo Abeni --- net/ipv4/udp_tunnel_nic.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c index 3b32a0afa979..53a1a9c1f8bf 100644 --- a/net/ipv4/udp_tunnel_nic.c +++ b/net/ipv4/udp_tunnel_nic.c @@ -32,13 +32,12 @@ struct udp_tunnel_nic_table_entry { * @lock: protects all fields * @need_sync: at least one port start changed * @need_replay: space was freed, we need a replay of all ports - * @work_pending: @work is currently scheduled * @n_tables: number of tables under @entries * @missed: bitmap of tables which overflown * @entries: table of tables of ports currently offloaded */ struct udp_tunnel_nic { - struct work_struct work; + struct delayed_work work; struct net_device *dev; @@ -46,7 +45,6 @@ struct udp_tunnel_nic { u8 need_sync:1; u8 need_replay:1; - u8 work_pending:1; unsigned int n_tables; unsigned long missed; @@ -301,11 +299,10 @@ __udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) static void udp_tunnel_nic_device_sync(struct net_device *dev, struct udp_tunnel_nic *utn) { - if (!utn->need_sync || utn->work_pending) + if (!utn->need_sync) return; - queue_work(udp_tunnel_nic_workqueue, &utn->work); - utn->work_pending = 1; + queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 0); } static bool @@ -731,12 +728,17 @@ udp_tunnel_nic_replay(struct net_device *dev, struct udp_tunnel_nic *utn) static void udp_tunnel_nic_device_sync_work(struct work_struct *work) { struct udp_tunnel_nic *utn = - container_of(work, struct udp_tunnel_nic, work); + container_of(work, struct udp_tunnel_nic, work.work); - rtnl_lock(); + /* We cannot block on RTNL here, otherwise we would deadlock with + * udp_tunnel_nic_unregister() calling cancel_delayed_work_sync() + * while holding RTNL. Requeue with 1 jiffy delay if RTNL is contended. + */ + if (!rtnl_trylock()) { + queue_delayed_work(udp_tunnel_nic_workqueue, &utn->work, 1); + return; + } mutex_lock(&utn->lock); - - utn->work_pending = 0; __udp_tunnel_nic_device_sync(utn->dev, utn); if (utn->need_replay) @@ -757,7 +759,7 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info, if (!utn) return NULL; utn->n_tables = n_tables; - INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work); + INIT_DELAYED_WORK(&utn->work, udp_tunnel_nic_device_sync_work); mutex_init(&utn->lock); for (i = 0; i < n_tables; i++) { @@ -901,11 +903,11 @@ udp_tunnel_nic_unregister(struct net_device *dev, struct udp_tunnel_nic *utn) udp_tunnel_nic_flush(dev, utn); udp_tunnel_nic_unlock(dev); - /* Wait for the work to be done using the state, netdev core will - * retry unregister until we give up our reference on this device. + /* Make sure no work is running or queued before freeing @utn. + * The work handler uses rtnl_trylock(), so it will not deadlock + * against the RTNL we are holding here. */ - if (utn->work_pending) - return; + cancel_delayed_work_sync(&utn->work); udp_tunnel_nic_free(utn); release_dev: -- cgit v1.2.3 From 6395789e4739aa5177bbec0fa0f07ccc38d249b0 Mon Sep 17 00:00:00 2001 From: Jose Maria Casanova Crespo Date: Mon, 27 Jul 2026 11:32:28 -0300 Subject: drm/vc4: Supply the overflow slot size in BPOS, not the whole bin BO size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vc4_overflow_mem_work() points BPOA at a 512KB slot inside the 16MB binner BO, but writes the size of the whole BO to BPOS. On every binner out-of-memory event the PTB is therefore authorized to write tile lists across all the other slots (which may hold the tile state, tile alloc and overflow memory of in-flight jobs) and, for any slot but the first, past the end of the binner BO into unrelated CMA memory. Since CMA pages are recycled into page cache and user allocations, this is arbitrary memory corruption by GPU DMA. In practice it shows up as GPU hangs with corrupted control list pointers, userspace heap corruption, a GPU that stays permanently wedged after the first hang, and occasional full system crashes, whenever a job overflows the initial binner slot. The bug dates back to the conversion from a dedicated overflow BO (where writing the full BO size was correct) to the slotted binner BO. Fixes: 553c942f8b2c ("drm/vc4: Allow using more than 256MB of CMA memory.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Jose Maria Casanova Crespo Reviewed-by: Maíra Canal Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260727-vc4-bin-oom-fixes-v2-1-0d8a5eddc7c9@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 8e5141bb5075..7877d493d80e 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -104,7 +104,7 @@ vc4_overflow_mem_work(struct work_struct *work) vc4->bin_alloc_overflow = BIT(bin_bo_slot); V3D_WRITE(V3D_BPOA, bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size); - V3D_WRITE(V3D_BPOS, bo->base.base.size); + V3D_WRITE(V3D_BPOS, vc4->bin_alloc_size); V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); spin_unlock_irqrestore(&vc4->job_lock, irqflags); -- cgit v1.2.3 From 48a570c964d8e37d353381e4195106277e17f5cb Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Mon, 27 Jul 2026 11:32:29 -0300 Subject: drm/vc4: Zero the tile state data array before each BIN job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The binner BO is a single 16MB buffer split into 512KB slots that are handed out to jobs at submission time and recycled as jobs complete, without ever being cleared. Each slot holds the job's Tile State Data Array (TSDA) at its start, followed by the tile allocation pool. While the tile allocation pool is only walked by the render thread through branches the binner generated during the current job, the TSDA is the PTB's own per-tile bookkeeping and is consumed by the hardware itself. Although the kernel sets the "Auto-initialise Tile State Data Array" flag in the tile binning mode configuration, the PTB demonstrably still acts on stale tile state left by the slot's previous user: the binner ends up creating invalid command streams with invalid primitive streams and branches, which can cause GPU hangs as observed in [1][2]. Zero the TSDA when the job's binning slot is configured. This clears 48 bytes per tile (~24KB for a 1080p frame) in the submission path, and guarantees the PTB never sees another job's tile state. The tile count is only checked for being non-zero today, so the 8-bit fields it comes from can describe a tile state array almost six times larger than the slot it has to live in. Bound it before the slot is handed out, since such size decides how much of the slot is left for the tile alloc pool. Link: https://github.com/raspberrypi/linux/issues/3221 [1] Link: https://github.com/raspberrypi/linux/issues/5780 [2] Fixes: 553c942f8b2c ("drm/vc4: Allow using more than 256MB of CMA memory.") Cc: stable@vger.kernel.org Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260727-vc4-bin-oom-fixes-v2-2-0d8a5eddc7c9@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_validate.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 7f2fadfde7a8..d2a65c968b1f 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -385,6 +385,23 @@ validate_tile_binning_config(VALIDATE_ARGS) return -EINVAL; } + /* The tile state data array is 48 bytes per tile, and we put it at + * the start of a BO containing both it and the tile alloc. + */ + tile_state_size = 48 * tile_count; + + /* Since the tile alloc array will follow us, align. */ + tile_state_size = roundup(tile_state_size, 4096); + + /* Reject configurations whose tile state would leave no room for + * the tile alloc pool that follows it in the slot. + */ + if (tile_state_size >= vc4->bin_alloc_size) { + DRM_DEBUG("Tile binning config of %dx%d too large\n", + exec->bin_tiles_x, exec->bin_tiles_y); + return -EINVAL; + } + bin_slot = vc4_v3d_get_bin_slot(vc4); if (bin_slot < 0) { if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) { @@ -400,13 +417,13 @@ validate_tile_binning_config(VALIDATE_ARGS) exec->bin_slots |= BIT(bin_slot); bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size; - /* The tile state data array is 48 bytes per tile, and we put it at - * the start of a BO containing both it and the tile alloc. - */ - tile_state_size = 48 * tile_count; + exec->tile_alloc_offset = bin_addr + tile_state_size; - /* Since the tile alloc array will follow us, align. */ - exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096); + /* The TSDA area must be zeroed out before use, otherwise the PTB might + * consume a stale tile state. + */ + memset(vc4->bin_bo->base.vaddr + bin_slot * vc4->bin_alloc_size, 0, + tile_state_size); *(uint8_t *)(validated + 14) = ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK | -- cgit v1.2.3 From 2f067f5a450ea07efd249142a11d940a068fe29c Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Tue, 28 Jul 2026 19:21:56 +0800 Subject: wifi: mac80211: fix tid_tx use-after-free on BA session stop ieee80211_stop_tx_ba_cb() hands tid_tx to kfree_rcu() through ieee80211_remove_tid_tx(), and then reads tid_tx->ndp after dropping sta->lock: ieee80211_remove_tid_tx(sta, tid); /* kfree_rcu(tid_tx, rcu_head) */ ... spin_unlock_bh(&sta->lock); if (start_txq) ieee80211_agg_start_txq(sta, tid, false); if (send_delba) ieee80211_send_delba(..., tid_tx->ndp); That read is not covered by an RCU read-side critical section, and it runs in preemptible process context: both callers hold the wiphy mutex, reaching it either from the ieee80211_ba_session_work() wiphy work or from ieee80211_sta_tear_down_BA_sessions() during station teardown. Softirqs can run in that window too, both from the local_bh_enable() that ends ieee80211_agg_start_txq() and from any interrupt exit, so the RCU callback can free tid_tx before the read. Driving the function from a test module with the grace period forced into that window, KASAN reports the read, and the free arrives on the ordinary RCU softirq path: BUG: KASAN: slab-use-after-free in ieee80211_stop_tx_ba_cb+0x3cd/0x400 Read of size 1 at addr ffff888002b9f52e by task kworker/0:1/10 [...] Freed by task 57: __kasan_slab_free+0x47/0x70 __rcu_free_sheaf_prepare+0x70/0x250 rcu_free_sheaf_nobarn+0x18/0x40 rcu_core+0x426/0x1310 handle_softirqs+0x144/0x590 __irq_exit_rcu+0xea/0x150 irq_exit_rcu+0x9/0x20 sysvec_apic_timer_interrupt+0x6b/0x80 asm_sysvec_apic_timer_interrupt+0x1a/0x20 send_delba is only set when tx_stop is set, which happens for AGG_STOP_LOCAL_REQUEST alone, so this is reached on local teardown - session idle timeout, PTK rekey, suspend, HW reconfig - and not from a peer's DELBA. Read ndp into a local before the session is freed, while sta->lock is still held. tid_tx->ndp has a single writer, in ieee80211_tx_ba_session_handle_start(), which cannot run concurrently here: both paths are serialised by the wiphy mutex, and the session is already marked HT_AGG_STATE_STOPPING at this point. tid_tx->ndp is also the only tid_tx dereference left after ieee80211_remove_tid_tx() in this function. Fixes: 98acd4c1d9f7 ("wifi: mac80211: add support for NDP ADDBA/DELBA for S1G") Assisted-by: Codex:gpt-5.6-sol Assisted-by: Kimi:K3 Cc: stable@vger.kernel.org Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260728112156.96822-1-enderaoelyther@gmail.com [move/change the comment a bit to be more general not just on ndp, initialize ndp directly] Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 4833b46770b6..0832213430f4 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -915,6 +915,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx) { struct ieee80211_sub_if_data *sdata = sta->sdata; + bool ndp = ndp = tid_tx->ndp; bool send_delba = false; bool start_txq = false; @@ -934,6 +935,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid, send_delba = true; ieee80211_remove_tid_tx(sta, tid); + /* tid_tx is now invalid since ieee80211_remove_tid_tx() frees it */ start_txq = true; unlock_sta: @@ -946,7 +948,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid, ieee80211_send_delba(sdata, sta->sta.addr, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE, - tid_tx->ndp); + ndp); } void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, -- cgit v1.2.3 From a2f5286ca4f304d3fd469f01b96b518608912a5c Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sat, 25 Jul 2026 19:50:28 +0530 Subject: wifi: cfg80211: validate IEs in cfg80211_wext_siwgenie() The KASAN allocation trace shows that a malformed IE buffer is stored via SIOCSIWGENIE (cfg80211_wext_siwgenie()) without any validation. The crash trace shows that a subsequent SIOCSIWESSID triggers a connection attempt which calls cfg80211_sme_get_conn_ies() to process the stored IE buffer, causing: - An out-of-bounds read in skip_ie() which reads ies[pos+1] (the length byte) past the end of the 1-byte buffer. - An integer underflow in the memcpy size argument when offs returned by ieee80211_ie_split() exceeds ies_len, causing unsigned subtraction to wrap to SIZE_MAX and triggering a fortify panic. Fix this by validating the IE buffer in cfg80211_wext_siwgenie() before storing it. Reported-by: syzbot+cc867e537e4bd36f69bb@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=cc867e537e4bd36f69bb Signed-off-by: Deepanshu Kartikey Link: https://patch.msgid.link/20260725142028.32560-1-kartikey406@gmail.com [drop unnecessary ie_len check, update commit message] Signed-off-by: Johannes Berg --- net/wireless/wext-sme.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 573b6b15a446..b5914f3658db 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -319,6 +319,15 @@ int cfg80211_wext_siwgenie(struct net_device *dev, return 0; if (ie_len) { + const struct element *elem; + + for_each_element(elem, extra, ie_len) { + /* nothing */ + } + + if (!for_each_element_completed(elem, extra, ie_len)) + return -EINVAL; + ie = kmemdup(extra, ie_len, GFP_KERNEL); if (!ie) return -ENOMEM; -- cgit v1.2.3 From 99a948382af8a225e2d5e54a7052158cd6281cc6 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Tue, 28 Jul 2026 19:53:25 +0800 Subject: wifi: mwifiex: use the subframe length when parsing A-MSDU TDLS frames mwifiex_11n_dispatch_amsdu_pkt() splits an A-MSDU with ieee80211_amsdu_to_8023s() and walks the resulting subframes. For each subframe it passes the subframe data pointer to mwifiex_process_tdls_action_frame(), but pairs it with skb->len, the length of the A-MSDU parent, instead of rx_skb->len: rx_skb = __skb_dequeue(&list); rx_hdr = (struct rx_packet_hdr *)rx_skb->data; if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { mwifiex_process_tdls_action_frame(priv, (u8 *)rx_hdr, skb->len); } The parent is not a valid description of that buffer, and may not be valid memory at all. ieee80211_amsdu_to_8023s() ends with if (!reuse_skb) dev_kfree_skb(skb); and it only sets reuse_skb when the parent is linear, is not a head_frag, and is being consumed as the *last* subframe. So when the parent does not qualify for reuse it has already been freed, and the read of skb->len is a use-after-free. When it is reused, skb->len is the length of the last subframe, applied to every earlier subframe, which over-states the buffer whenever an earlier subframe is shorter. The callee cannot absorb a wrong length, because it derives its own ceiling from the value it is given. Each frame type computes ies_len = len - sizeof(struct ethhdr) - TDLS_*_FIX_LEN; and the element walk is then bounded entirely against that ceiling, for (end = pos + ies_len; pos + 1 < end; pos += 2 + pos[1]) { u8 ie_len = pos[1]; if (pos + 2 + ie_len > end) break; so a too-large len moves end past the end of the subframe and the walk reads and copies beyond it. The A-MSDU layout is chosen by the sender, which makes the difference between the last subframe and a shorter earlier one remotely selectable. Reaching this requires TDLS support in firmware and the TDLS ethertype on the subframe. The other caller, mwifiex_process_rx_packet(), is correct: it passes a pointer and a length that describe the same region of the RX buffer. Pass rx_skb->len, the length of the subframe actually being parsed. Fixes: 776f742040ca ("mwifiex: fix AMPDU not setup on TDLS link problem") Assisted-by: Codex:gpt-5.6-sol Assisted-by: Kimi:K3 Cc: stable@vger.kernel.org Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260728115325.19128-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index 610ec8302adf..9deb47f22a61 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -44,7 +44,7 @@ static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { mwifiex_process_tdls_action_frame(priv, (u8 *)rx_hdr, - skb->len); + rx_skb->len); } if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) -- cgit v1.2.3 From 04513922958005046f8b481c0f77212c556a9c38 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Fri, 24 Jul 2026 04:22:23 +0800 Subject: wifi: cfg80211: publish PMSR request before starting the driver nl80211_pmsr_start() assigns the request cookie, calls the driver's ->start_pmsr() callback, and only then adds the request to wdev->pmsr_list, without holding pmsr_lock for the addition. mac80211_hwsim saves the request in its start callback and returns. Since nl80211 uses parallel_ops, an immediate REPORT_PMSR can then run before nl80211_pmsr_start() reaches its post-start list_add_tail(). hwsim also dispatches reports from its virtio receive workqueue. Completion removes the request from wdev->pmsr_list under pmsr_lock and frees it. Thus completion can precede publication, race the unlocked list mutation, or free the request before nl80211_pmsr_start() reads req->cookie for the netlink reply. Add the request to wdev->pmsr_list under pmsr_lock before calling the driver, and use a cookie value saved before the call so the request is not dereferenced after a successful start. On an error return the driver has not retained or completed the request, so remove it from the list under the lock and free it. Fixes: 9bb7e0f24e7e ("cfg80211: add peer measurement with FTM initiator API") Link: https://lore.kernel.org/all/20260723010916.76433-1-enderaoelyther@gmail.com/ Assisted-by: Codex:gpt-5 Assisted-by: Claude:opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260723202223.99661-1-enderaoelyther@gmail.com Signed-off-by: Johannes Berg --- net/wireless/pmsr.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index d1e2fae5bc0e..97449bcb9a22 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -420,6 +420,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) const struct cfg80211_pmsr_capabilities *capa; struct cfg80211_pmsr_request *req; struct nlattr *peers, *peer; + u64 cookie; capa = rdev->wiphy.pmsr_capa; @@ -521,14 +522,27 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) } req->cookie = cfg80211_assign_cookie(rdev); req->nl_portid = info->snd_portid; + cookie = req->cookie; + + /* + * Add to the list before the driver call; under races or broken + * drivers, completion may free the request before rdev_start_pmsr() + * returns. Use the saved cookie below. + */ + spin_lock_bh(&wdev->pmsr_lock); + list_add_tail(&req->list, &wdev->pmsr_list); + spin_unlock_bh(&wdev->pmsr_lock); err = rdev_start_pmsr(rdev, wdev, req); - if (err) + if (err) { + /* An error return leaves the request owned by this path. */ + spin_lock_bh(&wdev->pmsr_lock); + list_del(&req->list); + spin_unlock_bh(&wdev->pmsr_lock); goto out_err; + } - list_add_tail(&req->list, &wdev->pmsr_list); - - nl_set_extack_cookie_u64(info->extack, req->cookie); + nl_set_extack_cookie_u64(info->extack, cookie); return 0; out_err: kfree(req); -- cgit v1.2.3 From 0502d5077e419427d80f4d46ba95d0067f5fb916 Mon Sep 17 00:00:00 2001 From: Zhao Li Date: Thu, 23 Jul 2026 09:09:28 +0800 Subject: wifi: mac80211: validate individual TWT params before driver setup ieee80211_process_rx_twt_action() only partially validates a received S1G TWT setup frame before queueing it. An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() with twt->length too short for the full struct ieee80211_twt_params. The individual path passes twt to drv_add_twt_setup(). Both the tracepoint and the driver callback consume the complete parameters block, not merely req_type. Do not pass a short individual agreement to the driver. Broadcast agreements remain unchanged because they are rejected locally after accessing only req_type. Fixes: f5a4c24e689f ("mac80211: introduce individual TWT support in AP mode") Assisted-by: Codex:gpt-5 Assisted-by: Claude:opus-4.8 Signed-off-by: Zhao Li Link: https://patch.msgid.link/20260723010928.76551-1-enderaoelyther@gmail.com [edit commit message to not overclaim lack of validation nor understate driver impact] Signed-off-by: Johannes Berg --- net/mac80211/s1g.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c index 5af4a0c6c642..abc338e22e59 100644 --- a/net/mac80211/s1g.c +++ b/net/mac80211/s1g.c @@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata, struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.s1g.variable; struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) && + twt->length < sizeof(twt->control) + sizeof(*twt_agrt)) + return; + twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST); /* broadcast TWT not supported yet */ -- cgit v1.2.3 From 57aa1718d5953dd532137d43b696c68545c2e0b3 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 24 Jul 2026 11:55:45 +0200 Subject: wifi: iwlegacy: replace BUG_ON() with WARN_ON() on num_stations check BUG_ON() for il->num_stations < 0 can happen in real word, see https://bugzilla.kernel.org/show_bug.cgi?id=221733 Replace BUG_ON() with WARN_ON() (and reset the counter to 0) to do not put whole system to inconsistent state on the condition. Also allocate debugfs buffer for all stations (32 or 25) to do not use num_stations since it might not be right. Signed-off-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260724095545.33647-1-stf_xl@wp.pl [clarify commit message wrt. debugfs buffer] Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlegacy/common.c | 7 ++++--- drivers/net/wireless/intel/iwlegacy/debug.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c index 8d0ff339ad08..0bb807ff8edf 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.c +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -2179,8 +2179,8 @@ il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr) il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; il->num_stations--; - - BUG_ON(il->num_stations < 0); + if (WARN_ON(il->num_stations < 0)) + il->num_stations = 0; spin_unlock_irqrestore(&il->sta_lock, flags); @@ -2328,7 +2328,8 @@ il_dealloc_bcast_stations(struct il_priv *il) il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; il->num_stations--; - BUG_ON(il->num_stations < 0); + if (WARN_ON(il->num_stations < 0)) + il->num_stations = 0; kfree(il->stations[i].lq); il->stations[i].lq = NULL; } diff --git a/drivers/net/wireless/intel/iwlegacy/debug.c b/drivers/net/wireless/intel/iwlegacy/debug.c index d998a3f1b056..8a9f79ff1c6e 100644 --- a/drivers/net/wireless/intel/iwlegacy/debug.c +++ b/drivers/net/wireless/intel/iwlegacy/debug.c @@ -396,7 +396,7 @@ il_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, int i, j, pos = 0; ssize_t ret; /* Add 30 for initial string */ - const size_t bufsz = 30 + sizeof(char) * 500 * (il->num_stations); + const size_t bufsz = 30 + sizeof(char) * 500 * max_sta; buf = kmalloc(bufsz, GFP_KERNEL); if (!buf) -- cgit v1.2.3 From a81fc9266e1c5fef9ccf675a9b44b2f4ab464923 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:12 +0100 Subject: netfs: clear PG_private_2 on copy-to-cache append failure netfs_pgpriv2_copy_to_cache() marks the folio with PG_private_2 before netfs_pgpriv2_copy_folio() appends it to the copy-to-cache rolling buffer. If the append fails, the folio is not queued for cache writeback, so the PG_private_2 state and its reference must be released immediately. Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-2-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/read_pgpriv2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c index a1489aa29f78..7eacc58abadb 100644 --- a/fs/netfs/read_pgpriv2.c +++ b/fs/netfs/read_pgpriv2.c @@ -54,6 +54,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio /* Attach the folio to the rolling buffer. */ if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { + folio_end_private_2(folio); clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); return; } -- cgit v1.2.3 From 37a1c535c80c67d98668d190c7432f9ebda43310 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:13 +0100 Subject: netfs: handle single writeback rolling buffer allocation failure netfs_write_folio_single() takes an extra folio reference before appending the folio to the rolling buffer. rolling_buffer_append() can fail if it cannot allocate another folio_queue. Check the return value and drop the extra folio reference before returning the error. Fixes: 49866ce7ea8d ("netfs: Add support for caching single monolithic objects such as AFS dirs") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-3-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/write_issue.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index f2761c99795a..14efe4cb9393 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -720,6 +720,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, size_t iter_off = 0; size_t fsize = folio_size(folio), flen; loff_t fpos = folio_pos(folio); + ssize_t ret; bool to_eof = false; bool no_debug = false; @@ -748,7 +749,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, /* Attach the folio to the rolling buffer. */ folio_get(folio); - rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); + ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); + if (ret < 0) { + folio_put(folio); + return ret; + } /* Move the submission point forward to allow for write-streaming data * not starting at the front of the page. We don't do write-streaming -- cgit v1.2.3 From 87eb3d272dcbcbbfe5c1576c10e5dc72810cf1f6 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:14 +0100 Subject: netfs: release readahead folios on iterator preparation failure netfs_prepare_read_iterator() batches readahead folios in put_batch so that the folio references can be dropped after the I/O iterator has been prepared. If rolling_buffer_load_from_ra() fails after earlier folios have been batched, the function returns immediately and leaves those references held. Release the batch before returning the error. Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-4-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/buffered_read.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 24a8a5418e31..3d86414ee40f 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, added = rolling_buffer_load_from_ra(&rreq->buffer, ractl, &put_batch); - if (added < 0) + if (added < 0) { + folio_batch_release(&put_batch); return added; + } rreq->submitted += added; } folio_batch_release(&put_batch); -- cgit v1.2.3 From 1d78d56c43ef3768183e8370e7367b162700e049 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 27 Jul 2026 14:07:15 +0100 Subject: netfs: Fix folio_queue ENOMEM in writeback by adding a mempool Fix the handling of folio_queue allocation failure in writeback by adding a mempool and passing in gfp_t flags to the rolling buffer functions that allocate memory, using the mempool if gfp != GFP_KERNEL. This is then extended upwards and the gfp to be used for a request is stored in the netfs_io_request struct and is then used for both requests and subrequests, eliminating the sleeping loops there. The failure caused: folio != NULL WARNING: fs/netfs/write_issue.c:603 at netfs_writepages+0x883/0xa10 fs/netfs/write_issue.c:603, CPU#3: syz.0.17/5919 Fixes: cd0277ed0c18 ("netfs: Use new folio_queue data type and iterator instead of xarray iter") Reported-by: syzbot+0da43efa72f88bd3a8af@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0da43efa72f88bd3a8af Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-5-dhowells@redhat.com Tested-by: syzbot+0da43efa72f88bd3a8af@syzkaller.appspotmail.com cc: Paulo Alcantara cc: Yun Zhou cc: Matthew Wilcox cc: Christoph Hellwig cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/buffered_read.c | 6 +++--- fs/netfs/internal.h | 1 + fs/netfs/main.c | 7 +++++++ fs/netfs/objects.c | 30 +++++++++++++++++------------- fs/netfs/read_pgpriv2.c | 2 +- fs/netfs/rolling_buffer.c | 22 +++++++++++++--------- fs/netfs/write_issue.c | 10 +++++----- include/linux/netfs.h | 1 + include/linux/rolling_buffer.h | 6 +++--- 9 files changed, 51 insertions(+), 34 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 3d86414ee40f..7fdfa4f27e34 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -361,7 +361,7 @@ void netfs_readahead(struct readahead_control *ractl) netfs_rreq_expand(rreq, ractl); rreq->submitted = rreq->start; - if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) + if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0) goto cleanup_free; netfs_read_to_pagecache(rreq, ractl); @@ -380,10 +380,10 @@ static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct fo { ssize_t added; - if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) + if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0) return -ENOMEM; - added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags); + added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags, rreq->gfp); if (added < 0) return added; rreq->submitted = rreq->start + added; diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index d889caa401dc..420ee7b26580 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -43,6 +43,7 @@ extern struct list_head netfs_io_requests; extern spinlock_t netfs_proc_lock; extern mempool_t netfs_request_pool; extern mempool_t netfs_subrequest_pool; +extern mempool_t netfs_folioq_pool; #ifdef CONFIG_PROC_FS static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq) diff --git a/fs/netfs/main.c b/fs/netfs/main.c index 73da6c9f5777..927badf3989d 100644 --- a/fs/netfs/main.c +++ b/fs/netfs/main.c @@ -28,6 +28,7 @@ static struct kmem_cache *netfs_request_slab; static struct kmem_cache *netfs_subrequest_slab; mempool_t netfs_request_pool; mempool_t netfs_subrequest_pool; +mempool_t netfs_folioq_pool; #ifdef CONFIG_PROC_FS LIST_HEAD(netfs_io_requests); @@ -108,6 +109,9 @@ static int __init netfs_init(void) { int ret = -ENOMEM; + if (mempool_init_kmalloc_pool(&netfs_folioq_pool, 100, sizeof(struct folio_queue)) < 0) + goto error_folioq_pool; + netfs_request_slab = kmem_cache_create("netfs_request", sizeof(struct netfs_io_request), 0, SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, @@ -160,6 +164,8 @@ error_subreq: error_reqpool: kmem_cache_destroy(netfs_request_slab); error_req: + mempool_exit(&netfs_folioq_pool); +error_folioq_pool: return ret; } fs_initcall(netfs_init); @@ -172,5 +178,6 @@ static void __exit netfs_exit(void) kmem_cache_destroy(netfs_subrequest_slab); mempool_exit(&netfs_request_pool); kmem_cache_destroy(netfs_request_slab); + mempool_exit(&netfs_folioq_pool); } module_exit(netfs_exit); diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c index b8c4918d3dcd..01461a74642d 100644 --- a/fs/netfs/objects.c +++ b/fs/netfs/objects.c @@ -7,7 +7,6 @@ #include #include -#include #include "internal.h" static void netfs_free_request(struct work_struct *work); @@ -26,17 +25,23 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping, struct netfs_io_request *rreq; mempool_t *mempool = ctx->ops->request_pool ?: &netfs_request_pool; struct kmem_cache *cache = mempool->pool_data; + gfp_t gfp = GFP_KERNEL; int ret; - for (;;) { - rreq = mempool_alloc(mempool, GFP_KERNEL); - if (rreq) - break; - msleep(10); + /* Writeback is part of memory reclaim and must not fail due to ENOMEM. */ + if (origin == NETFS_WRITEBACK || origin == NETFS_WRITEBACK_SINGLE) { + gfp = GFP_NOFS; /* Allows use of mempools. */ + + rreq = mempool_alloc(mempool, gfp); + } else { + rreq = mempool->alloc(gfp, mempool->pool_data); + if (!rreq) + return ERR_PTR(-ENOMEM); } memset(rreq, 0, kmem_cache_size(cache)); INIT_WORK(&rreq->cleanup_work, netfs_free_request); + rreq->gfp = gfp; rreq->start = start; rreq->len = len; rreq->origin = origin; @@ -200,13 +205,12 @@ struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq mempool_t *mempool = rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool; struct kmem_cache *cache = mempool->pool_data; - for (;;) { - subreq = mempool_alloc(rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool, - GFP_KERNEL); - if (subreq) - break; - msleep(10); - } + if (rreq->gfp == GFP_KERNEL) + subreq = mempool->alloc(rreq->gfp, mempool->pool_data); + else + subreq = mempool_alloc(mempool, rreq->gfp); + if (!subreq) + return NULL; memset(subreq, 0, kmem_cache_size(cache)); INIT_WORK(&subreq->work, NULL); diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c index 7eacc58abadb..c31190993b76 100644 --- a/fs/netfs/read_pgpriv2.c +++ b/fs/netfs/read_pgpriv2.c @@ -53,7 +53,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio trace_netfs_folio(folio, netfs_folio_trace_store_copy); /* Attach the folio to the rolling buffer. */ - if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { + if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) { folio_end_private_2(folio); clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); return; diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c index a17fbf9853a4..8c0026836f9c 100644 --- a/fs/netfs/rolling_buffer.c +++ b/fs/netfs/rolling_buffer.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -27,7 +28,10 @@ struct folio_queue *netfs_folioq_alloc(unsigned int rreq_id, gfp_t gfp, { struct folio_queue *fq; - fq = kmalloc_obj(*fq, gfp); + if (gfp == GFP_KERNEL) + fq = netfs_folioq_pool.alloc(gfp, netfs_folioq_pool.pool_data); + else + fq = mempool_alloc(&netfs_folioq_pool, gfp); if (fq) { netfs_stat(&netfs_n_folioq); folioq_init(fq, rreq_id); @@ -50,7 +54,7 @@ void netfs_folioq_free(struct folio_queue *folioq, { trace_netfs_folioq(folioq, trace); netfs_stat_d(&netfs_n_folioq); - kfree(folioq); + mempool_free(folioq, &netfs_folioq_pool); } EXPORT_SYMBOL(netfs_folioq_free); @@ -60,11 +64,11 @@ EXPORT_SYMBOL(netfs_folioq_free); * consumer. */ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, - unsigned int direction) + unsigned int direction, gfp_t gfp) { struct folio_queue *fq; - fq = netfs_folioq_alloc(rreq_id, GFP_NOFS, netfs_trace_folioq_rollbuf_init); + fq = netfs_folioq_alloc(rreq_id, gfp, netfs_trace_folioq_rollbuf_init); if (!fq) return -ENOMEM; @@ -77,14 +81,14 @@ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, /* * Add another folio_queue to a rolling buffer if there's no space left. */ -int rolling_buffer_make_space(struct rolling_buffer *roll) +int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp) { struct folio_queue *fq, *head = roll->head; if (!folioq_full(head)) return 0; - fq = netfs_folioq_alloc(head->rreq_id, GFP_NOFS, netfs_trace_folioq_make_space); + fq = netfs_folioq_alloc(head->rreq_id, gfp, netfs_trace_folioq_make_space); if (!fq) return -ENOMEM; fq->prev = head; @@ -122,7 +126,7 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, int nr, ix, to; ssize_t size = 0; - if (rolling_buffer_make_space(roll) < 0) + if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0) return -ENOMEM; fq = roll->head; @@ -153,12 +157,12 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, * Append a folio to the rolling buffer. */ ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, - unsigned int flags) + unsigned int flags, gfp_t gfp) { ssize_t size = folio_size(folio); int slot; - if (rolling_buffer_make_space(roll) < 0) + if (rolling_buffer_make_space(roll, gfp) < 0) return -ENOMEM; slot = folioq_append(roll->head, folio); diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 14efe4cb9393..2d9cfcd43658 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -108,7 +108,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping, ictx = netfs_inode(wreq->inode); if (is_cacheable) fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx)); - if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0) + if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE, wreq->gfp) < 0) goto nomem; wreq->cleaned_to = wreq->start; @@ -167,7 +167,7 @@ void netfs_prepare_write(struct netfs_io_request *wreq, */ if (iov_iter_is_folioq(wreq_iter) && wreq_iter->folioq_slot >= folioq_nr_slots(wreq_iter->folioq)) - rolling_buffer_make_space(&wreq->buffer); + rolling_buffer_make_space(&wreq->buffer, wreq->gfp); subreq = netfs_alloc_subrequest(wreq); subreq->source = stream->source; @@ -334,7 +334,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, _enter(""); - if (rolling_buffer_make_space(&wreq->buffer) < 0) + if (rolling_buffer_make_space(&wreq->buffer, wreq->gfp) < 0) return -ENOMEM; /* netfs_perform_write() may shift i_size around the page or from out @@ -436,7 +436,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, } /* Attach the folio to the rolling buffer. */ - rolling_buffer_append(&wreq->buffer, folio, 0); + rolling_buffer_append(&wreq->buffer, folio, 0, wreq->gfp); /* Move the submission point forward to allow for write-streaming data * not starting at the front of the page. We don't do write-streaming @@ -749,7 +749,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, /* Attach the folio to the rolling buffer. */ folio_get(folio); - ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); + ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK, wreq->gfp); if (ret < 0) { folio_put(folio); return ret; diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 1bc120d61c5b..d0b62d53eea9 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -255,6 +255,7 @@ struct netfs_io_request { unsigned long long cleaned_to; /* Position we've cleaned folios to */ unsigned long long abandon_to; /* Position to abandon folios to */ const struct folio *no_unlock_folio; /* Don't unlock this folio after read */ + gfp_t gfp; /* GFP flags to use */ unsigned int direct_bv_count; /* Number of elements in direct_bv[] */ unsigned int debug_id; unsigned int rsize; /* Maximum read size (0 for none) */ diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.h index ac15b1ffdd83..9e5dad29669c 100644 --- a/include/linux/rolling_buffer.h +++ b/include/linux/rolling_buffer.h @@ -43,13 +43,13 @@ struct rolling_buffer_snapshot { #define ROLLBUF_MARK_2 BIT(1) int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, - unsigned int direction); -int rolling_buffer_make_space(struct rolling_buffer *roll); + unsigned int direction, gfp_t gfp); +int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp); ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, struct readahead_control *ractl, struct folio_batch *put_batch); ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, - unsigned int flags); + unsigned int flags, gfp_t gfp); struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll); void rolling_buffer_clear(struct rolling_buffer *roll); -- cgit v1.2.3 From 79055d82772b9584f259b747fe40ff56a076678d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 28 Jul 2026 14:26:32 +0200 Subject: binfmt_misc: don't let an 'F' entry pin its own instance An entry registered with 'F' opens its interpreter at registration time and holds that file until the entry is freed. Any entry nobody removes by hand only gets closed once the binfmt_misc superblock is shut down. If the interpreter lives on a mount that keeps that superblock alive the two pin each other: binfmt_misc sb -> inode -> entry -> interp_file -> vfsmount -> binfmt_misc sb TL;DR the file is never closed. Once the mount namespace is gone there is nothing left to unregister through either. There are two ways to trigger this bug: - Point the interpreter at the instance itself. Its files are regular files owned by the mounter and both bm_get_inode() and simple_fill_super() leave i_op at empty_iops. So notify_change() falls back to simple_setattr() and chmod +x works. We never set SB_I_NOEXEC and so open_exec() accepts it. - Use the instance as an overlayfs lower layer. The overlay superblock holds a clone_private_mount() of every layer until it is destroyed and that clone is in no namespace. So umount_tree() never reaches it. That's a DoS. And it isn't only the superblock that leaks. It pins the user namespace it was mounted in, so every iteration permanently eats one of the caller's user namespace charges. So let's just do the sane thing. SB_I_NOEXEC makes open_exec() fail on the instance's own files and s_stack_depth makes overlayfs reject the layer before it ever takes a clone. That also covers the ecryptfs and fuse passthrough variants. What 'F' promises is unchanged. The stable tag is narrower than the Fixes tags on purpose. Before sandboxed mounts this needed global root against the single instance everyone shares, and the change doesn't apply to those trees anyway. Note that SB_I_NODEV is implicitly raised for userns mounts but raise it explicitly here as well. Link: https://patch.msgid.link/20260728-work-binfmt_misc-selfpin-v1-1-74df5daeca5b@kernel.org Fixes: 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers") Fixes: 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts") Cc: stable@vger.kernel.org # v6.7+ Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 5de615ca7a75..47aeb2b68d3e 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -937,6 +937,10 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc) if (WARN_ON(user_ns != current_user_ns())) return -EINVAL; + /* Never exec off this instance and never let anything stack on it. */ + sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; + sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH; + /* * Lazily allocate a new binfmt_misc instance for this namespace, i.e. * do it here during the first mount of binfmt_misc. We don't need to -- cgit v1.2.3 From db1856ea9196cf6e015d12199a34c0b9313c7bfa Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:02 +0200 Subject: binfmt_misc: restore write access when removing an entry Registering an entry with the MISC_FMT_OPEN_FILE flag opens the interpreter via open_exec() which denies write access to it for as long as the entry exists. Removing the entry closes the interpreter file via filp_close() but never restores write access, leaving the inode's i_writecount permanently negative. Opening the interpreter for writing keeps failing with ETXTBSY long after the entry is gone until the inode is evicted from the inode cache. Commit 90f601b497d7 ("binfmt_misc: restore write access before closing files opened by open_exec()") fixed the same imbalance in the error path of bm_register_write() but the actual removal path has been leaking the write denial since the introduction of the flag. Restore write access in put_binfmt_handler() before closing the interpreter file. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-1-a162f7cb58d6@kernel.org Fixes: 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 47aeb2b68d3e..adab06d18550 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -162,8 +162,10 @@ static Node *get_binfmt_handler(struct binfmt_misc *misc, static void put_binfmt_handler(Node *e) { if (refcount_dec_and_test(&e->users)) { - if (e->flags & MISC_FMT_OPEN_FILE) + if (e->flags & MISC_FMT_OPEN_FILE) { + exe_file_allow_write_access(e->interp_file); filp_close(e->interp_file, NULL); + } kfree(e); } } -- cgit v1.2.3 From fa5990ca8fd917003e526036bcc50413edb9722c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:03 +0200 Subject: binfmt_misc: use exe_file_deny_write_access() for the interpreter clone For MISC_FMT_OPEN_FILE entries load_misc_binary() clones the registered interpreter file and denies write access to the clone via plain deny_write_access(). The clone is installed as bprm->interpreter and later released by the exec machinery through exe_file_allow_write_access() which skips the i_writecount increment for files with FMODE_FSNOTIFY_HSM set. The deny and allow side can therefore come to different conclusions when pre-content watches are in play: if a pre-content watch is added to the interpreter after registration every subsequent exec through that entry takes a write denial on the clone that is never paired with a write allowance, driving the interpreter inode's i_writecount further down with each exec and leaving the interpreter unwritable even after the entry and all its users are gone. Take the write denial via exe_file_deny_write_access() so both sides of the pairing base their decision on the same file mode, and propagate failure instead of silently ignoring it: an interpreter that is concurrently open for writing now fails the exec with ETXTBSY, exactly like an interpreter freshly opened via open_exec() would. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-2-a162f7cb58d6@kernel.org Fixes: 0357ef03c94e ("fs: don't block write during exec on pre-content watched files") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index adab06d18550..bf7d6b975825 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -249,8 +249,14 @@ static int load_misc_binary(struct linux_binprm *bprm) if (fmt->flags & MISC_FMT_OPEN_FILE) { interp_file = file_clone_open(fmt->interp_file); - if (!IS_ERR(interp_file)) - deny_write_access(interp_file); + if (!IS_ERR(interp_file)) { + int err = exe_file_deny_write_access(interp_file); + + if (err) { + fput(interp_file); + interp_file = ERR_PTR(err); + } + } } else { interp_file = open_exec(fmt->interpreter); } -- cgit v1.2.3 From 8e85d50ba1117fd446bf9a250bd8a97d48384bdc Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:04 +0200 Subject: binfmt_misc: reject a flag character as the field delimiter The registration string starts with a user chosen delimiter that separates the individual fields. So that the field parsers terminate even on a truncated string create_entry() pads the buffer with that same delimiter: memset(buf + count, del, 8); Most fields are scanned for the delimiter with strchr()/scanarg() and happily stop on the padding. The flags field is different: instead of scanning for the delimiter check_special_flags() consumes the flag characters 'P', 'O', 'C' and 'F' and stops at the first byte that is none of them, relying on the trailing delimiter to end the scan. If the delimiter is itself a flag character the padding no longer acts as a terminator. The scan swallows all eight padding bytes and keeps reading past the end of the allocation until it hits a byte that is not a flag character. For example registering PaPEPPxPPiP with 'P' as the delimiter (name "a", type extension, magic "x", interpreter "i", empty flags) leaves the flag scan running off the end of the buffer. The registration is rejected in the end because the parser does not stop exactly at buf + count, but only after the out of bounds read has already happened. With an unlucky allocation layout the scan can walk into an unmapped page; under KASAN it is reported as a slab out of bounds read. binfmt_misc mounts are available to unprivileged users in a user namespace so the read is reachable without privileges. Reject a delimiter that is one of the flag characters up front. Such a registration was always rejected anyway, only after the out of bounds read, so no valid registration string changes meaning. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-3-a162f7cb58d6@kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index bf7d6b975825..a73a37b8a013 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -384,6 +384,10 @@ static Node *create_entry(const char __user *buffer, size_t count) pr_debug("register: delim: %#x {%c}\n", del, del); + /* A flag-char delimiter runs the flag scan off the buffer. */ + if (del == 'P' || del == 'O' || del == 'C' || del == 'F') + goto einval; + /* Pad the buffer with the delim to simplify parsing below. */ memset(buf + count, del, 8); -- cgit v1.2.3 From b8206f516fe7cbe785cf44bf09c17c438d7c3cad Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 28 Jul 2026 15:48:10 +0200 Subject: binfmt_misc: don't leak the user namespace when the mount fails bm_get_tree() takes a reference to the user namespace and hands it to get_tree_keyed() as the sget key. sget_fc() moves that reference into sb->s_fs_info and clears fc->s_fs_info, so from that point on the superblock owns it and bm_free() doesn't see it anymore. The superblock drops it in ->put_super(). But generic_shutdown_super() only calls ->put_super() from inside the if (sb->s_root) branch, so nothing releases it when bm_fill_super() fails: - The kzalloc_obj() failure leaves s_root NULL and the whole branch is skipped. - A simple_fill_super() failure in the file loop leaves s_root set, but s_op still points at simple_super_operations, which has no ->put_super(). bm_fill_super() installs s_ops only once simple_fill_super() returned success, and installing it earlier wouldn't help either because simple_fill_super() overwrites s_op. Either way vfs_get_super() calls deactivate_locked_super() and the reference is gone for good. binfmt_misc mounts are available in a user namespace and both the inode and the dentry cache are SLAB_ACCOUNT, so an unprivileged caller under a tight memory cgroup can fail simple_fill_super() on demand and leak one user namespace per attempt. Drop the reference in ->kill_sb() instead, which runs unconditionally, the same way nfsd and rpc_pipefs release their keyed s_fs_info. That also stops ->put_super() from clearing s_fs_info while the superblock is still on @fs_supers. generic_shutdown_super() leaves it there on purpose so that sget_fc() keeps finding it until kill_sb() has run, but a NULL s_fs_info makes test_keyed_super() miss it, so a concurrent mount for the same user namespace skips the grab_super() wait and creates a second superblock for a namespace that is still being torn down. Link: https://patch.msgid.link/20260728-work-binfmt_misc-usernsleak-v1-1-dbd8d5e626e7@kernel.org Fixes: 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index a73a37b8a013..c97f10b48b5b 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -921,18 +921,9 @@ static const struct file_operations bm_status_operations = { /* Superblock handling */ -static void bm_put_super(struct super_block *sb) -{ - struct user_namespace *user_ns = sb->s_fs_info; - - sb->s_fs_info = NULL; - put_user_ns(user_ns); -} - static const struct super_operations s_ops = { .statfs = simple_statfs, .evict_inode = bm_evict_inode, - .put_super = bm_put_super, }; static int bm_fill_super(struct super_block *sb, struct fs_context *fc) @@ -990,13 +981,12 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc) /* * When the binfmt_misc superblock for this userns is shutdown * ->enabled might have been set to false and we don't reinitialize - * ->enabled again in put_super() as someone might already be mounting - * binfmt_misc again. It also would be pointless since by the time - * ->put_super() is called we know that the binary type list for this - * bintfmt_misc mount is empty making load_misc_binary() return - * -ENOEXEC independent of whether ->enabled is true. Instead, if - * someone mounts binfmt_misc for the first time or again we simply - * reset ->enabled to true. + * ->enabled again during shutdown as someone might already be mounting + * binfmt_misc again. It also would be pointless since by then we know + * that the binary type list for this binfmt_misc mount is empty making + * load_misc_binary() return -ENOEXEC independent of whether ->enabled + * is true. Instead, if someone mounts binfmt_misc for the first time or + * again we simply reset ->enabled to true. */ misc->enabled = true; @@ -1022,6 +1012,14 @@ static const struct fs_context_operations bm_context_ops = { .get_tree = bm_get_tree, }; +static void bm_kill_sb(struct super_block *sb) +{ + struct user_namespace *user_ns = sb->s_fs_info; + + kill_anon_super(sb); + put_user_ns(user_ns); +} + static int bm_init_fs_context(struct fs_context *fc) { fc->ops = &bm_context_ops; @@ -1038,7 +1036,7 @@ static struct file_system_type bm_fs_type = { .name = "binfmt_misc", .init_fs_context = bm_init_fs_context, .fs_flags = FS_USERNS_MOUNT, - .kill_sb = kill_anon_super, + .kill_sb = bm_kill_sb, }; MODULE_ALIAS_FS("binfmt_misc"); -- cgit v1.2.3 From e095f249e2209674f6366f6db0383a2b96e19239 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 23 Jul 2026 13:57:35 +0800 Subject: net: ethernet: mtk_eth_soc: pass eth to mtk_handle_irq_rx in poll_controller mtk_handle_irq_rx expects a struct mtk_eth * (matching the request_irq cookie), but mtk_poll_controller incorrectly passed the net_device *. Calling ndo_poll_controller with CONFIG_NET_POLL_CONTROLLER enabled would then crash. Fixes: 8186f6e382d8 ("net-next: mediatek: fix compile error inside mtk_poll_controller()") Signed-off-by: Chenguang Zhao Link: https://patch.msgid.link/20260723055735.885112-1-chenguang.zhao@linux.dev Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 5d291e50a47b..351444fb4871 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -3467,7 +3467,7 @@ static void mtk_poll_controller(struct net_device *dev) mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask); - mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], dev); + mtk_handle_irq_rx(eth->irq[MTK_FE_IRQ_RX], eth); mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask); } -- cgit v1.2.3 From 00a8ce2a2a9fa17674e1feec4d9105c1a5d6a419 Mon Sep 17 00:00:00 2001 From: Raushan Patel Date: Fri, 24 Jul 2026 11:14:35 +0530 Subject: tracing/probes: Reject $arg0 in meta argument expansion traceprobe_expand_meta_args() parses $argN with simple_strtoul() and calls sprint_nth_btf_arg(n - 1, ...). For $arg0, n is 0 so the index is -1. Because ctx->nr_params is signed, the "idx >= nr_params" guard in sprint_nth_btf_arg() does not catch the negative index, and ctx->params[-1].name_off is read out of bounds. The normal per-argument path (parse_probe_vars()) already rejects $arg0 via its argument-number check, but meta-argument expansion runs before per-argument parsing and substitutes the value first, bypassing that check. Reject $arg0 explicitly during expansion. Link: https://lore.kernel.org/all/20260724054435.146279-1-raushan.jhon@gmail.com/ Fixes: 18b1e870a496 ("tracing/probes: Add $arg* meta argument for all function args") Cc: stable@vger.kernel.org Signed-off-by: Raushan Patel Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_probe.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 506e6037e163..c8fd9b946f44 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -1901,7 +1901,11 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], trace_probe_log_err(0, BAD_VAR); return ERR_PTR(-ENOENT); } - /* Note: $argN starts from $arg1 */ + /* Note: $argN starts from $arg1, so $arg0 is invalid. */ + if (n == 0) { + trace_probe_log_err(0, BAD_ARG_NUM); + return ERR_PTR(-EINVAL); + } ret = sprint_nth_btf_arg(n - 1, type, buf + used, bufsize - used, ctx); if (ret < 0) -- cgit v1.2.3 From aca0cd1bf16574327ef64f3178e5f5e37a61ef0b Mon Sep 17 00:00:00 2001 From: Raushan Patel Date: Fri, 24 Jul 2026 12:12:08 +0530 Subject: tracing/fprobe: Roll back on enable_trace_fprobe() failure enable_trace_fprobe() sets the file link or the TP_FLAG_PROFILE flag and then registers each trace_fprobe in the probe list. If __register_trace_fprobe() fails partway through, the function returns immediately without unregistering the trace_fprobes it already registered or undoing the file link / flag it set, leaving the event half-enabled and leaking the registered fprobe(s). enable_trace_kprobe() already handles this with a rollback path. Do the same for fprobe: on failure, unregister all probes and clear the file link or profile flag. Link: https://lore.kernel.org/all/20260724064208.480030-1-raushan.jhon@gmail.com/ Fixes: 334e5519c375 ("tracing/probes: Add fprobe events for tracing function entry and exit.") Cc: stable@vger.kernel.org Signed-off-by: Raushan Patel Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/trace_fprobe.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c index 9f5f08c0e7c2..3120403a5b60 100644 --- a/kernel/trace/trace_fprobe.c +++ b/kernel/trace/trace_fprobe.c @@ -1481,11 +1481,21 @@ static int enable_trace_fprobe(struct trace_event_call *call, list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) { ret = __register_trace_fprobe(tf); if (ret < 0) - return ret; + goto err; } } return 0; + +err: + /* Failed to enable one of them. Roll back all */ + list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) + __unregister_trace_fprobe(tf); + if (file) + trace_probe_remove_file(tp, file); + else + trace_probe_clear_flag(tp, TP_FLAG_PROFILE); + return ret; } /* -- cgit v1.2.3 From 00d86dd5c2034e0e139e4806137b3b43e07ddd83 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 11:04:00 +0800 Subject: i2c: imx: mark I2C adapter when hardware is powered down On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers. During system suspend/resume, there exists a time window between: - suspend_noirq and the system entering suspend - the system starting to resume and resume_noirq In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored. If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang. Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available. Fixes: 358025ac091e ("i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()") Signed-off-by: Carlos Song Cc: # v6.14+ Reviewed-by: Frank Li Acked-by: Oleksij Rempel Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260525030400.3182911-1-carlos.song@oss.nxp.com --- drivers/i2c/busses/i2c-imx.c | 45 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index d5e6e2eca3b3..d549d630b41a 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1952,6 +1952,47 @@ static int i2c_imx_runtime_resume(struct device *dev) return 0; } +static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&i2c_imx->adapter); + + /* + * Cancel the slave timer before powering down to prevent + * i2c_imx_slave_timeout() from accessing hardware registers + * while the clock is disabled. + */ + hrtimer_cancel(&i2c_imx->slave_timer); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&i2c_imx->adapter); + if (i2c_imx->slave) { + hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY); + hrtimer_restart(&i2c_imx->slave_timer); + } + return ret; + } + + return 0; +} + +static int __maybe_unused i2c_imx_resume_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + i2c_mark_adapter_resumed(&i2c_imx->adapter); + + return 0; +} + static int i2c_imx_suspend(struct device *dev) { /* @@ -1985,8 +2026,8 @@ static int i2c_imx_resume(struct device *dev) } static const struct dev_pm_ops i2c_imx_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq, + i2c_imx_resume_noirq) SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume) RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL) }; -- cgit v1.2.3 From bba13ad17b1a11b3f1ed9b3a5d556191d7755a59 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Mon, 27 Jul 2026 10:36:59 +0200 Subject: of/address: Fix NULL bus dereference in of_pci_range_parser_one() The bus matching rework made of_match_bus() return NULL for nodes with ranges/dma-ranges but no local #address-cells. parser_init() stored that NULL bus, and the range iterator later dereferenced it. Reject such nodes in parser_init(), leaving an explicit empty iterator for callers that ignore the init return, and make of_dma_get_max_cpu_address() honour the init failure so a rejected node cannot clamp the DMA limit. Fixes: 64ee3cf096ac ("of/address: Rework bus matching to avoid warnings") Cc: stable@vger.kernel.org Signed-off-by: Carlo Caione Link: https://patch.msgid.link/20260727-of-range-parser-null-bus-v3-1-be01b708a4ce@baylibre.com Signed-off-by: Rob Herring (Arm) --- drivers/of/address.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/of/address.c b/drivers/of/address.c index cf4aab11e9b1..499d37ceae21 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -753,6 +753,7 @@ EXPORT_SYMBOL(of_property_read_reg); static int parser_init(struct of_pci_range_parser *parser, struct device_node *node, const char *name) { + const __be32 *range; int rlen; parser->node = node; @@ -761,12 +762,20 @@ static int parser_init(struct of_pci_range_parser *parser, parser->ns = of_bus_n_size_cells(node); parser->dma = !strcmp(name, "dma-ranges"); parser->bus = of_match_bus(node); + parser->range = NULL; + parser->end = NULL; - parser->range = of_get_property(node, name, &rlen); - if (parser->range == NULL) + range = of_get_property(node, name, &rlen); + if (!range) return -ENOENT; - parser->end = parser->range + rlen / sizeof(__be32); + if (!parser->bus || + !OF_CHECK_COUNTS(parser->na, parser->ns) || + !OF_CHECK_ADDR_COUNT(parser->pna)) + return -EINVAL; + + parser->range = range; + parser->end = range + rlen / sizeof(__be32); return 0; } @@ -792,7 +801,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, int na = parser->na; int ns = parser->ns; int np = parser->pna + na + ns; - int busflag_na = parser->bus->flag_cells; + int busflag_na; if (!range) return NULL; @@ -800,6 +809,8 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, if (!parser->range || parser->range + np > parser->end) return NULL; + busflag_na = parser->bus->flag_cells; + range->flags = parser->bus->get_flags(parser->range); range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na); @@ -976,8 +987,7 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) np = of_root; ranges = of_get_property(np, "dma-ranges", &len); - if (ranges && len) { - of_dma_range_parser_init(&parser, np); + if (ranges && len && !of_dma_range_parser_init(&parser, np)) { for_each_of_range(&parser, &range) if (range.cpu_addr + range.size > cpu_end) cpu_end = range.cpu_addr + range.size - 1; -- cgit v1.2.3 From 0e65cd9e5d41c34f86b7c347967bedac54926041 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 21 Jul 2026 18:31:49 +0200 Subject: KVM: VMX: add memory clobber to asm for VMX instructions VMCLEAR/VMREAD/VMWRITE/VMPTRLD access the internal VMCS cache, which is not visible to the compiler; without a memory clobber, the compiler can reorder them in troublesome ways because "asm volatile" and "asm goto" only protect against removal of the asm. For example, placing a VMWRITE before the corresponding VMCS pointer is loaded can lead to corruption. While none of this has been observed, it is better to prevent than cure. Likewise, INVEPT and INVVPID access the TLB and, even though in their case the effect is only visible to the next VMLAUNCH/VMRESUME, it is technically correct to add the clobber there too. So avoid any urge to special case them, and simply hardcode "memory" into the clobber list of vmx_asm1() and vmx_asm2(). __vmcs_readl() open-codes its own asm, so add the clobber there as well. Link: https://lore.kernel.org/kvm/CABgObfbL3t21yVeSwiLSjjOUER+rTYDPHYAH9YU4TWGRjx6XHg@mail.gmail.com/ Cc: Sean Christopherson Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx_ops.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx_ops.h b/arch/x86/kvm/vmx/vmx_ops.h index 81784befaaf4..9ffe03a0c223 100644 --- a/arch/x86/kvm/vmx/vmx_ops.h +++ b/arch/x86/kvm/vmx/vmx_ops.h @@ -101,7 +101,7 @@ static __always_inline unsigned long __vmcs_readl(unsigned long field) : [output] "=r" (value) : [field] "r" (field) - : "cc" + : "cc", "memory" : do_fail, do_exception); return value; @@ -145,7 +145,7 @@ do_exception: : ASM_CALL_CONSTRAINT, [output] "=&r" (value) : [field] "r" (field) - : "cc"); + : "cc", "memory"); return value; #endif /* CONFIG_CC_HAS_ASM_GOTO_OUTPUT */ @@ -192,7 +192,7 @@ do { \ asm goto("1: " __stringify(insn) " %0\n\t" \ "jna %l[error]\n\t" \ _ASM_EXTABLE(1b, %l[fault]) \ - : : op1 : "cc" : error, fault); \ + : : op1 : "cc", "memory" : error, fault); \ return; \ error: \ instrumentation_begin(); \ @@ -208,7 +208,7 @@ do { \ asm goto("1: " __stringify(insn) " %1, %0\n\t" \ "jna %l[error]\n\t" \ _ASM_EXTABLE(1b, %l[fault]) \ - : : op1, op2 : "cc" : error, fault); \ + : : op1, op2 : "cc", "memory" : error, fault);\ return; \ error: \ instrumentation_begin(); \ -- cgit v1.2.3 From 9910e835580fef3bef53b70241dd00c4bffad693 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Mon, 27 Jul 2026 10:17:18 -0700 Subject: KVM: x86: Cancel delayed I/O APIC EOI handling before destroying vCPUs Cancel (and flush) the I/O APIC's delayed EOI handling work during the "pre VM destroy" phase, before vCPUs are destroyed, as processing the EOI broadcast will inject another IRQ if the line is asserted, i.e. will try to deliver an IRQ to the target vCPU(s). Canceling the work after vCPUs are destroyed leads to UAF if the delayed work is processed after vCPUs are destroyed. BUG: KASAN: slab-use-after-free in __kvm_irq_delivery_to_apic_fast+0x9bf/0xa20 arch/x86/kvm/lapic.c:1250 Read of size 8 at addr ffff8880499abea0 by task kworker/1:2/1218 CPU: 1 UID: 0 PID: 1218 Comm: kworker/1:2 Not tainted 7.1.0-rc7 #5 PREEMPT(lazy) 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 Workqueue: events kvm_ioapic_eoi_inject_work 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 __kvm_irq_delivery_to_apic_fast+0x9bf/0xa20 arch/x86/kvm/lapic.c:1250 __kvm_irq_delivery_to_apic+0xd8/0xbf0 arch/x86/kvm/lapic.c:1345 kvm_irq_delivery_to_apic arch/x86/kvm/lapic.h:129 ioapic_service+0x308/0x590 arch/x86/kvm/ioapic.c:492 kvm_ioapic_eoi_inject_work+0x13c/0x190 arch/x86/kvm/ioapic.c:532 process_one_work+0xa59/0x19a0 kernel/workqueue.c:3314 process_scheduled_works kernel/workqueue.c:3397 worker_thread+0x5eb/0xe50 kernel/workqueue.c:3478 kthread+0x370/0x450 kernel/kthread.c:436 ret_from_fork+0x72b/0xd30 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Note, the VM is unreachable once kvm_destroy_vm() starts, and scheduling new work via kvm_ioapic_send_eoi() can only be done via KVM_RUN, i.e. requires a live vCPU. Alternatively, KVM could simply destroy the I/O APIC during the "pre" phase of VM destruction, but that gets more than a bit sketchy as KVM expects the I/O APIC to exist if ioapic_in_kernel() is true, and nested virtualization in particular has a bad habit of touching VM-scope state during vCPU destruction. E.g. attempting to free the PIC during the pre phase would lead to a NULL pointer dereference in kvm_cpu_has_extint(), and it's not hard to imagine the I/O APIC having a similar flaw. Fixes: 17bcd7144263 ("KVM: x86: Free vCPUs before freeing VM state") Reported-by: Reported-by: Zhong Wang Reported-by: Xuanqing Shi Cc: stable@vger.kernel.org Signed-off-by: Weiming Shi Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson Message-ID: <20260727171718.543491-1-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/x86.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index afcac1042947..47cb9eba113b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -13429,9 +13429,15 @@ void kvm_arch_pre_destroy_vm(struct kvm *kvm) * iterating over vCPUs in a different task while vCPUs are being freed * is unsafe, i.e. will lead to use-after-free. The PIT also needs to * be stopped before IRQ routing is freed. + * + * Do NOT free the in-kernel PIC or I/O APIC here (but as above, make + * sure to flush any background work), as KVM expects interrupt routing + * structures to be valid until vCPUs are destroyed. */ #ifdef CONFIG_KVM_IOAPIC kvm_free_pit(kvm); + if (kvm->arch.vioapic) + cancel_delayed_work_sync(&kvm->arch.vioapic->eoi_inject); #endif kvm_mmu_pre_destroy_vm(kvm); -- cgit v1.2.3 From a19038a200f18d9e74ac30081797917d0886e16b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 28 Jul 2026 08:41:40 -0700 Subject: hwmon: (pmbus) Fix return value from pmbus_update_byte_data() pmbus_update_byte_data() is supposed to return a negative error code or 0. However, if no change is made to the register, it actually returns the register value. This can result in problems if the calling code explicitly expects to see an error code or 0. Fix it to return 0 on success or the error code as expected. Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 0081f16c3a95..5567d37f13fe 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -513,7 +513,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, if (tmp != rv) rv = _pmbus_write_byte_data(client, page, reg, tmp); - return rv; + return rv < 0 ? rv : 0; } EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); -- cgit v1.2.3 From 0f38453cdb2e17566ccb7c0f3dabd5bd21caca26 Mon Sep 17 00:00:00 2001 From: Jinu Kim Date: Tue, 21 Jul 2026 19:35:11 +0900 Subject: KVM: x86/mmu: Check write tracking in all address spaces kvm_gfn_is_write_tracked() checks only the supplied memslot, but page tracking is per-address-space and shadow pages are shared across all address spaces. With SMM, a GFN can therefore be write-tracked in one address space and appear untracked through the other. Check the supplied slot first, then the slot for the other address space. This ensures all callers honor write tracking regardless of the active address space. In particular, it prevents mmu_try_to_unsync_pages() from marking an upper-level shadow page unsync and eventually triggering the BUG in pte_list_remove(). Fixes: 699023e23965 ("KVM: x86: add SMM to the MMU role, support SMRAM address space") Assisted-by: Codex:GPT-5 Signed-off-by: Jinu Kim Message-ID: <20260721103512.2136240-2-kimjw04271234@gmail.com> [invert direction of the conditional. - Paolo] Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/page_track.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/mmu/page_track.c b/arch/x86/kvm/mmu/page_track.c index 1b17b12393a8..7e8195a311bb 100644 --- a/arch/x86/kvm/mmu/page_track.c +++ b/arch/x86/kvm/mmu/page_track.c @@ -130,13 +130,23 @@ void __kvm_write_track_remove_gfn(struct kvm *kvm, kvm_mmu_gfn_allow_lpage(slot, gfn); } -/* - * check if the corresponding access on the specified guest page is tracked. - */ +static bool __kvm_gfn_is_write_tracked(const struct kvm_memory_slot *slot, + gfn_t gfn) +{ + int index; + + if (!slot) + return false; + + index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K); + return !!READ_ONCE(slot->arch.gfn_write_track[index]); +} + +/* check if write access is tracked on the specified guest page. */ bool kvm_gfn_is_write_tracked(struct kvm *kvm, const struct kvm_memory_slot *slot, gfn_t gfn) { - int index; + const struct kvm_memory_slot *other_slot; if (!slot) return false; @@ -144,8 +154,18 @@ bool kvm_gfn_is_write_tracked(struct kvm *kvm, if (!kvm_page_track_write_tracking_enabled(kvm)) return false; - index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K); - return !!READ_ONCE(slot->arch.gfn_write_track[index]); + BUILD_BUG_ON(KVM_MAX_NR_ADDRESS_SPACES > 2); + + if (__kvm_gfn_is_write_tracked(slot, gfn)) + return true; + + if (kvm_arch_nr_memslot_as_ids(kvm) > 1) { + other_slot = __gfn_to_memslot(__kvm_memslots(kvm, slot->as_id ^ 1), gfn); + if (__kvm_gfn_is_write_tracked(other_slot, gfn)) + return true; + } + + return false; } #ifdef CONFIG_KVM_EXTERNAL_WRITE_TRACKING -- cgit v1.2.3 From 2e8a2c1b03068d76782343446f1b2114ae2ee0bd Mon Sep 17 00:00:00 2001 From: Jinu Kim Date: Tue, 21 Jul 2026 19:35:12 +0900 Subject: KVM: x86/mmu: Check all address spaces before skipping unsync mmu_try_to_unsync_pages() skips the shadow-page lookup when the supplied memslot allows a hugepage, because a shadow page would disallow hugepages. But hugepage metadata is per-address-space while shadow pages are shared across all address spaces. With SMM, the other address space can therefore have a shadow page even when the supplied memslot allows a hugepage. Check the corresponding memslot in the other address space before taking the fast path. Skip the shadow-page lookup only when all address spaces allow a hugepage. Fixes: b3ae3ceb5569 ("KVM: x86/mmu: KVM: x86/mmu: Skip unsync when large pages are allowed") Assisted-by: Codex:GPT-5 Signed-off-by: Jinu Kim [invert direction of the conditional. - Paolo] Message-ID: <20260721103512.2136240-3-kimjw04271234@gmail.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 22cf222d3033..66e69d2a41b3 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -722,6 +722,26 @@ static struct kvm_lpage_info *lpage_info_slot(gfn_t gfn, return &slot->arch.lpage_info[level - 2][idx]; } +static bool kvm_gfn_is_lpage_allowed(struct kvm *kvm, + const struct kvm_memory_slot *slot, + gfn_t gfn, int level) +{ + const struct kvm_memory_slot *other_slot; + + BUILD_BUG_ON(KVM_MAX_NR_ADDRESS_SPACES > 2); + + if (lpage_info_slot(gfn, slot, level)->disallow_lpage) + return false; + + if (kvm_arch_nr_memslot_as_ids(kvm) > 1) { + other_slot = __gfn_to_memslot(__kvm_memslots(kvm, slot->as_id ^ 1), gfn); + if (other_slot && lpage_info_slot(gfn, other_slot, level)->disallow_lpage) + return false; + } + + return true; +} + /* * The most significant bit in disallow_lpage tracks whether or not memory * attributes are mixed, i.e. not identical for all gfns at the current level. @@ -2968,7 +2988,7 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, * write-protected (see above), thus if the gfn can be mapped with a * hugepage and isn't write-tracked, it can't have a shadow page. */ - if (!lpage_info_slot(gfn, slot, PG_LEVEL_2M)->disallow_lpage) + if (kvm_gfn_is_lpage_allowed(kvm, slot, gfn, PG_LEVEL_2M)) return 0; /* -- cgit v1.2.3 From f495b6c4c8594122918552c9be2b51eb71647cd9 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Tue, 28 Jul 2026 14:50:01 +0200 Subject: ALSA: pcm: wake linked drain waiters on unlink snd_pcm_drain() on a linked stream parks an on-stack wait entry on the drained peer's runtime->sleep, and after schedule_timeout() removes it only if that peer is still found in the caller's group. If group membership changes during the wait and the sleep ends by signal or timeout (so autoremove_wake_function() does not run), finish_wait() is skipped and snd_pcm_drain() returns with the entry still queued on that stream's sleep list; a later wake_up() then walks a freed stack frame. This is reachable by unlinking either the drained or the draining stream. Unlike the close path (snd_pcm_drop() -> snd_pcm_post_stop()), snd_pcm_unlink() never wakes the sleep queues. Wake every group member under the group lock before the membership change, so a linked drainer is released and drops its entry while the streams are still grouped. The window was opened when snd_pcm_link_rwsem stopped being held across the wait and the removal became conditional on group membership (see Fixes). The later switch to finish_wait() kept that conditional removal, so the signal/timeout case remained. Fixes: f57f3df03a8e ("ALSA: pcm: More fine-grained PCM link locking") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/A0705100-D10B-4286-9980-0142ABEEAD51@doyensec.com Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 7dc0060617f1..c9d0c2bb55b2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2367,6 +2367,7 @@ static void relink_to_local(struct snd_pcm_substream *substream) static int snd_pcm_unlink(struct snd_pcm_substream *substream) { + struct snd_pcm_substream *s; struct snd_pcm_group *group; bool nonatomic = substream->pcm->nonatomic; bool do_free = false; @@ -2379,6 +2380,12 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) group = substream->group; snd_pcm_group_lock_irq(group, nonatomic); + /* release drain waiters before changing membership, else snd_pcm_drain() + * leaves its on-stack wait entry queued on a member's sleep list + */ + snd_pcm_group_for_each_entry(s, substream) + wake_up(&s->runtime->sleep); + relink_to_local(substream); refcount_dec(&group->refs); -- cgit v1.2.3 From cee046679655b4822f76efc9658f19efee9ac979 Mon Sep 17 00:00:00 2001 From: Robert Abrahamse Date: Tue, 28 Jul 2026 16:03:14 +0200 Subject: ALSA: usb-audio: Add quirk for Corsair Virtuoso (later revision) Add USB mixer mapping quirk for later revisions of the Corsair Virtuoso headset with USB IDs 0x1b1c:0x0a43 (wired) and 0x1b1c:0x0a44 (wireless). These devices exhibit the same mixer label collision as earlier Virtuoso variants: all controls are labelled "Headset", causing applications like PulseAudio to move the sidetone control instead of the main playback volume. Signed-off-by: Robert Abrahamse Link: https://patch.msgid.link/20260728140314.11601-1-denobyte2@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 4d8dca04f87d..ce27fc871f51 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -638,6 +638,16 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x1b1c, 0x0a42), .map = corsair_virtuoso_map, }, + { + /* Corsair Virtuoso (wired mode, later revision) */ + .id = USB_ID(0x1b1c, 0x0a43), + .map = corsair_virtuoso_map, + }, + { + /* Corsair Virtuoso (wireless mode, later revision) */ + .id = USB_ID(0x1b1c, 0x0a44), + .map = corsair_virtuoso_map, + }, { /* Corsair HS80 RGB Wireless (wired mode) */ .id = USB_ID(0x1b1c, 0x0a6a), -- cgit v1.2.3 From 7678e81498e20e78d7d5f64e552cd153117c1d66 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Tue, 28 Jul 2026 11:51:59 +0800 Subject: PCI: imx6: Fix i.MX6Q/DL boot hang caused by improper PHY power sequencing commit 610fa91d9863 ("PCI: imx6: Assert PERST# before enabling regulators") introduced a boot hang on i.MX6Q/DL variants by reordering imx_pcie_host_init() to call imx6q_pcie_enable_ref_clk() (which powered up the PHY) before imx6q_pcie_core_reset() (which powered it back down). Before 610fa91d9863, the sequence was: 1. imx_pcie_assert_core_reset() - power down PHY (set TEST_PD), set REF_CLK_EN 2. imx_pcie_clk_enable() - power up PHY (clear TEST_PD), set REF_CLK_EN 3. Link training starts with PHY powered up (TEST_PD cleared) 4. Link training succeeds After 610fa91d9863, the sequence became: 1. imx_pcie_clk_enable() - power up PHY (clear TEST_PD), set REF_CLK_EN 2. imx_pcie_assert_core_reset() - power down PHY (set TEST_PD), set REF_CLK_EN 3. imx_pcie_deassert_core_reset() - does nothing 4. Link training starts with PHY powered down (TEST_PD set) 5. Link training fails and boot hangs when PHY register accesses hang To fix this: - Remove TEST_PD PHY power control from imx6q_pcie_enable_ref_clk() - Remove REF_CLK_EN control from imx6q_pcie_core_reset() - Add TEST_PD PHY power control to imx6qp_pcie_core_reset(), which previously relied on imx6q_pcie_enable_ref_clk() to power up the PHY by clearing TEST_PD - Clear TEST_PD to power on PHY in imx_pcie_deassert_core_reset() These changes together ensure the correct sequence: 1. REF_CLK_EN set in clk_enable() (TEST_PD untouched) 2. TEST_PD set in assert_core_reset() (PHY power off) 3. TEST_PD cleared in deassert_core_reset() (PHY power on) 4. Link training starts with proper PHY state The i.MX6Q/DL PCIe PHY requires approximately 120us between TEST_PD de-assertion and link training start. Add usleep_range(200, 500) in imx6q_pcie_core_reset() after clearing TEST_PD to satisfy this requirement. Add explicit imx_pcie_assert_core_reset() calls in error paths and host_exit() to ensure no power leak. Fixes: 610fa91d9863 ("PCI: imx6: Assert PERST# before enabling regulators") Reported-by: Leonardo Costa Closes: https://lore.kernel.org/lkml/20260629143439.361560-1-leoreis.costa@gmail.com/ Signed-off-by: Richard Zhu Signed-off-by: Bjorn Helgaas Tested-by: Leonardo Costa Reviewed-by: Frank Li Link: https://patch.msgid.link/20260728035159.2702021-1-hongxing.zhu@oss.nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 43 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index dba27eac6bff..f7389b5437df 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -680,21 +680,12 @@ static int imx_pcie_attach_pd(struct device *dev) static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { - if (enable) { - /* power up core phy and enable ref clock */ - regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - /* - * The async reset input need ref clock to sync internally, - * when the ref clock comes after reset, internal synced - * reset time is too short, cannot meet the requirement. - * Add a ~10us delay here. - */ - usleep_range(10, 100); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); - } else { - regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - } + if (enable) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN); return 0; } @@ -825,8 +816,16 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) { + if (assert) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_SW_RST, assert ? IMX6Q_GPR1_PCIE_SW_RST : 0); + if (!assert) usleep_range(200, 500); @@ -835,11 +834,15 @@ static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) static int imx6q_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) { - if (!assert) - return 0; + if (assert) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); + if (!assert) + usleep_range(200, 500); return 0; } @@ -1445,6 +1448,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) return 0; err_phy_off: + imx_pcie_assert_core_reset(imx_pcie); phy_power_off(imx_pcie->phy); err_phy_exit: phy_exit(imx_pcie->phy); @@ -1466,6 +1470,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct imx_pcie *imx_pcie = to_imx_pcie(pci); + imx_pcie_assert_core_reset(imx_pcie); if (imx_pcie->phy) { if (phy_power_off(imx_pcie->phy)) dev_err(pci->dev, "unable to power off PHY\n"); -- cgit v1.2.3 From 8fd62901d6bf03f274a49dd0060793cc07dd51b0 Mon Sep 17 00:00:00 2001 From: Stanislaw Pal Date: Mon, 27 Jul 2026 18:32:16 +0200 Subject: spi: spi-qpic-snand: write the feature value before executing SET_FEATURE qcom_spi_send_cmdaddr() programs NAND_FLASH_CMD/NAND_EXEC_CMD and submits the descriptors, which makes the controller execute the command immediately. For SPINAND_SET_FEATURE the value to be written is only placed into NAND_FLASH_FEATURES afterwards, by qcom_spi_io_op(), in a second submission - so the chip is programmed with whatever that register happened to hold from a previous operation, and the intended value is only applied by the *next* SET_FEATURE. Measured on a TP-Link Archer AX55 v1 (IPQ5018, ESMT F50L1G41LB): writing 0x40 to the configuration register (0xb0) leaves the chip at 0x00, and the subsequent write of 0x00 leaves it at 0x40 - every write lands one operation late. This stayed unnoticed until v6.18 added SPI-NAND OTP support together with OTP entries for ESMT chips. spinand_otp_rw() enables OTP mode, reads, and disables it again, and mtd_otp_nvmem_add() does this during MTD registration. With the off-by-one, the "disable" write actually applies the previously requested value, so CFG_OTP_ENABLE ends up set: the chip stays in OTP mode, every subsequent array read returns the OTP area instead of the array (UBI reports an empty device) and all writes fail with -EIO because the OTP area is write protected. On this board that makes the whole flash unusable and the device unbootable. Write the feature value into NAND_FLASH_FEATURES as part of the same transaction, before NAND_EXEC_CMD. While at it, copy only the bytes the operation actually carries - the previous code dereferenced a 4-byte pointer on a one-byte buffer (spinand->scratchbuf). With this patch the flash contents read back bit-identical to a known-good dump of the same board taken under the vendor firmware (md5-verified across partitions), and writes work. Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Pal Reviewed-by: Md Sadre Alam Link: https://patch.msgid.link/20260727163216.109938-1-kuncy7@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 66f2d1b78ade..b6c58d9cfe14 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -1358,6 +1358,22 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, snandc->regs->addr0 = cpu_to_le32(op->addr.val); snandc->regs->addr1 = cpu_to_le32(0); + /* + * The feature value has to reach NAND_FLASH_FEATURES before the + * command is executed, otherwise the controller programs the chip + * with whatever the register happened to hold from a previous + * operation. + */ + if (opcode == SPINAND_SET_FEATURE) { + u32 ftr = 0; + + memcpy(&ftr, op->data.buf.out, + min_t(size_t, op->data.nbytes, sizeof(ftr))); + snandc->regs->flash_feature = cpu_to_le32(ftr); + qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, + NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); + } + qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -1395,10 +1411,8 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_ copy_ftr = true; break; case SPINAND_SET_FEATURE: - snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out); - qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, - NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); - break; + /* fully handled by qcom_spi_send_cmdaddr() */ + return 0; case SPINAND_PROGRAM_EXECUTE: case SPINAND_WRITE_EN: case SPINAND_RESET: -- cgit v1.2.3 From 9f7007ee9858c99aa43101bc8352c672fee85644 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 17 Jun 2026 17:57:54 -0400 Subject: idpf: bound interrupt-vector register fill to the allocated array idpf_get_reg_intr_vecs() fills the caller-allocated reg_vals[] array from the VIRTCHNL2_OP_ALLOC_VECTORS reply in adapter->req_vec_chunks, bounding its inner loop only by the per-chunk num_vectors. The array is sized separately: idpf_intr_reg_init() allocates kzalloc_objs(struct idpf_vec_regs, total_vecs) from caps.num_allocated_vectors and only checks the returned count after the fill. The sum of per-chunk num_vectors is never reconciled against total_vecs, so a reply with a small num_allocated_vectors but chunks summing higher writes past the end of reg_vals[]. Impact: a control plane (a PF or hypervisor device model) that returns a VIRTCHNL2_OP_ALLOC_VECTORS reply whose per-chunk num_vectors sum exceeds num_allocated_vectors writes struct idpf_vec_regs entries past the end of the reg_vals kmalloc allocation (KASAN slab-out-of-bounds write). Bound the fill loop to the array capacity passed in by the callers, mirroring the sibling idpf_vport_get_q_reg(). The existing num_regs < num_vecs check then rejects an undersized reply without the out-of-bounds write happening first. Fixes: d4d558718266 ("idpf: initialize interrupts and enable vport") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Reviewed-by: Aleksandr Loktionov Tested-by: Samuel Salin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/idpf/idpf_dev.c | 2 +- drivers/net/ethernet/intel/idpf/idpf_vf_dev.c | 2 +- drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 5 +++-- drivers/net/ethernet/intel/idpf/idpf_virtchnl.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_dev.c index 1a0c71c95ef1..4079a787657f 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_dev.c +++ b/drivers/net/ethernet/intel/idpf/idpf_dev.c @@ -87,7 +87,7 @@ static int idpf_intr_reg_init(struct idpf_vport *vport, if (!reg_vals) return -ENOMEM; - num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals); + num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs); if (num_regs < num_vecs) { err = -EINVAL; goto free_reg_vals; diff --git a/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c index a07d7e808ca9..6726084f6cfa 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c +++ b/drivers/net/ethernet/intel/idpf/idpf_vf_dev.c @@ -86,7 +86,7 @@ static int idpf_vf_intr_reg_init(struct idpf_vport *vport, if (!reg_vals) return -ENOMEM; - num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals); + num_regs = idpf_get_reg_intr_vecs(adapter, reg_vals, total_vecs); if (num_regs < num_vecs) { err = -EINVAL; goto free_reg_vals; diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c index dc5ad784f456..8bd6cca64c9b 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c @@ -1318,11 +1318,12 @@ idpf_vport_init_queue_reg_chunks(struct idpf_vport_config *vport_config, * idpf_get_reg_intr_vecs - Get vector queue register offset * @adapter: adapter structure to get the vector chunks * @reg_vals: Register offsets to store in + * @num_vecs: number of entries the @reg_vals array can hold * * Return: number of registers that got populated */ int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter, - struct idpf_vec_regs *reg_vals) + struct idpf_vec_regs *reg_vals, int num_vecs) { struct virtchnl2_vector_chunks *chunks; struct idpf_vec_regs reg_val; @@ -1346,7 +1347,7 @@ int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter, dynctl_reg_spacing = le32_to_cpu(chunk->dynctl_reg_spacing); itrn_reg_spacing = le32_to_cpu(chunk->itrn_reg_spacing); - for (i = 0; i < num_vec; i++) { + for (i = 0; i < num_vec && num_regs < num_vecs; i++) { reg_vals[num_regs].dyn_ctl_reg = reg_val.dyn_ctl_reg; reg_vals[num_regs].itrn_reg = reg_val.itrn_reg; reg_vals[num_regs].itrn_index_spacing = diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h index 6876e3ed9d1b..9b1c9c86f6ea 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h +++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h @@ -104,7 +104,7 @@ int idpf_vc_core_init(struct idpf_adapter *adapter); void idpf_vc_core_deinit(struct idpf_adapter *adapter); int idpf_get_reg_intr_vecs(struct idpf_adapter *adapter, - struct idpf_vec_regs *reg_vals); + struct idpf_vec_regs *reg_vals, int num_vecs); int idpf_queue_reg_init(struct idpf_vport *vport, struct idpf_q_vec_rsrc *rsrc, struct idpf_queue_id_reg_info *chunks); -- cgit v1.2.3 From bef152db47debcd14cbacefc5767f6f026c4bc89 Mon Sep 17 00:00:00 2001 From: Joshua Hay Date: Tue, 30 Jun 2026 16:56:19 -0700 Subject: idpf: adjust TxQ ring count minimum Set the TxQ ring count minimum to 128 descriptors. Any lower than this, and the queue will stall and trigger Tx timeouts in flow based scheduling mode. This is because next_to_clean might never be updated. In flow based scheduling mode, next_to_clean is only updated after a descriptor completion is processed, i.e. after the RE bit is set in the last descriptor of a Tx packet. This will never happen with a ring size of 64 and an IDPF_TX_SPLITQ_RE_MIN_GAP of 64. No matter what the value of last_re is initialized/set to, the calculated gap will be at most 63 and never trigger the RE bit. Even a ring size of 96 does not solve this. Because of how infrequent next_to_clean is updated and how small the ring is, IDPF_DESC_UNUSED will be much smaller on average. This increases the chance the queue will be stopped because a multi-descriptor packet, e.g. a large LSO packet, does not see enough resources on the ring. In this case, the queue will trigger the stop logic. The queue permanently stalls because there is no chance for a descriptor completion to update next_to_clean since it is dependent on a packet being sent. Fixes: 5f417d551324 ("idpf: replace flow scheduling buffer ring with buffer pool") Signed-off-by: Joshua Hay Reviewed-by: Aleksandr Loktionov Tested-by: Samuel Salin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 5 +---- drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c index 7f9056404f64..c724d429a7aa 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c @@ -3097,10 +3097,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb, tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE; tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP; - /* Set the RE bit to periodically "clean" the descriptor ring. - * MIN_GAP is set to MIN_RING size to ensure it will be set at - * least once each time around the ring. - */ + /* Set the RE bit periodically to "clean" the descriptor ring */ if (idpf_tx_splitq_need_re(tx_q)) { tx_params.eop_cmd |= IDPF_TXD_FLEX_FLOW_CMD_RE; tx_q->txq_grp->num_completions_pending++; diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h index 4be5b3b6d3ed..908dfa28674e 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h @@ -21,7 +21,7 @@ /* Mailbox Queue */ #define IDPF_MAX_MBXQ 1 -#define IDPF_MIN_TXQ_DESC 64 +#define IDPF_MIN_TXQ_DESC 128 #define IDPF_MIN_RXQ_DESC 64 #define IDPF_MIN_TXQ_COMPLQ_DESC 256 #define IDPF_MAX_QIDS 256 -- cgit v1.2.3 From 9bff30482c10f70d9e56c0633a6616e07140e217 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Fri, 3 Jul 2026 01:03:32 -0400 Subject: idpf: Fix mailbox IRQ name leak on request failure idpf_mb_intr_req_irq() allocates the mailbox IRQ name before calling request_irq(). On success, the name is released later through kfree(free_irq()), but request_irq() failure returns without freeing it. Free the allocated name on the request_irq() failure path. Fixes: 4930fbf419a7 ("idpf: add core init and interrupt request") Signed-off-by: Yuho Choi Reviewed-by: Aleksandr Loktionov Tested-by: Samuel Salin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/idpf/idpf_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c index cf966fe6c759..bb81e620c5c8 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_lib.c +++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c @@ -139,7 +139,7 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter) if (err) { dev_err(&adapter->pdev->dev, "IRQ request for mailbox failed, error: %d\n", err); - + kfree(name); return err; } -- cgit v1.2.3 From c2816d613f388814d27bc9fd6dbd931a88056e19 Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Wed, 29 Apr 2026 11:48:49 +0800 Subject: ice: wait for reset completion in ice_resume() ice_resume() schedules an asynchronous PF reset and returns immediately. The reset runs later in ice_service_task(). If userspace tries to bring up the net device before the reset finishes, ice_open() fails with -EBUSY: ice_resume() ice_schedule_reset() # sets ICE_PFR_REQ, returns ... ice_open() ice_is_reset_in_progress() # ICE_PFR_REQ still set, -EBUSY ... ice_service_task() ice_do_reset() ice_rebuild() # clears ICE_PFR_REQ, too late Reproduced on E800 series NICs during suspend/resume with irdma enabled, where the aux device probe widens the race window. ice 0000:81:00.0: can't open net device while reset is in progress Add a best-effort wait (10s timeout, matching ice_devlink_info_get()) for the reset to complete before returning from ice_resume(). In practice the reset completes in ~300ms. Fixes: 769c500dcc1e ("ice: Add advanced power mgmt for WoL") Cc: stable@vger.kernel.org Reviewed-by: Kohei Enju Reviewed-by: Aleksandr Loktionov Reviewed-by: Przemek Kitszel Signed-off-by: Aaron Ma Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index e2fd2dab03e3..d88835482d3a 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -5637,6 +5637,16 @@ static int ice_resume(struct device *dev) /* Restart the service task */ mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period)); + /* Best-effort wait for the scheduled reset to finish so that the + * device is operational before returning. Without this, userspace + * (e.g. NetworkManager) may try to open the net device while the + * asynchronous reset is still in progress, hitting -EBUSY. + */ + ret = ice_wait_for_reset(pf, secs_to_jiffies(10)); + if (ret) + dev_err(dev, "Wait for reset timed out (10s) during resume: %d\n", + ret); + return 0; } -- cgit v1.2.3 From fb096882095e5a8d6b5159e43793d4a38a0c5b1f Mon Sep 17 00:00:00 2001 From: Dawid Osuchowski Date: Thu, 14 May 2026 18:35:55 +0200 Subject: ice: fix VF interrupts cleanup When a virtual function sends an IRQ map command, the PF will set up interrupts according to that request. However, because these interrupts are never reset, the next time Virtual Function initializes, the interrupts are still enabled for a given VF, which leads to performance degradation in certain cases due to interrupts being unexpectedly enabled and thus causing interrupt floods. Cc: stable@vger.kernel.org Fixes: 1071a8358a28 ("ice: Implement virtchnl commands for AVF support") Suggested-by: Vladimir Medvedkin Reviewed-by: Aleksandr Loktionov Signed-off-by: Dawid Osuchowski Reviewed-by: Simon Horman Tested-by: Patryk Holda Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_vf_lib.c | 27 ++++++++++++++++++++++ .../net/ethernet/intel/ice/ice_vf_lib_private.h | 1 + drivers/net/ethernet/intel/ice/virt/queues.c | 21 +++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 9052e71e9c99..a54cb2b8d3c7 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -848,6 +848,30 @@ static void ice_notify_vf_reset(struct ice_vf *vf) NULL); } +/** + * ice_reset_interrupts - clear all queue interrupt configuration for a VSI + * @vsi: the VSI whose interrupt registers should be cleared + * + * Zero the QINT_RQCTL and QINT_TQCTL registers for all allocated queues + * in the VSI. This clears the entire register including MSIX_INDX, ITR_INDX, + * CAUSE_ENA and NEXTQ fields, unlike ice_vf_dis_rxq_interrupt() which only + * clears the CAUSE_ENA bit. + */ +void ice_reset_interrupts(struct ice_vsi *vsi) +{ + struct ice_pf *pf = vsi->back; + struct ice_hw *hw = &pf->hw; + int i; + + ice_for_each_alloc_rxq(vsi, i) + wr32(hw, QINT_RQCTL(vsi->rxq_map[i]), 0); + + ice_for_each_alloc_txq(vsi, i) + wr32(hw, QINT_TQCTL(vsi->txq_map[i]), 0); + + ice_flush(hw); +} + /** * ice_reset_vf - Reset a particular VF * @vf: pointer to the VF structure @@ -919,6 +943,9 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags) ice_dis_vf_qs(vf); + /* cleanup interrupt registers */ + ice_reset_interrupts(vsi); + /* Call Disable LAN Tx queue AQ whether or not queues are * enabled. This is needed for successful completion of VFR. */ diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h index 5392b0404986..321d29c25b7c 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h @@ -26,6 +26,7 @@ void ice_initialize_vf_entry(struct ice_vf *vf); void ice_deinitialize_vf_entry(struct ice_vf *vf); void ice_dis_vf_qs(struct ice_vf *vf); +void ice_reset_interrupts(struct ice_vsi *vsi); int ice_check_vf_init(struct ice_vf *vf); enum virtchnl_status_code ice_err_to_virt_err(int err); struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf); diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c index 31be2f76181c..431c9c546b04 100644 --- a/drivers/net/ethernet/intel/ice/virt/queues.c +++ b/drivers/net/ethernet/intel/ice/virt/queues.c @@ -224,6 +224,24 @@ void ice_vf_ena_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx) wr32(hw, QINT_RQCTL(pfq), reg | QINT_RQCTL_CAUSE_ENA_M); } +/** + * ice_vf_dis_rxq_interrupt - disable Rx queue interrupt via QINT_RQCTL + * @vsi: VSI of the VF to configure + * @q_idx: VF queue index used to determine the queue in the PF's space + */ +static void ice_vf_dis_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx) +{ + struct ice_hw *hw = &vsi->back->hw; + u32 pfq = vsi->rxq_map[q_idx]; + u32 reg; + + reg = rd32(hw, QINT_RQCTL(pfq)); + reg &= ~QINT_RQCTL_CAUSE_ENA_M; + wr32(hw, QINT_RQCTL(pfq), reg); + + ice_flush(hw); +} + /** * ice_vc_ena_qs_msg * @vf: pointer to the VF info @@ -416,6 +434,8 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg) goto error_param; } + for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) + ice_vf_dis_rxq_interrupt(vsi, vf_q_id); bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF); } else if (q_map) { for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) { @@ -436,6 +456,7 @@ int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg) goto error_param; } + ice_vf_dis_rxq_interrupt(vsi, vf_q_id); /* Clear enabled queues flag */ clear_bit(vf_q_id, vf->rxq_ena); } -- cgit v1.2.3 From 3a9de5590da4ffd9e9c541c4c4d492aa2b54cf6e Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Tue, 16 Jun 2026 23:57:42 +0800 Subject: ice: fix memory leak in ice_lbtest_prepare_rings() ice_lbtest_prepare_rings() frees Rx rings only when ice_vsi_start_all_rx_rings() fails. If ice_vsi_setup_rx_rings() fails after allocating some descriptors, or if ice_vsi_cfg_lan() fails after the Rx rings were prepared, the function reaches the Tx cleanup path without releasing the initialized Rx resources. Fix this by adding separate unwind paths for Rx setup failure and LAN configuration failure. The Rx setup failure path releases the partially prepared Rx rings before freeing Tx rings, while later failures first undo the LAN Tx configuration and then release the Rx rings in reverse setup order. 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 an Intel E800 Series adapter available to run the ethtool offline loopback selftest, no runtime testing was able to be performed. Fixes: 0e674aeb0b77 ("ice: Add handler for ethtool selftest") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Reviewed-by: Jacob Keller Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_ethtool.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index 49371b065845..7eb380be7ed2 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -1069,18 +1069,18 @@ static int ice_lbtest_prepare_rings(struct ice_vsi *vsi) status = ice_vsi_cfg_lan(vsi); if (status) - goto err_setup_rx_ring; + goto err_cfg_lan; status = ice_vsi_start_all_rx_rings(vsi); if (status) - goto err_start_rx_ring; + goto err_cfg_lan; return 0; -err_start_rx_ring: - ice_vsi_free_rx_rings(vsi); -err_setup_rx_ring: +err_cfg_lan: ice_vsi_stop_lan_tx_rings(vsi, ICE_NO_RESET, 0); +err_setup_rx_ring: + ice_vsi_free_rx_rings(vsi); err_setup_tx_ring: ice_vsi_free_tx_rings(vsi); -- cgit v1.2.3 From b00be7c6b4bd7da3d510753b27ff6cb7ec647d07 Mon Sep 17 00:00:00 2001 From: Przemyslaw Korba Date: Wed, 20 May 2026 13:50:06 +0200 Subject: ice: suppress DPLL errors during reset recovery During reset recovery, the admin queue returns EBUSY which is expected behavior. However, the DPLL subsystem was logging these as errors and incrementing the error counter, potentially leading to unnecessary warnings and even disabling the DPLL periodic worker if the threshold was reached. Suppress error logging and error counter increments when the admin queue returns EBUSY, as this is expected during reset recovery and not a real failure condition. test case: - ethtool --reset eth3 irq-shared dma-shared filter-shared offload-shared mac-shared phy-shared ram-shared - observe if dmesg EBUSY errors are gone Fixes: d7999f5ea64b ("ice: implement dpll interface to control cgu") Signed-off-by: Przemyslaw Korba Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Reviewed-by: Aleksandr Loktionov Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/ice/ice_dpll.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 30c3a4db7d61..85a74cd6ea1f 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -793,7 +793,7 @@ err: ret, libie_aq_str(pf->hw.adminq.sq_last_status), pin_type_name[pin_type], pin->idx); - else + else if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) dev_err_ratelimited(ice_pf_to_dev(pf), "err:%d %s failed to update %s pin:%u\n", ret, @@ -3024,7 +3024,8 @@ static int ice_dpll_pps_update_phase_offsets(struct ice_pf *pf, *phase_offset_pins_updated = 0; ret = ice_aq_get_cgu_input_pin_measure(&pf->hw, DPLL_TYPE_PPS, meas, ARRAY_SIZE(meas)); - if (ret && pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN) { + if (ret && (pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EAGAIN || + pf->hw.adminq.sq_last_status == LIBIE_AQ_RC_EBUSY)) { return 0; } else if (ret) { dev_err(ice_pf_to_dev(pf), @@ -3086,10 +3087,12 @@ ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init) d->dpll_idx, d->prev_input_idx, d->input_idx, d->dpll_state, d->prev_dpll_state, d->mode); if (ret) { - dev_err(ice_pf_to_dev(pf), - "update dpll=%d state failed, ret=%d %s\n", - d->dpll_idx, ret, - libie_aq_str(pf->hw.adminq.sq_last_status)); + /* EBUSY is expected during reset recovery, don't log error */ + if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) + dev_err(ice_pf_to_dev(pf), + "update dpll=%d state failed, ret=%d %s\n", + d->dpll_idx, ret, + libie_aq_str(pf->hw.adminq.sq_last_status)); return ret; } if (init) { @@ -3158,7 +3161,9 @@ static void ice_dpll_periodic_work(struct kthread_work *work) d->periodic_counter % dp->phase_offset_monitor_period == 0) ret = ice_dpll_pps_update_phase_offsets(pf, &phase_offset_ntf); if (ret) { - d->cgu_state_acq_err_num++; + /* EBUSY is expected during reset recovery */ + if (pf->hw.adminq.sq_last_status != LIBIE_AQ_RC_EBUSY) + d->cgu_state_acq_err_num++; /* stop rescheduling this worker */ if (d->cgu_state_acq_err_num > ICE_CGU_STATE_ACQ_ERR_THRESHOLD) { -- cgit v1.2.3 From 5ffab5b9589c50e4cfc0cf36ffd76c89422d4019 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 12 Jul 2026 14:22:42 +0100 Subject: igc: remove napi_synchronize() in igc_down() When an AF_XDP zero-copy application is killed abruptly, the XSK pool is torn down but NAPI keeps polling. igc_clean_rx_irq_zc() then returns the full budget on every poll, so napi_complete_done() never clears NAPI_STATE_SCHED. igc_down() calls napi_synchronize() before napi_disable(), so it spins forever waiting for that bit and the interface never goes down. Drop the napi_synchronize() and let napi_disable() do the job -- it sets NAPI_STATE_DISABLE, which forces the stuck poll to complete. Reorder it ahead of igc_set_queue_napi() so the NAPI mapping is cleared only after polling has stopped, matching the recent igb fix b1e067240379. Fixes: fc9df2a0b520 ("igc: Enable RX via AF_XDP zero-copy") Suggested-by: Maciej Fijalkowski Cc: stable@vger.kernel.org Signed-off-by: David Carlier Reviewed-by: Maciej Fijalkowski Reviewed-by: Dima Ruinskiy Tested-by: Moriya Kadosh Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igc/igc_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 2c9e2dfd8499..b3883a5a7d7a 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -5352,9 +5352,8 @@ void igc_down(struct igc_adapter *adapter) for (i = 0; i < adapter->num_q_vectors; i++) { if (adapter->q_vector[i]) { - napi_synchronize(&adapter->q_vector[i]->napi); - igc_set_queue_napi(adapter, i, NULL); napi_disable(&adapter->q_vector[i]->napi); + igc_set_queue_napi(adapter, i, NULL); } } -- cgit v1.2.3 From 0565052b7e2f436b7f1541f4849da96dc0aa7a0e Mon Sep 17 00:00:00 2001 From: Matt Vollrath Date: Thu, 16 Apr 2026 23:34:52 -0400 Subject: igbvf: Fix leak in TX DMA error cleanup If an error is encountered while mapping TX buffers, the driver should unmap any buffers already mapped for that skb. Because count is incremented before each frag mapping, it will always match the correct number of unmappings needed when dma_error is reached. Decrementing count before the while loop in dma_error causes an off-by-one error. If any mapping was successful before an unsuccessful mapping, exactly one DMA mapping (the head) would leak. This bug was introduced by a 2010 fix for an endless loop in dma_error. All other affected drivers have already been fixed. Fixes: c1fa347f20f1 ("e1000/e1000e/igb/igbvf/ixgb/ixgbe: Fix tests of unsigned in *_tx_map()") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-4-7-opus Signed-off-by: Matt Vollrath Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/igbvf/netdev.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 0a3d0a1cba43..c686ee120a14 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2190,8 +2190,6 @@ dma_error: buffer_info->time_stamp = 0; buffer_info->length = 0; buffer_info->mapped_as_page = false; - if (count) - count--; /* clear timestamp and dma mappings for remaining portion of packet */ while (count--) { -- cgit v1.2.3 From 816419dfea5c88126f35eb7a1b429a1bf546665e Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sun, 7 Jun 2026 22:57:06 +0800 Subject: e1000: fix memory leak in e1000_probe() In the e1000_probe() path, e1000_sw_init() allocates adapter->tx_ring and adapter->rx_ring. If the subsequent CE4100-specific MDIO BAR mapping fails, the error handling jumps past the ring cleanup code, leaking both allocations. Fix this leak by moving the err_mdio_ioremap label above the ring deallocation logic. This guarantees the proper release of these resources and prevents the memory leak. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc6. An x86_64 allyesconfig build showed no new warnings. As we do not have a CE4100 reference platform to test with, no runtime testing was able to be performed. Fixes: 5377a4160bb65 ("e1000: Add support for the CE4100 reference platform") Cc: stable@vger.kernel.org Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Reviewed-by: Dima Ruinskiy Signed-off-by: Tony Nguyen --- drivers/net/ethernet/intel/e1000/e1000_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 9b09eb144b81..d7f5c6f16142 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -1222,11 +1222,11 @@ err_eeprom: if (hw->flash_address) iounmap(hw->flash_address); +err_mdio_ioremap: kfree(adapter->tx_ring); kfree(adapter->rx_ring); err_dma: err_sw_init: -err_mdio_ioremap: iounmap(hw->ce4100_gbe_mdio_base_virt); iounmap(hw->hw_addr); err_ioremap: -- cgit v1.2.3 From d57e506f6a1e3929611340fae87c1e4823f4d85c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Mon, 20 Jul 2026 17:53:33 +0300 Subject: Bluetooth: ISO: clear iso_data always when detaching conn from hcon When setting conn->hcon = NULL, also conn->hcon->iso_data = NULL is necessary, otherwise later iso_conn_free() will UAF. Fix clearing of iso_data in iso_sock_disconn() Fixes KASAN: slab-use-after-free in iso_conn_hold_unless_zero on iso_sock_release() followed by hci_abort_conn_sync(). Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 2e95a153912c..babba61eb335 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -837,6 +837,7 @@ static void iso_sock_disconn(struct sock *sk) sk->sk_state = BT_DISCONN; iso_conn_lock(iso_pi(sk)->conn); hci_conn_drop(iso_pi(sk)->conn->hcon); + iso_pi(sk)->conn->hcon->iso_data = NULL; iso_pi(sk)->conn->hcon = NULL; iso_conn_unlock(iso_pi(sk)->conn); } -- cgit v1.2.3 From d0a7b48ad0921bd88effaee10bf970ab1d5d0ddd Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Tue, 21 Jul 2026 22:36:07 +0800 Subject: Bluetooth: mgmt: fix UAF in pair command cancellation The pairing completion and authentication failure callbacks look up the pending MGMT_OP_PAIR_DEVICE command by walking hdev->mgmt_pending. The lookup returned a command that was still linked on the shared pending list, without keeping mgmt_pending_lock held for the later dereference and removal. A concurrent MGMT_OP_CANCEL_PAIR_DEVICE request can remove and free the same pending command before the callback uses it. The reverse race is also possible when cancel_pair_device() gets a command from pending_find() and a callback removes it before the cancel path dereferences it. This can lead to a use-after-free and a second list_del(). Make the pairing lookup helpers transfer ownership of the pending command by removing it from hdev->mgmt_pending while holding mgmt_pending_lock. The callbacks and cancel path then complete the command and free it directly, so racing paths cannot find or free the same command again. Take a temporary hci_conn reference in cancel_pair_device() because the command completion drops the reference stored in the pending command. Fixes: e9a416b5ce0c ("Bluetooth: Add mgmt_pair_device command") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Reviewed-by: Ren Wei Reported-by: Vega Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 64 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1db10e0f617f..4fd37ac79986 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3514,11 +3514,13 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data, NULL, 0); } -static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn) +static struct mgmt_pending_cmd *remove_pairing(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; struct mgmt_pending_cmd *cmd; + mutex_lock(&hdev->mgmt_pending_lock); + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { if (cmd->opcode != MGMT_OP_PAIR_DEVICE) continue; @@ -3526,9 +3528,39 @@ static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn) if (cmd->user_data != conn) continue; + list_del(&cmd->list); + mutex_unlock(&hdev->mgmt_pending_lock); return cmd; } + mutex_unlock(&hdev->mgmt_pending_lock); + + return NULL; +} + +static struct mgmt_pending_cmd *remove_pairing_by_addr(struct hci_dev *hdev, + bdaddr_t *bdaddr) +{ + struct mgmt_pending_cmd *cmd; + struct hci_conn *conn; + + mutex_lock(&hdev->mgmt_pending_lock); + + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (cmd->opcode != MGMT_OP_PAIR_DEVICE) + continue; + + conn = cmd->user_data; + if (bacmp(bdaddr, &conn->dst) != 0) + continue; + + list_del(&cmd->list); + mutex_unlock(&hdev->mgmt_pending_lock); + return cmd; + } + + mutex_unlock(&hdev->mgmt_pending_lock); + return NULL; } @@ -3566,10 +3598,10 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete) u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED; struct mgmt_pending_cmd *cmd; - cmd = find_pairing(conn); + cmd = remove_pairing(conn); if (cmd) { cmd->cmd_complete(cmd, status); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); } } @@ -3579,14 +3611,14 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) BT_DBG("status %u", status); - cmd = find_pairing(conn); + cmd = remove_pairing(conn); if (!cmd) { BT_DBG("Unable to find a pending command"); return; } cmd->cmd_complete(cmd, mgmt_status(status)); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); } static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) @@ -3598,14 +3630,14 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) if (!status) return; - cmd = find_pairing(conn); + cmd = remove_pairing(conn); if (!cmd) { BT_DBG("Unable to find a pending command"); return; } cmd->cmd_complete(cmd, mgmt_status(status)); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); } static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, @@ -3762,23 +3794,17 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev); + cmd = remove_pairing_by_addr(hdev, &addr->bdaddr); if (!cmd) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, MGMT_STATUS_INVALID_PARAMS); goto unlock; } - conn = cmd->user_data; - - if (bacmp(&addr->bdaddr, &conn->dst) != 0) { - err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } + conn = hci_conn_get(cmd->user_data); cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, addr, sizeof(*addr)); @@ -3796,6 +3822,8 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, if (conn->conn_reason == CONN_REASON_PAIR_DEVICE) hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); + hci_conn_put(conn); + unlock: hci_dev_unlock(hdev); return err; @@ -10137,14 +10165,14 @@ void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status) ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type); ev.status = status; - cmd = find_pairing(conn); + cmd = remove_pairing(conn); mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); if (cmd) { cmd->cmd_complete(cmd, status); - mgmt_pending_remove(cmd); + mgmt_pending_free(cmd); } } -- cgit v1.2.3 From 8f2f62855a41d1730fb9e8122912bd2c8d6bed5d Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Fri, 24 Jul 2026 00:43:46 +0800 Subject: Bluetooth: mgmt: fix pending command UAF in EIR updates MGMT_OP_SET_LOCAL_NAME is handled asynchronously on powered controllers and can run set_name_sync(). When the controller is BR/EDR capable, set_name_sync() updates the local name and then rebuilds EIR data through eir_create(). The EIR builder walks hdev->uuids, but the UUID list can be changed and entries can be freed by MGMT_OP_ADD_UUID and MGMT_OP_REMOVE_UUID. pending_eir_or_class() is meant to serialize management commands that can change EIR or the class of device, but it did not include MGMT_OP_SET_LOCAL_NAME. In addition, it walked hdev->mgmt_pending without hdev->mgmt_pending_lock even though pending commands are added and removed under that mutex. A racing command completion can therefore remove and free a pending command while pending_eir_or_class() is still inspecting it, leading to a use-after-free in the pending-command list or allowing a local name update to rebuild EIR while UUID entries are being removed. Take hdev->mgmt_pending_lock while scanning hdev->mgmt_pending and treat MGMT_OP_SET_LOCAL_NAME as an EIR/class-affecting pending command on the powered asynchronous path. Check for a conflicting pending command before copying the new short name so a rejected SET_LOCAL_NAME request does not modify hdev->short_name. Fixes: 6fe26f694c82 ("Bluetooth: MGMT: Protect mgmt_pending list with its own lock") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Signed-off-by: Ren Wei Reported-by: Vega Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4fd37ac79986..167d75e34526 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2696,18 +2696,28 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev, static bool pending_eir_or_class(struct hci_dev *hdev) { struct mgmt_pending_cmd *cmd; + bool pending = false; + + mutex_lock(&hdev->mgmt_pending_lock); list_for_each_entry(cmd, &hdev->mgmt_pending, list) { switch (cmd->opcode) { case MGMT_OP_ADD_UUID: case MGMT_OP_REMOVE_UUID: case MGMT_OP_SET_DEV_CLASS: + case MGMT_OP_SET_LOCAL_NAME: case MGMT_OP_SET_POWERED: - return true; + pending = true; + break; } + + if (pending) + break; } - return false; + mutex_unlock(&hdev->mgmt_pending_lock); + + return pending; } static const u8 bluetooth_base_uuid[] = { @@ -4071,6 +4081,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } + if (hdev_is_powered(hdev) && pending_eir_or_class(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, + MGMT_STATUS_BUSY); + goto failed; + } + memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name)); if (!hdev_is_powered(hdev)) { -- cgit v1.2.3 From 47778d2c2087b5d192398f6fddf692d16a5431cf Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Thu, 23 Jul 2026 12:28:06 +0900 Subject: Bluetooth: HIDP: reject frames without a transaction header hidp_recv_ctrl_frame() and hidp_recv_intr_frame() read skb->data[0] before checking that the L2CAP SDU contains a transaction header. A connected HIDP peer can send an empty basic-mode SDU and make both paths use an uninitialized byte from skb tailroom. KMSAN reports the use in hidp_session_run(), with the uninitialized value originating in __alloc_skb() through vhci_write(). The control path produces two reports and the interrupt path produces one. The byte can also be controlled by a malformed lower-layer packet. If an HCI ACL packet contains an L2CAP PDU with a declared zero-length payload followed by an extra 0x15 byte, l2cap_recv_acldata() reduces skb->len to the declared PDU length before dispatch. The current HIDP path nevertheless consumes the extra byte as HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG and terminates the HIDP session. With this change, the same packet is discarded and a subsequent feature report request succeeds. Pull the transaction header with skb_pull_data() and discard frames that do not contain it. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Sangho Lee Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hidp/core.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 0e24c5e2955e..194208d03d18 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -560,16 +560,18 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, static void hidp_recv_ctrl_frame(struct hidp_session *session, struct sk_buff *skb) { - unsigned char hdr, type, param; + unsigned char type, param; + u8 *hdr; int free_skb = 1; BT_DBG("session %p skb %p len %u", session, skb, skb->len); - hdr = skb->data[0]; - skb_pull(skb, 1); + hdr = skb_pull_data(skb, 1); + if (!hdr) + goto free; - type = hdr & HIDP_HEADER_TRANS_MASK; - param = hdr & HIDP_HEADER_PARAM_MASK; + type = *hdr & HIDP_HEADER_TRANS_MASK; + param = *hdr & HIDP_HEADER_PARAM_MASK; switch (type) { case HIDP_TRANS_HANDSHAKE: @@ -590,6 +592,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, break; } +free: if (free_skb) kfree_skb(skb); } @@ -597,14 +600,15 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, static void hidp_recv_intr_frame(struct hidp_session *session, struct sk_buff *skb) { - unsigned char hdr; + u8 *hdr; BT_DBG("session %p skb %p len %u", session, skb, skb->len); - hdr = skb->data[0]; - skb_pull(skb, 1); + hdr = skb_pull_data(skb, 1); + if (!hdr) + goto free; - if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { + if (*hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { hidp_set_timer(session); if (session->input) @@ -616,9 +620,10 @@ static void hidp_recv_intr_frame(struct hidp_session *session, BT_DBG("report len %d", skb->len); } } else { - BT_DBG("Unsupported protocol header 0x%02x", hdr); + BT_DBG("Unsupported protocol header 0x%02x", *hdr); } +free: kfree_skb(skb); } -- cgit v1.2.3 From 90b5df5bf0a1acc811c6ecfe66c503e8268fdf96 Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Thu, 23 Jul 2026 10:53:52 -0400 Subject: regulator: dt-bindings: qcom,rpmh: Add support for PM8350B The PM8350B has only one LDO, which gets its power from a dedicated input. Add support for it. Signed-off-by: Esteban Urrutia Link: https://patch.msgid.link/20260723-pm8350b-ldo-v1-1-42b5428a0d3f@proton.me Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/qcom,rpmh-regulator.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index d93594304651..eed2ce7fa861 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -47,6 +47,7 @@ description: | For PM8150, smps1 - smps10, ldo1 - ldo18 For PM8150L, smps1 - smps8, ldo1 - ldo11, bob, flash, rgb For PM8350, smps1 - smps12, ldo1 - ldo10 + For PM8350B, ldo1 For PM8350C, smps1 - smps10, ldo1 - ldo13, bob For PM8450, smps1 - smps6, ldo1 - ldo4 For PM8550, smps1 - smps6, ldo1 - ldo17, bob1 - bob2 @@ -82,6 +83,7 @@ properties: - qcom,pm8150-rpmh-regulators - qcom,pm8150l-rpmh-regulators - qcom,pm8350-rpmh-regulators + - qcom,pm8350b-rpmh-regulators - qcom,pm8350c-rpmh-regulators - qcom,pm8450-rpmh-regulators - qcom,pm8550-rpmh-regulators @@ -337,6 +339,15 @@ allOf: patternProperties: "^vdd-s([1-9]|1[0-2])-supply$": true + - if: + properties: + compatible: + enum: + - qcom,pm8350b-rpmh-regulators + then: + properties: + vdd-l1-supply: true + - if: properties: compatible: -- cgit v1.2.3 From 6753251cfc9622a90feedc0c0883e3b0a6fcde8a Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Thu, 23 Jul 2026 10:53:53 -0400 Subject: regulator: qcom-rpmh: Add support for PM8350B The PM8350B has only one LDO, which gets its power from a dedicated input. Add support for it. Signed-off-by: Esteban Urrutia Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260723-pm8350b-ldo-v1-2-42b5428a0d3f@proton.me Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 756a4201225e..73856e3df337 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1269,6 +1269,11 @@ static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pm8350b_vreg_data[] = { + RPMH_VREG("ldo1", LDO, 1, &pmic5_nldo, "vdd-l1"), + {} +}; + static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { RPMH_VREG("smps1", SMPS, 1, &pmic5_hfsmps515, "vdd-s1"), RPMH_VREG("smps2", SMPS, 2, &pmic5_ftsmps510, "vdd-s2"), @@ -1868,6 +1873,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm8350-rpmh-regulators", .data = pm8350_vreg_data, }, + { + .compatible = "qcom,pm8350b-rpmh-regulators", + .data = pm8350b_vreg_data, + }, { .compatible = "qcom,pm8350c-rpmh-regulators", .data = pm8350c_vreg_data, -- cgit v1.2.3 From 34f53d27b81a16a02828c8fdfa4e02badc326f17 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Thu, 23 Jul 2026 12:28:07 +0900 Subject: Bluetooth: HIDP: validate numbered report payloads When hidp_get_raw_report() waits for a numbered report, hidp_process_data() compares the expected report number with skb->data[0]. A connected HIDP peer can reply with only a DATA transaction header, leaving the skb empty after the header is removed. KMSAN reports an uninitialized-value use in hidp_session_run(), with the value originating in __alloc_skb() through vhci_write(). The transaction header checks remove the empty-frame reports, but this report remains until the payload check is added. The comparison can also consume a peer-controlled byte beyond the declared L2CAP PDU. A DATA | FEATURE response followed by an extra 0x01 byte made the current code accept that byte as report ID 1 and complete HIDIOCGFEATURE with a zero-byte result. With this change the malformed response is rejected with -EIO, while a subsequent valid response still succeeds. Require a payload byte before comparing a numbered report ID. Unnumbered reports continue to accept an empty payload. Fixes: 0ff1731a1ae5 ("HID: bt: Add support for hidraw HIDIOCGFEATURE and HIDIOCSFEATURE") Cc: stable@vger.kernel.org Signed-off-by: Sangho Lee Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hidp/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 194208d03d18..f5bdf9f1ca63 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -543,9 +543,10 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, } if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && - param == session->waiting_report_type) { + param == session->waiting_report_type) { if (session->waiting_report_number < 0 || - session->waiting_report_number == skb->data[0]) { + (skb->len && + session->waiting_report_number == skb->data[0])) { /* hidp_get_raw_report() is waiting on this report. */ session->report_return = skb; done_with_skb = 0; -- cgit v1.2.3 From c4740e7f23ff9a8210198d8b4703259e21b9f69d Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Thu, 23 Jul 2026 14:48:45 +0800 Subject: Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp l2cap_le_connect_rsp() obtains a channel via __l2cap_get_chan_by_ident() but neither holds a reference nor uses l2cap_chan_hold_unless_zero() before locking and operating on it. A concurrent l2cap_chan_del() triggered by a remote disconnect can free the channel between the lookup and l2cap_chan_lock(), causing a use-after-free. The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() to safely hold a reference, but l2cap_le_connect_rsp() was left unprotected. Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup and l2cap_chan_put() on the exit path, consistent with other L2CAP response handlers. Fixes: f1496dee9cbd ("Bluetooth: Add initial code for LE L2CAP Connect Request") Assisted-by: Claude:deepseek-v4-pro Signed-off-by: Jiale Yao Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/l2cap_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 538ae9aa3479..1156aba4e83c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4820,6 +4820,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, if (!chan) return -EBADSLT; + chan = l2cap_chan_hold_unless_zero(chan); + if (!chan) + return -EBADSLT; + err = 0; l2cap_chan_lock(chan); @@ -4865,6 +4869,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, } l2cap_chan_unlock(chan); + l2cap_chan_put(chan); return err; } -- cgit v1.2.3 From b230e5bf501c5edaf2eb0991cb862ac142031d4b Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Wed, 22 Jul 2026 17:26:14 +0800 Subject: Bluetooth: RFCOMM: validate skb length in rfcomm_recv_frame rfcomm_recv_frame() casts skb->data to struct rfcomm_hdr and dereferences hdr->addr and hdr->ctrl without validating skb->len first. A truncated frame with skb->len less than the minimum header size causes an out-of-bounds read of uninitialized memory. Additionally, a zero-length frame causes skb->len-- to underflow to UINT_MAX, making skb_tail_pointer() read far past the buffer. Commit 23882b828c3c ("Bluetooth: RFCOMM: validate skb length in MCC handlers") fixed the same class of missing-length-check bugs in the MCC sub-handlers, but the top-level rfcomm_recv_frame() was left unfixed. KMSAN reports: BUG: KMSAN: uninit-value in rfcomm_run ... Uninit was created at: __alloc_skb+0x474/0xb60 vhci_write+0xe9/0x870 Fix this by rejecting frames smaller than sizeof(struct rfcomm_hdr) + 1 (the minimum frame must have a 3-byte header and a 1-byte FCS). Signed-off-by: Jiale Yao Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/rfcomm/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 75f7512dec54..2e8c080b4d9e 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1795,6 +1795,11 @@ static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s, return s; } + if (skb->len < sizeof(*hdr) + 1) { + kfree_skb(skb); + return s; + } + dlci = __get_dlci(hdr->addr); type = __get_type(hdr->ctrl); -- cgit v1.2.3 From cdc36db204ffd97b947d64374cf23a210dc74777 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Thu, 23 Jul 2026 23:34:40 +0800 Subject: Bluetooth: hci_sync: Fix advertising data UAFs hci_find_adv_instance() returns an adv_info pointer that is valid only while hdev->lock is held. The advertising command-sync paths perform instance lookups without that lock and, in some cases, retain the pointer while waiting for a controller response. An advertising termination event can therefore interleave as follows: hci_cmd_sync_work hci_rx_work hci_find_adv_instance() __hci_cmd_sync_status() wait for controller reply hci_dev_lock() hci_remove_adv_instance() kfree(adv) adv->scan_rsp_changed = false KASAN reported: BUG: KASAN: slab-use-after-free in hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 Write of size 1 at addr ffff88810a45d21d by task kworker/u17:0/88 Workqueue: hci0 hci_cmd_sync_work Call Trace: hci_set_ext_scan_rsp_data_sync+0x2e1/0x300 hci_schedule_adv_instance_sync+0x390/0x4c0 hci_cmd_sync_work+0x173/0x300 Allocated by task 87: hci_add_adv_instance+0x538/0xac0 add_advertising+0x885/0x1160 Freed by task 89: kfree+0x131/0x3c0 hci_remove_adv_instance+0x1d8/0x3b0 hci_le_ext_adv_term_evt+0x17b/0x730 Protect the instance lookup and payload construction in the extended advertising, scan response, and periodic advertising data paths. Snapshot the advertising parameters under hdev->lock, but release the lock before waiting for the controller. Clear advertising-data dirty bits before issuing their commands and restore them after a failure using a fresh lookup. Likewise, update the reported transmit power through a fresh lookup after the parameter command completes. No adv_info pointer then survives an HCI command wait. Fixes: cba6b758711c ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 2") Cc: stable@vger.kernel.org Suggested-by: Luiz Augusto von Dentz Signed-off-by: Chengfeng Ye Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 131 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 34 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index c0b1fc293b49..aa3d53818812 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -1233,10 +1233,11 @@ static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance, } static int -hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv, +hci_set_ext_adv_params_sync(struct hci_dev *hdev, u8 instance, const struct hci_cp_le_set_ext_adv_params *cp, struct hci_rp_le_set_ext_adv_params *rp) { + struct adv_info *adv; struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp), @@ -1264,11 +1265,15 @@ hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv, if (!rp->status) { hdev->adv_addr_type = cp->own_addr_type; - if (!cp->handle) { + if (!instance) { /* Store in hdev for instance 0 */ hdev->adv_tx_power = rp->tx_power; - } else if (adv) { - adv->tx_power = rp->tx_power; + } else { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (adv) + adv->tx_power = rp->tx_power; + hci_dev_unlock(hdev); } } @@ -1284,9 +1289,13 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance) int err; if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); - if (!adv || !adv->adv_data_changed) + if (!adv || !adv->adv_data_changed) { + hci_dev_unlock(hdev); return 0; + } } len = eir_create_adv_data(hdev, instance, pdu->data, @@ -1297,16 +1306,27 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance) pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG; + if (adv) { + adv->adv_data_changed = false; + hci_dev_unlock(hdev); + } + err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA, struct_size(pdu, data, len), pdu, HCI_CMD_TIMEOUT); - if (err) + if (err) { + if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (adv) + adv->adv_data_changed = true; + hci_dev_unlock(hdev); + } + return err; + } - /* Update data if the command succeed */ - if (adv) { - adv->adv_data_changed = false; - } else { + if (!instance) { memcpy(hdev->adv_data, pdu->data, len); hdev->adv_data_len = len; } @@ -1360,22 +1380,22 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) struct adv_info *adv; bool secondary_adv; - if (instance > 0) { - adv = hci_find_adv_instance(hdev, instance); - if (!adv) - return -EINVAL; - } else { - adv = NULL; - } - /* Updating parameters of an active instance will return a - * Command Disallowed error, so we must first disable the - * instance if it is active. + * Command Disallowed error, so disable it before taking a snapshot. */ - if (adv) { + if (instance > 0) { err = hci_disable_ext_adv_instance_sync(hdev, instance); if (err) return err; + + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (!adv) { + hci_dev_unlock(hdev); + return -EINVAL; + } + } else { + adv = NULL; } flags = hci_adv_instance_flags(hdev, instance); @@ -1386,8 +1406,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) || mgmt_get_connectable(hdev); - if (!is_advertising_allowed(hdev, connectable)) + if (!is_advertising_allowed(hdev, connectable)) { + if (instance) + hci_dev_unlock(hdev); return -EPERM; + } /* Set require_privacy to true only when non-connectable * advertising is used and it is not periodic. @@ -1398,8 +1421,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) err = hci_get_random_address(hdev, require_privacy, adv_use_rpa(hdev, flags), adv, &own_addr_type, &random_addr); - if (err < 0) + if (err < 0) { + if (instance) + hci_dev_unlock(hdev); return err; + } memset(&cp, 0, sizeof(cp)); @@ -1450,6 +1476,9 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp.channel_map = hdev->le_adv_channel_map; cp.handle = adv ? adv->handle : instance; + if (instance) + hci_dev_unlock(hdev); + if (flags & MGMT_ADV_FLAG_SEC_2M) { cp.primary_phy = HCI_ADV_PHY_1M; cp.secondary_phy = HCI_ADV_PHY_2M; @@ -1462,12 +1491,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) cp.secondary_phy = HCI_ADV_PHY_1M; } - err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp); + err = hci_set_ext_adv_params_sync(hdev, instance, &cp, &rp); if (err) return err; /* Update adv data as tx power is known now */ - err = hci_set_ext_adv_data_sync(hdev, cp.handle); + err = hci_set_ext_adv_data_sync(hdev, instance); if (err) return err; @@ -1475,9 +1504,14 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance) own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) && bacmp(&random_addr, BDADDR_ANY)) { /* Check if random address need to be updated */ - if (adv) { - if (!bacmp(&random_addr, &adv->random_addr)) + if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (!adv || !bacmp(&random_addr, &adv->random_addr)) { + hci_dev_unlock(hdev); return 0; + } + hci_dev_unlock(hdev); } else { if (!bacmp(&random_addr, &hdev->random_addr)) return 0; @@ -1499,9 +1533,13 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) int err; if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); - if (!adv || !adv->scan_rsp_changed) + if (!adv || !adv->scan_rsp_changed) { + hci_dev_unlock(hdev); return 0; + } } len = eir_create_scan_rsp(hdev, instance, pdu->data); @@ -1511,15 +1549,27 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG; + if (adv) { + adv->scan_rsp_changed = false; + hci_dev_unlock(hdev); + } + err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA, struct_size(pdu, data, len), pdu, HCI_CMD_TIMEOUT); - if (err) + if (err) { + if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); + if (adv) + adv->scan_rsp_changed = true; + hci_dev_unlock(hdev); + } + return err; + } - if (adv) { - adv->scan_rsp_changed = false; - } else { + if (!instance) { memcpy(hdev->scan_rsp_data, pdu->data, len); hdev->scan_rsp_data_len = len; } @@ -1534,8 +1584,14 @@ static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance) memset(&cp, 0, sizeof(cp)); + if (instance) + hci_dev_lock(hdev); + len = eir_create_scan_rsp(hdev, instance, cp.data); + if (instance) + hci_dev_unlock(hdev); + if (hdev->scan_rsp_data_len == len && !memcmp(cp.data, hdev->scan_rsp_data, len)) return 0; @@ -1670,9 +1726,13 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) struct adv_info *adv = NULL; if (instance) { + hci_dev_lock(hdev); + adv = hci_find_adv_instance(hdev, instance); - if (!adv || !adv->periodic) + if (!adv || !adv->periodic) { + hci_dev_unlock(hdev); return 0; + } } len = eir_create_per_adv_data(hdev, instance, pdu->data); @@ -1681,6 +1741,9 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance) pdu->handle = adv ? adv->handle : instance; pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE; + if (adv) + hci_dev_unlock(hdev); + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA, struct_size(pdu, data, len), pdu, HCI_CMD_TIMEOUT); @@ -6523,7 +6586,7 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev, if (err) return err; - err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp); + err = hci_set_ext_adv_params_sync(hdev, 0, &cp, &rp); if (err) return err; -- cgit v1.2.3 From 0786469ee242952008628ed0e2d386098e2065ab Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:24 +0300 Subject: Bluetooth: ISO: fix CONNECTED -> CLOSED transition on shutdown/release Commit d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") merged a version of the UAF fix that breaks releasing connected ISO sockets. Since hci_conn::iso_data is set to NULL, iso_chan_del() won't be called when the hci_conn disconnects, and the ISO socket does not emit POLLHUP correctly. Fix by retaining full hci_conn <-> iso_conn association while in BT_DISCONNECT state, so that local disconnect via shutdown() follows similar ISO socket code path as remote disconnect. Use a separate flag to track whether hci_conn_drop() is needed, instead of setting iso_conn::hcon = NULL In iso_sock_ready(), disallow disconnecting socket going BT_CONNECTED, in case hcon connects while its drop is pending. Fixes: d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon") Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index babba61eb335..299a9336b5e1 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -24,8 +24,14 @@ static struct bt_sock_list iso_sk_list = { }; /* ---- ISO connections ---- */ +enum { + ISO_CONN_DROPPED, + __ISO_CONN_NUM_FLAGS +}; + struct iso_conn { struct hci_conn *hcon; + DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS); /* @lock: spinlock protecting changes to iso_conn fields */ spinlock_t lock; @@ -107,7 +113,8 @@ static void iso_conn_free(struct kref *ref) if (conn->hcon) { conn->hcon->iso_data = NULL; - hci_conn_drop(conn->hcon); + if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) + hci_conn_drop(conn->hcon); } /* Ensure no more work items will run since hci_conn has been dropped */ @@ -306,6 +313,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, iso_pi(sk)->conn = conn; conn->sk = sk; + clear_bit(ISO_CONN_DROPPED, conn->flags); if (parent) bt_accept_enqueue(parent, sk, true); @@ -835,11 +843,8 @@ static void iso_sock_disconn(struct sock *sk) } sk->sk_state = BT_DISCONN; - iso_conn_lock(iso_pi(sk)->conn); - hci_conn_drop(iso_pi(sk)->conn->hcon); - iso_pi(sk)->conn->hcon->iso_data = NULL; - iso_pi(sk)->conn->hcon = NULL; - iso_conn_unlock(iso_pi(sk)->conn); + if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags)) + hci_conn_drop(iso_pi(sk)->conn->hcon); } static void __iso_sock_close(struct sock *sk) @@ -2042,9 +2047,18 @@ static void iso_sock_ready(struct sock *sk) return; lock_sock(sk); + + switch (sk->sk_state) { + case BT_DISCONN: + case BT_CLOSED: + release_sock(sk); + return; + } + iso_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); + release_sock(sk); } -- cgit v1.2.3 From 89cf154d7c18e6e94a3da83051f3cf2bac317ae2 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:25 +0300 Subject: Bluetooth: ISO: lock sk in iso_sock_getname Accessing iso_pi(sk)->conn requires lock_sock, which is not held here. Fix by adding the lock/release. Fixes: 2df108c227b2 ("Bluetooth: ISO: Fix using BT_SK_PA_SYNC to detect BIS sockets") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 299a9336b5e1..dbb8f43052f0 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1472,6 +1472,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, BT_DBG("sock %p, sk %p", sock, sk); + lock_sock(sk); + addr->sa_family = AF_BLUETOOTH; if (peer) { @@ -1493,6 +1495,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr, sa->iso_bdaddr_type = iso_pi(sk)->src_type; } + release_sock(sk); + return len; } -- cgit v1.2.3 From 4311fd6f429065a8ba208660360a895627a00cf3 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:26 +0300 Subject: Bluetooth: ISO: lock sk in iso_connect_ind Accessing iso_pi(sk)->conn requires lock_sock, which is not taken in the "ev3" part of iso_connect_ind. It may also be NULL if socket has transitioned away from the LISTEN/CONNECT states before locking. Fix by adding lock/release. Recheck hcon is valid after lock acquire where needed. Fixes: 168d9bf9c7f0 ("Bluetooth: ISO: Reassemble PA data for bcast sink") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index dbb8f43052f0..651661833966 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -2369,7 +2369,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) lock_sock(sk); - hcon = iso_pi(sk)->conn->hcon; + hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL; iso_pi(sk)->qos.bcast.encryption = ev2->encryption; if (ev2->num_bis < iso_pi(sk)->bc_num_bis) @@ -2409,9 +2409,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) if (!sk) goto done; - hcon = iso_pi(sk)->conn->hcon; + lock_sock(sk); + + hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL; if (!hcon) - goto done; + goto release3; if (ev3->data_status == LE_PA_DATA_TRUNCATED) { /* The controller was unable to retrieve PA data. */ @@ -2419,12 +2421,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) HCI_MAX_PER_AD_TOT_LEN); hcon->le_per_adv_data_len = 0; hcon->le_per_adv_data_offset = 0; - goto done; + goto release3; } if (hcon->le_per_adv_data_offset + ev3->length > HCI_MAX_PER_AD_TOT_LEN) - goto done; + goto release3; memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset, ev3->data, ev3->length); @@ -2443,18 +2445,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) &base_len); if (!base || base_len > BASE_MAX_LENGTH) - goto done; + goto release3; - lock_sock(sk); memcpy(iso_pi(sk)->base, base, base_len); iso_pi(sk)->base_len = base_len; - release_sock(sk); } else { /* This is a PA data fragment. Keep pa_data_len set to 0 * until all data has been reassembled. */ hcon->le_per_adv_data_len = 0; } + +release3: + release_sock(sk); } else { sk = iso_get_sock(hdev, &hdev->bdaddr, BDADDR_ANY, BT_LISTEN, iso_match_dst, BDADDR_ANY); -- cgit v1.2.3 From e9cb51813d79fc9aae4a2098aab3ab6ebd7fb6c8 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:27 +0300 Subject: Bluetooth: ISO: fix timeout vs sync_timeout typo in check_bcast_qos In iso.c check_bcast_qos(), missing bcast.timeout is not set to its default value, and appears typoed as bcast.sync_timeout. Fix the typo. Fixes: b37cab587aa3 ("Bluetooth: ISO: Don't reject BT_ISO_QOS if parameters are unset") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 651661833966..e51253e5c161 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1796,7 +1796,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos) return false; if (!qos->bcast.timeout) - qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT; + qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT; if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000) return false; -- cgit v1.2.3 From 4e20192d46a685d73e590a60a4a2419a0a8afcbf Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:28 +0300 Subject: Bluetooth: ISO: validate sockaddr_iso first in iso_sock_rebind_bis() iso_sock_rebind_bis() updates socket iso_pi(sk)->bc_num_bis before validating the BIS values, so it's possible to end up with bc_num_bis inconsistent. Assign to iso_pi(sk)->bc_num_bis only after validation. Fixes: 80837140c1f2 ("Bluetooth: ISO: Allow binding a PA sync socket") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index e51253e5c161..5de4a2f886eb 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1039,15 +1039,15 @@ static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa, goto done; } - iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; - - for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++) + for (int i = 0; i < sa->iso_bc->bc_num_bis; i++) if (sa->iso_bc->bc_bis[i] < 0x01 || sa->iso_bc->bc_bis[i] > 0x1f) { err = -EINVAL; goto done; } + iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis; + memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis, iso_pi(sk)->bc_num_bis); -- cgit v1.2.3 From 0d255e63fcf3f13a570d7ac11678fa1164ac015c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:29 +0300 Subject: Bluetooth: ISO: hold sk properly in iso_conn_ready sk deref in iso_conn_ready must be done either under conn->lock, or holding a refcount, to avoid concurrent close. conn->sk is currently accessed without either: [Task 1] [Task 2] iso_sock_release iso_conn_ready sk = conn->sk lock_sock(sk) conn->sk = NULL lock_sock(sk) release_sock(sk) iso_sock_kill(sk) UAF on sk deref Fix possible UAF by holding sk refcount in iso_conn_ready(). Also recheck after lock_sock that the socket is still valid. Adjust locking so conn->sk is cleared only under lock_sock. Fixes: 27c24fda62b60 ("Bluetooth: switch to lock_sock in SCO") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 5de4a2f886eb..80a58275891d 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -805,11 +805,13 @@ static void iso_sock_kill(struct sock *sk) BT_DBG("sk %p state %d", sk, sk->sk_state); /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ + lock_sock(sk); if (iso_pi(sk)->conn) { iso_conn_lock(iso_pi(sk)->conn); iso_pi(sk)->conn->sk = NULL; iso_conn_unlock(iso_pi(sk)->conn); } + release_sock(sk); /* Kill poor orphan */ bt_sock_unlink(&iso_sk_list, sk); @@ -2047,23 +2049,17 @@ static void iso_sock_ready(struct sock *sk) { BT_DBG("sk %p", sk); - if (!sk) - return; - - lock_sock(sk); + lockdep_assert(lockdep_sock_is_held(sk)); switch (sk->sk_state) { case BT_DISCONN: case BT_CLOSED: - release_sock(sk); return; } iso_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); - - release_sock(sk); } static bool iso_match_big(struct sock *sk, void *data) @@ -2093,7 +2089,7 @@ static bool iso_match_dst(struct sock *sk, void *data) static void iso_conn_ready(struct iso_conn *conn) { struct sock *parent = NULL; - struct sock *sk = conn->sk; + struct sock *sk; struct hci_ev_le_big_sync_established *ev = NULL; struct hci_ev_le_pa_sync_established *ev2 = NULL; struct hci_ev_le_per_adv_report *ev3 = NULL; @@ -2102,7 +2098,22 @@ static void iso_conn_ready(struct iso_conn *conn) BT_DBG("conn %p", conn); + iso_conn_lock(conn); + sk = iso_sock_hold(conn); + iso_conn_unlock(conn); + if (sk) { + lock_sock(sk); + + /* conn->sk may have become NULL if racing with sk close, but + * due to held hdev->lock, it can't become different sk. + */ + if (!conn->sk) { + release_sock(sk); + sock_put(sk); + return; + } + /* Attempt to update source address in case of BIS Sender if * the advertisement is using a random address. */ @@ -2115,14 +2126,15 @@ static void iso_conn_ready(struct iso_conn *conn) adv = hci_find_adv_instance(bis->hdev, bis->iso_qos.bcast.bis); if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) { - lock_sock(sk); iso_pi(sk)->src_type = BDADDR_LE_RANDOM; bacpy(&iso_pi(sk)->src, &adv->random_addr); - release_sock(sk); } } - iso_sock_ready(conn->sk); + iso_sock_ready(sk); + + release_sock(sk); + sock_put(sk); } else { hcon = conn->hcon; if (!hcon) -- cgit v1.2.3 From ce57442a379212fe3fda59c9437ee8217eceb5b1 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:30 +0300 Subject: Bluetooth: ISO: fix leaking sk after socket release iso_sock_kill() tests !sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || sock_flag(sk, SOCK_DEAD) for early return, but this is always true since sock_orphan(sk) sets SOCK_DEAD, so the sk reference released by socket always leaks, iso_sock_destruct is never called. The socket reference also leaks when __iso_sock_close() does not set SOCK_ZAPPED, since iso_conn_del() does not call iso_sock_kill() after zapping. Fix by replacing SOCK_DEAD by BT_SK_KILLED flag that is not used for something else, and lock_sock to ensure iso_sock_kill() puts sk only after socket release only once. Release and iso_conn_del may run concurrently. Call iso_sock_kill() from iso_conn_del() to clean sk up after zapping. Remove call to iso_sock_kill() from iso_sock_close(), as it's generally no-op there. Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 80a58275891d..5f0f45a573a7 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -62,6 +62,7 @@ static void iso_sock_kill(struct sock *sk); enum { BT_SK_BIG_SYNC, BT_SK_PA_SYNC, + BT_SK_KILLED, }; struct iso_pinfo { @@ -295,6 +296,7 @@ static void iso_conn_del(struct hci_conn *hcon, int err) iso_sock_clear_timer(sk); iso_chan_del(sk, err); release_sock(sk); + iso_sock_kill(sk); sock_put(sk); } @@ -798,24 +800,29 @@ static void iso_sock_cleanup_listen(struct sock *parent) */ static void iso_sock_kill(struct sock *sk) { + lock_sock(sk); + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || - sock_flag(sk, SOCK_DEAD)) + test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) { + release_sock(sk); return; + } BT_DBG("sk %p state %d", sk, sk->sk_state); /* Sock is dead, so set conn->sk to NULL to avoid possible UAF */ - lock_sock(sk); if (iso_pi(sk)->conn) { iso_conn_lock(iso_pi(sk)->conn); iso_pi(sk)->conn->sk = NULL; iso_conn_unlock(iso_pi(sk)->conn); } - release_sock(sk); /* Kill poor orphan */ bt_sock_unlink(&iso_sk_list, sk); sock_set_flag(sk, SOCK_DEAD); + set_bit(BT_SK_KILLED, &iso_pi(sk)->flags); + + release_sock(sk); sock_put(sk); } @@ -892,7 +899,6 @@ static void iso_sock_close(struct sock *sk) iso_sock_clear_timer(sk); __iso_sock_close(sk); release_sock(sk); - iso_sock_kill(sk); } static void iso_sock_init(struct sock *sk, struct sock *parent) @@ -2040,8 +2046,16 @@ static int iso_sock_release(struct socket *sock) release_sock(sk); } + /* Make sure sk is valid even if iso_conn_del() is concurrent */ + sock_hold(sk); + + lock_sock(sk); sock_orphan(sk); + release_sock(sk); + iso_sock_kill(sk); + + sock_put(sk); return err; } -- cgit v1.2.3 From 200fa1629c57a3ca2b03d3ca63fd3a9bfd910c43 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:31 +0300 Subject: Bluetooth: ISO: avoid deadlocks in iso_sock_timeout iso_sock_timeout() takes lock_sock, so sync disabling the timer while holding that lock may deadlock. iso_sock_timeout() may also run concurrently with iso_conn_del(), which leads to UAF [Task 1] [Task hdev->workqueue] iso_sock_timeout iso_conn_del iso_conn_hold_unless_zero iso_chan_del `------------> iso_conn_put caller frees hcon iso_conn_put iso_conn_free conn->hcon->iso_data = NULL; /* UAF */ Fix the deadlock by removing the disable from the lock_sock sections. Move the timer from iso_conn to iso_pinfo to decouple it from iso_conn which may need to be freed in lock_sock section. Convert some of the clear_timer to disable_timer. Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 60 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 5f0f45a573a7..0cc08416fde5 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -37,8 +37,6 @@ struct iso_conn { spinlock_t lock; struct sock *sk; - struct delayed_work timeout_work; - struct sk_buff *rx_skb; __u32 rx_len; __u16 tx_sn; @@ -81,6 +79,7 @@ struct iso_pinfo { __u8 base_len; __u8 base[BASE_MAX_LENGTH]; struct iso_conn *conn; + struct delayed_work timeout_work; }; static struct bt_iso_qos default_qos; @@ -118,9 +117,6 @@ static void iso_conn_free(struct kref *ref) hci_conn_drop(conn->hcon); } - /* Ensure no more work items will run since hci_conn has been dropped */ - disable_delayed_work_sync(&conn->timeout_work); - kfree_skb(conn->rx_skb); kfree(conn); @@ -161,48 +157,45 @@ static struct sock *iso_sock_hold(struct iso_conn *conn) static void iso_sock_timeout(struct work_struct *work) { - struct iso_conn *conn = container_of(work, struct iso_conn, - timeout_work.work); - struct sock *sk; - - conn = iso_conn_hold_unless_zero(conn); - if (!conn) - return; - - iso_conn_lock(conn); - sk = iso_sock_hold(conn); - iso_conn_unlock(conn); - iso_conn_put(conn); - - if (!sk) - return; + struct iso_pinfo *pi = container_of(work, struct iso_pinfo, + timeout_work.work); + struct sock *sk = &pi->bt.sk; BT_DBG("sock %p state %d", sk, sk->sk_state); lock_sock(sk); - sk->sk_err = ETIMEDOUT; - sk->sk_state_change(sk); + if (!sock_flag(sk, SOCK_ZAPPED)) { + sk->sk_err = ETIMEDOUT; + sk->sk_state_change(sk); + } release_sock(sk); - sock_put(sk); } static void iso_sock_set_timer(struct sock *sk, long timeout) { + lockdep_assert(lockdep_sock_is_held(sk)); + + cancel_delayed_work(&iso_pi(sk)->timeout_work); + if (!iso_pi(sk)->conn) return; BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout); - cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); - schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout); + schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout); } static void iso_sock_clear_timer(struct sock *sk) { - if (!iso_pi(sk)->conn) - return; + BT_DBG("sock %p state %d", sk, sk->sk_state); + cancel_delayed_work(&iso_pi(sk)->timeout_work); +} + +static void iso_sock_disable_timer(struct sock *sk) +{ + lockdep_assert(!lockdep_sock_is_held(sk)); BT_DBG("sock %p state %d", sk, sk->sk_state); - cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); + disable_delayed_work_sync(&iso_pi(sk)->timeout_work); } /* ---- ISO connections ---- */ @@ -227,7 +220,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) kref_init(&conn->ref); spin_lock_init(&conn->lock); - INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout); hcon->iso_data = conn; conn->hcon = hcon; @@ -292,8 +284,9 @@ static void iso_conn_del(struct hci_conn *hcon, int err) return; } + iso_sock_disable_timer(sk); + lock_sock(sk); - iso_sock_clear_timer(sk); iso_chan_del(sk, err); release_sock(sk); iso_sock_kill(sk); @@ -800,6 +793,8 @@ static void iso_sock_cleanup_listen(struct sock *parent) */ static void iso_sock_kill(struct sock *sk) { + iso_sock_disable_timer(sk); + lock_sock(sk); if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || @@ -895,8 +890,9 @@ static void __iso_sock_close(struct sock *sk) /* Must be called on unlocked socket. */ static void iso_sock_close(struct sock *sk) { + iso_sock_disable_timer(sk); + lock_sock(sk); - iso_sock_clear_timer(sk); __iso_sock_close(sk); release_sock(sk); } @@ -965,6 +961,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock, iso_pi(sk)->qos = default_qos; iso_pi(sk)->sync_handle = -1; + INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout); + bt_sock_link(&iso_sk_list, sk); return sk; } -- cgit v1.2.3 From aa9f7cb2bd3a2be998ceb739fc9a2f986eba43eb Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:32 +0300 Subject: Bluetooth: ISO: ensure no dangling hcon references in iso_conn After iso_conn_del(), ISO sockets should not dereference the hcon any more. Currently, clearing iso_conn::hcon relies on iso_conn_del() releasing the last reference to the iso_conn. Simplify this by explicitly clearing conn->hcon in iso_conn_del(), to avoid more complex reasoning on races about who holds the last reference. Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 0cc08416fde5..bfd39baa8503 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -263,6 +263,7 @@ static void iso_chan_del(struct sock *sk, int err) } static void iso_conn_del(struct hci_conn *hcon, int err) + __must_hold(&hcon->hdev->lock) { struct iso_conn *conn = hcon->iso_data; struct sock *sk; @@ -277,11 +278,10 @@ static void iso_conn_del(struct hci_conn *hcon, int err) iso_conn_lock(conn); sk = iso_sock_hold(conn); iso_conn_unlock(conn); - iso_conn_put(conn); if (!sk) { iso_conn_put(conn); - return; + goto done; } iso_sock_disable_timer(sk); @@ -291,6 +291,15 @@ static void iso_conn_del(struct hci_conn *hcon, int err) release_sock(sk); iso_sock_kill(sk); sock_put(sk); + +done: + /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */ + iso_conn_lock(conn); + conn->hcon = NULL; + hcon->iso_data = NULL; + iso_conn_unlock(conn); + + iso_conn_put(conn); } static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, @@ -306,6 +315,11 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, return -EBUSY; } + if (!conn->hcon) { + BT_ERR("conn->hcon missing"); + return -EIO; + } + iso_pi(sk)->conn = conn; conn->sk = sk; clear_bit(ISO_CONN_DROPPED, conn->flags); @@ -2500,6 +2514,7 @@ done: } static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) + __must_hold(&hcon->hdev->lock) { if (hcon->type != CIS_LINK && hcon->type != BIS_LINK && hcon->type != PA_LINK) { @@ -2511,8 +2526,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) struct hci_link *link, *t; list_for_each_entry_safe(link, t, &hcon->link_list, - list) + list) { + lockdep_assert_held(&link->conn->hdev->lock); iso_conn_del(link->conn, bt_to_errno(status)); + } return; } @@ -2542,6 +2559,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) } static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason) + __must_hold(&hcon->hdev->lock) { if (hcon->type != CIS_LINK && hcon->type != BIS_LINK && hcon->type != PA_LINK) -- cgit v1.2.3 From fdfde532ab1caa165fcd8985001157ac8b4db365 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:33 +0300 Subject: Bluetooth: ISO: fix refcounting of iso_conn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iso_conn_del() and iso_chan_del() have a race that results to double-put of iso_conn: [Task hdev->workqueue] [Task 2] iso_conn_del iso_chan_del iso_conn_hold_unless_zero iso_conn_lock iso_conn_lock conn->sk = NULL iso_conn_unlock sk = iso_sock_hold(conn) <---------´ if (!sk) iso_conn_put iso_conn_put iso_conn_put /* UAF */ The extra put for !sk in iso_conn_del() is currently required since failing iso_chan_add() may leave iso_conn not associated with any sk. Fix by having iso_pi(sk)->conn own refcount when non-NULL, so iso_conn_del does not need to put it. Adjust the iso_conn_add() refcounting so that conn is put if it does not get associated with an sk. Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index bfd39baa8503..30de99ba4b30 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -108,9 +108,6 @@ static void iso_conn_free(struct kref *ref) BT_DBG("conn %p", conn); - if (conn->sk) - iso_pi(conn->sk)->conn = NULL; - if (conn->hcon) { conn->hcon->iso_data = NULL; if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) @@ -145,6 +142,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn) return conn; } +static struct iso_conn *iso_conn_hold(struct iso_conn *conn) +{ + BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref)); + + kref_get(&conn->ref); + return conn; +} + static struct sock *iso_sock_hold(struct iso_conn *conn) { if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk)) @@ -210,7 +215,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) conn->hcon = hcon; iso_conn_unlock(conn); } - iso_conn_put(conn); return conn; } @@ -279,10 +283,8 @@ static void iso_conn_del(struct hci_conn *hcon, int err) sk = iso_sock_hold(conn); iso_conn_unlock(conn); - if (!sk) { - iso_conn_put(conn); + if (!sk) goto done; - } iso_sock_disable_timer(sk); @@ -320,7 +322,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, return -EIO; } - iso_pi(sk)->conn = conn; + iso_pi(sk)->conn = iso_conn_hold(conn); conn->sk = sk; clear_bit(ISO_CONN_DROPPED, conn->flags); @@ -427,6 +429,7 @@ static int iso_connect_bis(struct sock *sk) } err = iso_chan_add(conn, sk, NULL); + iso_conn_put(conn); if (err) goto unlock; @@ -529,6 +532,7 @@ static int iso_connect_cis(struct sock *sk) } err = iso_chan_add(conn, sk, NULL); + iso_conn_put(conn); if (err) goto unlock; @@ -1310,10 +1314,9 @@ static int iso_listen_bis(struct sock *sk) } err = iso_chan_add(conn, sk, NULL); - if (err) { - hci_conn_drop(hcon); + iso_conn_put(conn); + if (err) goto unlock; - } unlock: release_sock(sk); @@ -2551,8 +2554,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) struct iso_conn *conn; conn = iso_conn_add(hcon); - if (conn) + if (conn) { iso_conn_ready(conn); + iso_conn_put(conn); + } } else { iso_conn_del(hcon, bt_to_errno(status)); } -- cgit v1.2.3 From af24e338bf5dafb80f42baa9a0b9e9b57b1c5d9c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Fri, 24 Jul 2026 23:20:34 +0300 Subject: Bluetooth: ISO: fix race of kfree vs kref_get_unless_zero hci_conn::iso_data is accessed and modified without lock or RCU. This leads to a race [Task hdev->workqueue] [Task 2] iso_recv iso_conn_put(conn) conn = LOAD hcon->iso_data iso_conn_free(conn) iso_conn_hold_unless_zero(conn) hcon->iso_data = NULL kfree(conn) kref_get_unless_zero(&conn->ref) /* UAF */ and also to races in iso_conn_add() vs. iso_conn_free(). Fix by adding spinlock hci_conn::proto_lock and using it to guard hci_conn::iso_data. Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 4 ++- net/bluetooth/hci_conn.c | 2 ++ net/bluetooth/iso.c | 64 +++++++++++++++++++++++++++++++++------- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e7133ff87fbf..3df59849dcbe 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -767,9 +767,11 @@ struct hci_conn { struct dentry *debugfs; struct hci_dev *hdev; + + spinlock_t proto_lock; /* lock guarding protocol data */ void *l2cap_data; void *sco_data; - void *iso_data; + void *iso_data __guarded_by(&proto_lock); struct list_head link_list; struct hci_conn *parent; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 1966cd153d97..ebb04badf10c 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1123,6 +1123,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle); INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout); + spin_lock_init(&conn->proto_lock); + atomic_set(&conn->refcnt, 0); hci_dev_hold(hdev); diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 30de99ba4b30..a461c8a4efed 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -109,9 +109,16 @@ static void iso_conn_free(struct kref *ref) BT_DBG("conn %p", conn); if (conn->hcon) { - conn->hcon->iso_data = NULL; - if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) - hci_conn_drop(conn->hcon); + spin_lock(&conn->hcon->proto_lock); + + /* Check we are not racing with iso_conn_add */ + if (conn->hcon->iso_data == conn) { + conn->hcon->iso_data = NULL; + if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) + hci_conn_drop(conn->hcon); + } + + spin_unlock(&conn->hcon->proto_lock); } kfree_skb(conn->rx_skb); @@ -126,7 +133,21 @@ static void iso_conn_put(struct iso_conn *conn) BT_DBG("conn %p refcnt %d", conn, kref_read(&conn->ref)); + /* The following race vs. iso_conn_del() is possible: + * + * 1. conn->hcon != NULL here + * 2. kref_put puts the last reference + * 3. concurrent iso_conn_del() gets iso_conn_hold_unless_zero() -> NULL + * and returns immediately, so conn->hcon is not cleared + * 4. iso_conn_free() dereferences conn->hcon + * + * To avoid UAF in step 4, take RCU before decrementing the refcount. + */ + rcu_read_lock(); + kref_put(&conn->ref, iso_conn_free); + + rcu_read_unlock(); } static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn) @@ -205,22 +226,28 @@ static void iso_sock_disable_timer(struct sock *sk) /* ---- ISO connections ---- */ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) + __must_hold(&hcon->hdev->lock) { - struct iso_conn *conn = hcon->iso_data; + struct iso_conn *conn; + + spin_lock(&hcon->proto_lock); - conn = iso_conn_hold_unless_zero(conn); + conn = iso_conn_hold_unless_zero(hcon->iso_data); if (conn) { if (!conn->hcon) { iso_conn_lock(conn); conn->hcon = hcon; iso_conn_unlock(conn); } + spin_unlock(&hcon->proto_lock); return conn; } - conn = kzalloc_obj(*conn); - if (!conn) + conn = kzalloc_obj(*conn, GFP_ATOMIC); + if (!conn) { + spin_unlock(&hcon->proto_lock); return NULL; + } kref_init(&conn->ref); spin_lock_init(&conn->lock); @@ -229,6 +256,8 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) conn->hcon = hcon; conn->tx_sn = 0; + spin_unlock(&hcon->proto_lock); + BT_DBG("hcon %p conn %p", hcon, conn); return conn; @@ -269,10 +298,12 @@ static void iso_chan_del(struct sock *sk, int err) static void iso_conn_del(struct hci_conn *hcon, int err) __must_hold(&hcon->hdev->lock) { - struct iso_conn *conn = hcon->iso_data; + struct iso_conn *conn; struct sock *sk; - conn = iso_conn_hold_unless_zero(conn); + spin_lock(&hcon->proto_lock); + conn = iso_conn_hold_unless_zero(hcon->iso_data); + spin_unlock(&hcon->proto_lock); if (!conn) return; @@ -296,10 +327,12 @@ static void iso_conn_del(struct hci_conn *hcon, int err) done: /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */ + spin_lock(&hcon->proto_lock); iso_conn_lock(conn); conn->hcon = NULL; hcon->iso_data = NULL; iso_conn_unlock(conn); + spin_unlock(&hcon->proto_lock); iso_conn_put(conn); } @@ -421,6 +454,8 @@ static int iso_connect_bis(struct sock *sk) iso_pi(sk)->bc_sid = hcon->sid; } + lockdep_assert_held(&hcon->hdev->lock); + conn = iso_conn_add(hcon); if (!conn) { hci_conn_drop(hcon); @@ -524,6 +559,8 @@ static int iso_connect_cis(struct sock *sk) } } + lockdep_assert_held(&hcon->hdev->lock); + conn = iso_conn_add(hcon); if (!conn) { hci_conn_drop(hcon); @@ -855,8 +892,8 @@ static void iso_sock_disconn(struct sock *sk) */ if (bis_sk) { hcon->state = BT_OPEN; - hcon->iso_data = NULL; - iso_pi(sk)->conn->hcon = NULL; + set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags); + iso_sock_clear_timer(sk); iso_chan_del(sk, bt_to_errno(hcon->abort_reason)); sock_put(bis_sk); @@ -1306,6 +1343,8 @@ static int iso_listen_bis(struct sock *sk) goto unlock; } + lockdep_assert_held(&hcon->hdev->lock); + conn = iso_conn_add(hcon); if (!conn) { hci_conn_drop(hcon); @@ -2591,7 +2630,10 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) return -ENOENT; } + spin_lock(&hcon->proto_lock); conn = iso_conn_hold_unless_zero(hcon->iso_data); + spin_unlock(&hcon->proto_lock); + hcon = NULL; hci_dev_unlock(hdev); -- cgit v1.2.3 From b640ff9af3c809ff5ea2077fbba17df1594ec1e4 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sat, 25 Jul 2026 01:54:40 -0700 Subject: Bluetooth: btintel: Validate length before parsing diagnostics TLV btintel_diagnostics() accesses tlv->val[0] without first validating that the diagnostics VSE is long enough to contain that field, so may cause reading data beyond the received frame. Fix by validating the length before access. Fixes: af395330abed ("Bluetooth: btintel: Add Intel devcoredump support") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btintel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 5e9cac090bd8..bf567b7c5f00 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -3771,6 +3771,9 @@ static int btintel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) { struct intel_tlv *tlv = (void *)&skb->data[5]; + if (skb->len < 5 + sizeof(*tlv) + sizeof(tlv->val[0])) + goto recv_frame; + /* The first event is always an event type TLV */ if (tlv->type != INTEL_TLV_TYPE_ID) goto recv_frame; -- cgit v1.2.3 From 5761d003daa987ac81463f570713ce9c9dd204e5 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:17 +0300 Subject: Bluetooth: hci_conn: hold conn reference in abort_conn_sync() There is theoretical UAF if the conn is freed while the hci_sync task is running. Hold refcount to avoid that. Fixes: 227a0cdf4a02 ("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/hci_conn.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ebb04badf10c..b1f911fd4ad6 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -3165,6 +3165,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data) return hci_abort_conn_sync(hdev, conn, conn->abort_reason); } +static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err) +{ + struct hci_conn *conn = data; + + hci_conn_put(conn); +} + int hci_abort_conn(struct hci_conn *conn, u8 reason) { struct hci_dev *hdev = conn->hdev; @@ -3190,7 +3197,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) * as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does * already queue its callback on cmd_sync_work. */ - err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL); + err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn), + abort_conn_destroy); + if (err) + hci_conn_put(conn); return (err == -EEXIST) ? 0 : err; } -- cgit v1.2.3 From 2f5d635ad5906b0235bc0c870e8beba3116e1e98 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:18 +0300 Subject: Bluetooth: hci_sync: hold conn in hci_connect_acl/le_sync() callbacks There is theoretical UAF if the conn is freed while the hci_sync task is running. Hold refcount to avoid that. Fixes: 881559af5f5c ("Bluetooth: hci_sync: Attempt to dequeue connection attempt") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index aa3d53818812..79cd52974eb0 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7152,12 +7152,23 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) return err; } +static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data, + int err) +{ + struct hci_conn *conn = data; + + hci_conn_put(conn); +} + int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) { int err; - err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn, - NULL); + err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, + hci_conn_get(conn), + hci_acl_create_conn_sync_complete); + if (err) + hci_conn_put(conn); return (err == -EEXIST) ? 0 : err; } @@ -7168,36 +7179,41 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "err %d", err); if (err == -ECANCELED) - return; + goto done; hci_dev_lock(hdev); if (!hci_conn_valid(hdev, conn)) - goto done; + goto unlock; if (!err) { hci_connect_le_scan_cleanup(conn, 0x00); - goto done; + goto unlock; } /* Check if connection is still pending */ if (conn != hci_lookup_le_connect(hdev)) - goto done; + goto unlock; /* Flush to make sure we send create conn cancel command if needed */ flush_delayed_work(&conn->le_conn_timeout); hci_conn_failed(conn, bt_status(err)); -done: +unlock: hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) { int err; - err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn, + err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, + hci_conn_get(conn), create_le_conn_complete); + if (err) + hci_conn_put(conn); return (err == -EEXIST) ? 0 : err; } -- cgit v1.2.3 From 56e78b670356caab0b607e8aad4cf819a1909d07 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:19 +0300 Subject: Bluetooth: hci_sync: hold conn in hci_connect_big_sync() callback There is theoretical UAF if the conn is freed while the hci_sync task is running. Hold refcount to avoid that. Handle NULL hcon, return 0 + do nothing to match the previous behavior. Fixes: 024421cf3992 ("Bluetooth: hci_conn: Fix not setting timeout for BIG Create Sync") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 79cd52974eb0..cdc7dff7054c 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7522,10 +7522,12 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "err %d", err); if (err == -ECANCELED) - return; + goto done; - if (hci_conn_valid(hdev, conn)) - clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags); + clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags); + +done: + hci_conn_put(conn); } static int hci_le_big_create_sync(struct hci_dev *hdev, void *data) @@ -7577,8 +7579,14 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn) { int err; - err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn, + if (!conn) + return 0; + + err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, + hci_conn_get(conn), create_big_complete); + if (err) + hci_conn_put(conn); return (err == -EEXIST) ? 0 : err; } -- cgit v1.2.3 From 44fc74069d8988f2825246f9401218e29de2c0ab Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:20 +0300 Subject: Bluetooth: hci_sync: hold conn in hci_connect_pa_sync() callback There is theoretical UAF if the conn is freed while the hci_sync task is running. Hold refcount to avoid that. 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 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index cdc7dff7054c..f50d7cd3a331 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7336,7 +7336,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "err %d", err); if (err == -ECANCELED) - return; + goto done; hci_dev_lock(hdev); @@ -7360,6 +7360,8 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err) unlock: hci_dev_unlock(hdev); +done: + hci_conn_put(conn); } static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn, @@ -7510,8 +7512,11 @@ int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn) { int err; - err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn, + err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, + hci_conn_get(conn), create_pa_complete); + if (err) + hci_conn_put(conn); return (err == -EEXIST) ? 0 : err; } -- cgit v1.2.3 From abf9753edf3f88282c44a605f3945d8d4f8dd86c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:21 +0300 Subject: Bluetooth: hci_sync: hold conn in hci_past_sync() callback Avoids giving freed pointers to hci_conn_valid(), which kmalloc may have reused. Hold refcount to avoid that. Fixes: d3413703d5f8 ("Bluetooth: ISO: Add support to bind to trigger PAST") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index f50d7cd3a331..0118342ac7ea 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -7606,6 +7606,8 @@ static void past_complete(struct hci_dev *hdev, void *data, int err) bt_dev_dbg(hdev, "err %d", err); + hci_conn_put(past->conn); + hci_conn_put(past->le); kfree(past); } @@ -7670,8 +7672,8 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le) if (!data) return -ENOMEM; - data->conn = conn; - data->le = le; + data->conn = hci_conn_get(conn); + data->le = hci_conn_get(le); if (conn->role == HCI_ROLE_MASTER) err = hci_cmd_sync_queue_once(conn->hdev, @@ -7681,8 +7683,11 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le) err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync, data, past_complete); - if (err) + if (err) { + hci_conn_put(data->conn); + hci_conn_put(data->le); kfree(data); + } return (err == -EEXIST) ? 0 : err; } -- cgit v1.2.3 From 2c1e4e00613dfd105f978be2276e5e265801ec9f Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:22 +0300 Subject: Bluetooth: hci_sync: fix hci_conn_del() use in hci_le_create_conn_sync hci_conn_del() caller must hold hdev->lock, check the conn was not concurrently deleted, and usually inform socket the conn is going to be deleted. Use hci_abort_conn_sync() instead of calling hci_conn_del() without locks etc. Fixes: 8e8b92ee60de5 ("Bluetooth: hci_sync: Add hci_le_create_conn_sync") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 0118342ac7ea..10bc4c71509f 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6757,7 +6757,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && hdev->le_scan_type == LE_SCAN_ACTIVE && !hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) { - hci_conn_del(conn); + conn->state = BT_OPEN; + hci_abort_conn_sync(hdev, conn, + HCI_ERROR_REJ_LIMITED_RESOURCES); hci_conn_put(conn); return -EBUSY; } -- cgit v1.2.3 From c0a9dcd2be398eee505d4b254ec3a845aa8ab189 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Jul 2026 12:59:23 +0300 Subject: Bluetooth: hci_sync: remove unnecessary hci_conn_get in create_conn_sync hci_conn_get() without already held reference is data race against concurrent deletion. In previous patches, the refcount has been changed to be taken before starting the hci_sync task, so remove these extra get() + put() as they are not needed. Fixes: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()") Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 10bc4c71509f..c8d14128c363 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6741,11 +6741,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) bt_dev_dbg(hdev, "conn %p", conn); - /* Hold a reference so conn stays valid for the HCI_CONN_CREATE - * clear_bit() at done. - */ - hci_conn_get(conn); - clear_bit(HCI_CONN_SCANNING, &conn->flags); conn->state = BT_CONNECT; @@ -6760,7 +6755,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data) conn->state = BT_OPEN; hci_abort_conn_sync(hdev, conn, HCI_ERROR_REJ_LIMITED_RESOURCES); - hci_conn_put(conn); return -EBUSY; } @@ -6858,7 +6852,6 @@ done: /* Re-enable advertising after the connection attempt is finished. */ hci_resume_advertising_sync(hdev); - hci_conn_put(conn); return err; } @@ -7133,11 +7126,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) else cp.role_switch = 0x00; - /* Hold a reference so conn stays valid for the HCI_CONN_CREATE - * clear_bit() below. - */ - hci_conn_get(conn); - /* Mark create connection in flight so hci_cancel_connect_sync() can * cancel it while blocking on the connection complete event. */ @@ -7149,7 +7137,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) conn->conn_timeout, NULL); clear_bit(HCI_CONN_CREATE, &conn->flags); - hci_conn_put(conn); return err; } -- cgit v1.2.3 From b186c18c4843dd58adc29443369bddc71cb626a3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Jul 2026 17:57:32 +0200 Subject: Bluetooth: btmtk: Fix short read errors in btmtk_usb_uhw_reg_read() If btmtk_usb_uhw_reg_read() gets a "short" read from a device, it will accidentally treat that as a "real" read and populate the returned value with some unknown and probably totally invalid data. Fix this logic error up by calling usb_control_msg_recv() which guarantees a "full" read happens, and then simplify the error checking for when btmtk_usb_uhw_reg_read() is called. Note, one caller of btmtk_usb_uhw_reg_read() does not check the return value, but as we pre-initialize the return value as 0, an incorrect read will not do anything wrong. Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmtk.c | 50 +++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 02a96342e964..6f060e4433db 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -804,30 +804,24 @@ static int btmtk_usb_uhw_reg_write(struct hci_dev *hdev, u32 reg, u32 val) static int btmtk_usb_uhw_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) { struct btmtk_data *data = hci_get_priv(hdev); - int pipe, err; - void *buf; - - buf = kzalloc(4, GFP_KERNEL); - if (!buf) - return -ENOMEM; + u8 buf[sizeof(u32)]; + int err; - pipe = usb_rcvctrlpipe(data->udev, 0); - err = usb_control_msg(data->udev, pipe, 0x01, - 0xDE, - reg >> 16, reg & 0xffff, - buf, 4, USB_CTRL_GET_TIMEOUT); - if (err < 0) { + *val = 0; + err = usb_control_msg_recv(data->udev, 0, 0x01, + 0xDE, + reg >> 16, reg & 0xffff, + buf, sizeof(buf), USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); + if (err) { bt_dev_err(hdev, "Failed to read uhw reg(%d)", err); - goto err_free_buf; + return err; } *val = get_unaligned_le32(buf); bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val); -err_free_buf: - kfree(buf); - - return err; + return 0; } static int btmtk_usb_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) @@ -877,7 +871,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (dev_id == 0x7922) { err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); - if (err < 0) + if (err) return err; val |= 0x00002020; err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val); @@ -887,7 +881,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); - if (err < 0) + if (err) return err; val |= BIT(0); err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val); @@ -896,14 +890,14 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) msleep(100); } else if (dev_id == 0x7925 || dev_id == 0x6639) { err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); - if (err < 0) + if (err) return err; val |= (1 << 5); err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val); if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); - if (err < 0) + if (err) return err; val &= 0xFFFF00FF; val |= (1 << 13); @@ -914,7 +908,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val); - if (err < 0) + if (err) return err; val |= (1 << 0); err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val); @@ -924,13 +918,13 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val); - if (err < 0) + if (err) return err; err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF); if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val); - if (err < 0) + if (err) return err; msleep(100); } else { @@ -940,7 +934,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_WDT_STATUS, &val); - if (err < 0) + if (err) return err; /* Reset the bluetooth chip via USB interface. */ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 1); @@ -950,13 +944,13 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val); - if (err < 0) + if (err) return err; err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF); if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val); - if (err < 0) + if (err) return err; /* MT7921 need to delay 20ms between toggle reset bit */ msleep(20); @@ -964,7 +958,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) if (err < 0) return err; err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val); - if (err < 0) + if (err) return err; } -- cgit v1.2.3 From 0cc4b5649ae83deb8222100dba31aa0f100a19cd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Jul 2026 17:57:33 +0200 Subject: Bluetooth: btmtk: Fix short read errors in btmtk_usb_reg_read() If btmtk_usb_reg_read() gets a "short" read from a device, it will accidentally treat that as a "real" read and populate the returned value with some unknown and probably totally invalid data. Fix this logic error up by calling usb_control_msg_recv() which guarantees a "full" read happens, and then simplify the error checking for when btmtk_usb_reg_read() is called (it's really just btmtk_usb_id_get() that calls btmtk_usb_reg_read(), so fix up those return sites. Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btmtk.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 6f060e4433db..66b346761043 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -827,27 +827,21 @@ static int btmtk_usb_uhw_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) static int btmtk_usb_reg_read(struct hci_dev *hdev, u32 reg, u32 *val) { struct btmtk_data *data = hci_get_priv(hdev); - int pipe, err, size = sizeof(u32); - void *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; + u8 buf[sizeof(u32)]; + int err; - pipe = usb_rcvctrlpipe(data->udev, 0); - err = usb_control_msg(data->udev, pipe, 0x63, - USB_TYPE_VENDOR | USB_DIR_IN, - reg >> 16, reg & 0xffff, - buf, size, USB_CTRL_GET_TIMEOUT); + *val = 0; + err = usb_control_msg_recv(data->udev, 0, 0x63, + USB_TYPE_VENDOR | USB_DIR_IN, + reg >> 16, reg & 0xffff, + buf, sizeof(buf), USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); if (err < 0) - goto err_free_buf; + return err; *val = get_unaligned_le32(buf); -err_free_buf: - kfree(buf); - - return err; + return 0; } static int btmtk_usb_id_get(struct hci_dev *hdev, u32 reg, u32 *id) @@ -974,7 +968,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id) } err = btmtk_usb_id_get(hdev, 0x70010200, &val); - if (err < 0 || (!val && dev_id != 0x6639)) + if (err || (!val && dev_id != 0x6639)) bt_dev_err(hdev, "Can't get device id, subsys reset fail."); return err; @@ -1318,24 +1312,24 @@ int btmtk_usb_setup(struct hci_dev *hdev) calltime = ktime_get(); err = btmtk_usb_id_get(hdev, 0x80000008, &dev_id); - if (err < 0) { + if (err) { bt_dev_err(hdev, "Failed to get device id (%d)", err); return err; } if (!dev_id || dev_id != 0x7663) { err = btmtk_usb_id_get(hdev, 0x70010200, &dev_id); - if (err < 0) { + if (err) { bt_dev_err(hdev, "Failed to get device id (%d)", err); return err; } err = btmtk_usb_id_get(hdev, 0x80021004, &fw_version); - if (err < 0) { + if (err) { bt_dev_err(hdev, "Failed to get fw version (%d)", err); return err; } err = btmtk_usb_id_get(hdev, 0x70010020, &fw_flavor); - if (err < 0) { + if (err) { bt_dev_err(hdev, "Failed to get fw flavor (%d)", err); return err; } -- cgit v1.2.3 From cac43d360c928bc0cbbd18809632388265649761 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 27 Jul 2026 17:57:34 +0200 Subject: Bluetooth: btusb: Fix short read errors in btusb_qca_send_vendor_req() If btusb_qca_send_vendor_req() gets a "short" read from a device, it will accidentally treat that as a "real" read and populate the returned value with some unknown and probably totally invalid data. Fix this logic error up by calling usb_control_msg_recv() which guarantees a "full" read happens, and then simplify the error checking for when btusb_qca_send_vendor_req() is called. Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Luiz Augusto von Dentz --- drivers/bluetooth/btusb.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 8f7ed469cac6..184e95c1625e 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3424,28 +3424,16 @@ static const char *qca_get_fw_subdirectory(const struct qca_version *ver) static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request, void *data, u16 size) { - int pipe, err; - u8 *buf; - - buf = kmalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; + int err; /* Found some of USB hosts have IOT issues with ours so that we should * not wait until HCI layer is ready. */ - pipe = usb_rcvctrlpipe(udev, 0); - err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN, - 0, 0, buf, size, USB_CTRL_GET_TIMEOUT); - if (err < 0) { + err = usb_control_msg_recv(udev, 0, request, USB_TYPE_VENDOR | USB_DIR_IN, + 0, 0, data, size, USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); + if (err) dev_err(&udev->dev, "Failed to access otp area (%d)", err); - goto done; - } - - memcpy(data, buf, size); - -done: - kfree(buf); return err; } @@ -3652,7 +3640,7 @@ static bool btusb_qca_need_patch(struct usb_device *udev) struct qca_version ver; if (btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, - sizeof(ver)) < 0) + sizeof(ver))) return false; /* only low ROM versions need patches */ return !(le32_to_cpu(ver.rom_version) & ~0xffffU); @@ -3670,7 +3658,7 @@ static int btusb_setup_qca(struct hci_dev *hdev) err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, sizeof(ver)); - if (err < 0) + if (err) return err; ver_rom = le32_to_cpu(ver.rom_version); @@ -3693,7 +3681,7 @@ static int btusb_setup_qca(struct hci_dev *hdev) err = btusb_qca_send_vendor_req(udev, QCA_CHECK_STATUS, &status, sizeof(status)); - if (err < 0) + if (err) return err; if (!(status & QCA_PATCH_UPDATED)) { @@ -3704,7 +3692,7 @@ static int btusb_setup_qca(struct hci_dev *hdev) err = btusb_qca_send_vendor_req(udev, QCA_GET_TARGET_VERSION, &ver, sizeof(ver)); - if (err < 0) + if (err) return err; btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); -- cgit v1.2.3 From abd93c85c8667add738ee82aeab95dd9fc8265a2 Mon Sep 17 00:00:00 2001 From: Aldo Ariel Panzardo Date: Sat, 25 Jul 2026 16:52:30 -0300 Subject: Bluetooth: SCO: give the socket its own sco_conn reference sco_conn_del() drops a reference it does not own. It takes one transient reference via sco_conn_hold_unless_zero() and releases it with the sco_conn_put() that follows sco_sock_hold(); the additional put in the !sk branch releases a second one: conn = sco_conn_hold_unless_zero(conn); ... sk = sco_sock_hold(conn); sco_conn_unlock(conn); sco_conn_put(conn); if (!sk) { sco_conn_put(conn); return; } When close() races the controller's Disconnection Complete, sco_chan_del() clears conn->sk and drops the socket's reference while sco_conn_del() is running. sco_conn_del() then sees sk == NULL, its own put drops the count to zero and frees the conn, and the second put writes to the freed kref: BUG: KASAN: slab-use-after-free in sco_conn_put.part.0+0x1a/0x190 Write of size 4 at addr ffff8881099dec74 by task kworker/u17:3/413 Workqueue: hci1 hci_rx_work Call Trace: sco_conn_put.part.0+0x1a/0x190 hci_disconn_complete_evt+0x1ee/0x3e0 hci_event_packet+0x54a/0x650 hci_rx_work+0x321/0x3d0 Allocated by task 413: sco_conn_add+0x72/0x1a0 sco_connect_cfm+0x88/0x670 Freed by task 413: sco_conn_del.isra.0+0x3f/0xf0 hci_disconn_complete_evt+0x1ee/0x3e0 refcount_t: underflow; use-after-free. The root cause is that the socket stores the connection without holding a reference of its own. __sco_chan_add() does: sco_pi(sk)->conn = conn; so the socket borrows whatever reference its caller happened to hold, and the callers paper over that with ad-hoc holds and puts. Give the socket a counted reference instead: __sco_chan_add() takes one and it is released together with the channel (sco_chan_del()) and in sco_sock_destruct(). With the socket holding its own reference, sco_conn_del() no longer needs the extra put and the redundant hold in sco_conn_ready() goes away. Making the socket own its reference means the connection is now actually freed on the error paths of sco_connect() where it used to leak, which in turn runs sco_conn_free() and its hci_conn_drop(conn->hcon). To keep the hci_conn accounting balanced, make that ownership explicit as well: sco_conn_add() consumes one hci_conn reference and the sco_conn owns it for its lifetime. sco_connect() hands over the reference returned by hci_connect_sco() and no longer drops it on the error paths; sco_connect_cfm(), which is not given a reference, takes one with hci_conn_hold() before handing it to sco_conn_add() (and drops it again if the allocation fails); and the explicit hci_conn_hold() in sco_conn_ready() is removed. Every reference then has a single, clear owner. Fixes: e6720779ae61 ("Bluetooth: SCO: Use kref to track lifetime of sco_conn") Cc: stable@vger.kernel.org Suggested-by: Pauli Virtanen Signed-off-by: Aldo Ariel Panzardo Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/sco.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index c05f79b7aa31..3d4362a09df4 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -188,6 +188,9 @@ static void sco_sock_clear_timer(struct sock *sk) } /* ---- SCO connections ---- */ +/* Consumes a reference on @hcon, which the returned sco_conn owns until it is + * freed. On failure (NULL return) the reference is left for the caller to drop. + */ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) { struct sco_conn *conn = hcon->sco_data; @@ -198,6 +201,9 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) sco_conn_lock(conn); conn->hcon = hcon; sco_conn_unlock(conn); + } else { + /* conn already owns a reference on hcon */ + hci_conn_drop(hcon); } return conn; } @@ -265,10 +271,8 @@ static void sco_conn_del(struct hci_conn *hcon, int err) sco_conn_unlock(conn); sco_conn_put(conn); - if (!sk) { - sco_conn_put(conn); + if (!sk) return; - } /* Kill socket */ lock_sock(sk); @@ -283,7 +287,7 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, { BT_DBG("conn %p", conn); - sco_pi(sk)->conn = conn; + sco_pi(sk)->conn = sco_conn_hold(conn); conn->sk = sk; if (parent) @@ -366,15 +370,15 @@ static int sco_connect(struct sock *sk) */ if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { release_sock(sk); - hci_conn_drop(hcon); + sco_conn_put(conn); err = -EBADFD; goto unlock; } err = sco_chan_add(conn, sk, NULL); + sco_conn_put(conn); if (err) { release_sock(sk); - hci_conn_drop(hcon); goto unlock; } @@ -1452,8 +1456,6 @@ static void sco_conn_ready(struct sco_conn *conn) bacpy(&sco_pi(sk)->src, &conn->hcon->src); bacpy(&sco_pi(sk)->dst, &conn->hcon->dst); - sco_conn_hold(conn); - hci_conn_hold(conn->hcon); __sco_chan_add(conn, sk, parent); if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) @@ -1509,10 +1511,12 @@ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status) if (!status) { struct sco_conn *conn; - conn = sco_conn_add(hcon); + conn = sco_conn_add(hci_conn_hold(hcon)); if (conn) { sco_conn_ready(conn); sco_conn_put(conn); + } else { + hci_conn_drop(hcon); } } else sco_conn_del(hcon, bt_to_errno(status)); -- cgit v1.2.3 From 98f2e9e6d6f91a6abb43f166b244b428ba85fa2b Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Fri, 17 Jul 2026 10:55:07 +0200 Subject: i2c: iproc: reset bus after timeout if START_BUSY is stuck If a transaction times out, the START_BUSY signal can stay up, and subsequent transactaction attempts will fail as the bus is still considered busy. I can easily trigger this by attempting to read from an address with no device, e.g. when running i2cdetect. After the first read times out, all subsequent read attempts return busy. To get to a working state again, the controller needs to be reset to clear the START_BUSY signal. So check for START_BUSY still asserted on a timeout, and do reset in case it is, This is also done by the original non-upstream iproc-smbus driver implementation [1]. Works around situations like: bcm-iproc-2c 1803b000.i2c: transaction timed out bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy ... where the bus never recovers after a timeout. [1] https://github.com/opencomputeproject/onie/blob/master/patches/kernel/3.2.69/driver-iproc-smbus.patch Fixes: e6e5dd3566e0 ("i2c: iproc: Add Broadcom iProc I2C Driver") Signed-off-by: Jonas Gorski Cc: # v4.0+ Acked-by: Ray Jui Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260717085507.34209-1-jonas.gorski@bisdn.de --- drivers/i2c/busses/i2c-bcm-iproc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index b5629cffe99b..86ca4c2221c4 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -803,6 +803,17 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c, } if (!time_left && !iproc_i2c->xfer_is_done) { + /* + * The controller may fail to clear START_BUSY after a timeout, + * reset the controller to recover in that case. + */ + if (!!(iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET) & + BIT(M_CMD_START_BUSY_SHIFT))) { + bcm_iproc_i2c_enable_disable(iproc_i2c, false); + bcm_iproc_i2c_init(iproc_i2c); + bcm_iproc_i2c_enable_disable(iproc_i2c, true); + } + /* flush both TX/RX FIFOs */ val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); -- cgit v1.2.3 From d64ec362c369bbc33833f7936d5f3a706b0d5c45 Mon Sep 17 00:00:00 2001 From: Liem Date: Mon, 29 Jun 2026 10:38:28 +0800 Subject: i2c: imx: Fix slave registration race and error handling In i2c_imx_reg_slave(), the slave pointer was assigned before pm_runtime_resume_and_get(). If pm_runtime_resume_and_get() failed, the error path returned without clearing i2c_imx->slave, leaving it non-NULL and causing all subsequent registration attempts to fail with -EBUSY. Additionally, because this driver uses a shared IRQ, the interrupt handler i2c_imx_isr() can execute concurrently and, after acquiring slave_lock, dereference i2c_imx->slave. The previous fix attempt added a lockless i2c_imx->slave = NULL on the error path, but that could race with the ISR under the lock and still cause a NULL pointer dereference. Fix both issues by deferring the assignment of i2c_imx->slave and i2c_imx->last_slave_event to after a successful resume, and by performing the assignment inside the slave_lock critical section. This guarantees that the slave pointer is never left stale on the error path and is always valid when observed by the interrupt handler. Fixes: f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver") Signed-off-by: Liem Cc: # v5.11+ Reviewed-by: Frank Li Acked-by: Carlos Song Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260629023829.152651-2-liem16213@gmail.com --- drivers/i2c/busses/i2c-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index d549d630b41a..49859ee494c6 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -930,9 +930,6 @@ static int i2c_imx_reg_slave(struct i2c_client *client) if (i2c_imx->slave) return -EBUSY; - i2c_imx->slave = client; - i2c_imx->last_slave_event = I2C_SLAVE_STOP; - /* Resume */ ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent); if (ret < 0) { @@ -940,6 +937,11 @@ static int i2c_imx_reg_slave(struct i2c_client *client) return ret; } + scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) { + i2c_imx->slave = client; + i2c_imx->last_slave_event = I2C_SLAVE_STOP; + } + i2c_imx_slave_init(i2c_imx); return 0; -- cgit v1.2.3 From 6ac7702b6cc2b94aaed9ef2d95bfbefcdc90061f Mon Sep 17 00:00:00 2001 From: Liem Date: Mon, 29 Jun 2026 10:38:29 +0800 Subject: i2c: imx: Cancel hrtimer before clearing slave pointer In i2c_imx_unreg_slave(), the slave pointer is set to NULL after disabling interrupts. However, a pending interrupt might already have started the hrtimer (i2c_imx_slave_timeout) before the pointer was cleared. If the hrtimer fires after i2c_imx->slave is set to NULL, the timer callback i2c_imx_slave_finish_op() will call i2c_imx_slave_event() with a NULL slave pointer, which results in a use-after-free / NULL pointer dereference. Fix by canceling the hrtimer and waiting for it to complete after disabling interrupts, before clearing the slave pointer. Fixes: f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver") Signed-off-by: Liem Cc: # v5.11+ Acked-by: Carlos Song Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260629023829.152651-3-liem16213@gmail.com --- drivers/i2c/busses/i2c-imx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 49859ee494c6..9477d814fde9 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -960,6 +960,7 @@ static int i2c_imx_unreg_slave(struct i2c_client *client) i2c_imx_reset_regs(i2c_imx); + hrtimer_cancel(&i2c_imx->slave_timer); i2c_imx->slave = NULL; /* Suspend */ -- cgit v1.2.3 From b08c9857aa1f5f3a81d375d6d4bb1d8b92f22ebc Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Thu, 25 Jun 2026 17:42:45 +0800 Subject: i2c: qcom-cci: drop custom suspend/resume and rely on runtime PM helpers cci_resume() unconditionally calls cci_resume_runtime() regardless of the runtime PM state. If the device is already runtime-suspended before system suspend, the clock is re-enabled while runtime_status remains RPM_SUSPENDED. As a result, pm_request_autosuspend() does not arm the timer, leaving the clock permanently enabled. Fixes: e517526195de ("i2c: Add Qualcomm CCI I2C driver") Signed-off-by: Wenmeng Liu Cc: # v5.8+ Reviewed-by: Vladimir Zapolskiy Reviewed-by: Konrad Dybcio Reviewed-by: Loic Poulain Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260625-cci-v1-1-a100cda673ce@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-cci.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 4d64895a9e9e..bdeda3979c48 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -492,24 +492,8 @@ static int __maybe_unused cci_resume_runtime(struct device *dev) return 0; } -static int __maybe_unused cci_suspend(struct device *dev) -{ - if (!pm_runtime_suspended(dev)) - return cci_suspend_runtime(dev); - - return 0; -} - -static int __maybe_unused cci_resume(struct device *dev) -{ - cci_resume_runtime(dev); - pm_request_autosuspend(dev); - - return 0; -} - static const struct dev_pm_ops qcom_cci_pm = { - SET_SYSTEM_SLEEP_PM_OPS(cci_suspend, cci_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_RUNTIME_PM_OPS(cci_suspend_runtime, cci_resume_runtime, NULL) }; -- cgit v1.2.3 From a8c171c107c0b61a5e7e10cedab0fb72aeaf640d Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Thu, 23 Jul 2026 20:08:04 -0300 Subject: x86/boot: Add volatile, clobbers and zero-length test in memcmp() Add the volatile qualifier and clobbers parameter to prevent bugs with instruction reordering and optimization. Also add TEST for the zero-length case to set ZF, as, if the count register is zero, the REPE prefix does not run the CMPSB instruction, leaving the ZF flag undetermined. [ bp: Add a comment about the len==0 case. ] Fixes: 62bd0337d0c4 ("Top header file for new x86 setup code") Closes: https://sashiko.dev/#/patchset/20260701-pvh-kasan-inline-v6-0-ba99045dfa9f%40igalia.com Suggested-by: Borislav Petkov Signed-off-by: Mauricio Faria de Oliveira Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/all/20260721-pvh-kasan-inline-v7-2-38979a50cef0@igalia.com --- arch/x86/boot/string.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c index ac0f900ebc47..1632d40e1f54 100644 --- a/arch/x86/boot/string.c +++ b/arch/x86/boot/string.c @@ -32,8 +32,15 @@ int memcmp(const void *s1, const void *s2, size_t len) { bool diff; - asm("repe cmpsb" - : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len)); + + /* + * Make sure ZF is properly set in the len==0 case because in it, + * RCX==0 and the REPE; CMPSB won't get executed. + */ + asm volatile("test %3, %3\n\t" + "repe cmpsb" + : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len) + : : "cc", "memory"); return diff; } -- cgit v1.2.3 From 35699ae99deabdb9100c2e6b5365c996712846e7 Mon Sep 17 00:00:00 2001 From: Suresh Guttula Date: Tue, 14 Jul 2026 20:20:39 +0530 Subject: drm/amdgpu : update mmhub eco sec lvl for vcn5_3 This patch requests PSP to set the sec lvl for vcn and jpeg. Signed-off-by: Suresh Guttula Reviewed-by: McRae Geoffrey Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 4c8b8472f85a730a6853ab68474f210143f42b5a) --- drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h | 1 + drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c | 24 ++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c | 26 ++++++++++++++++++++++++++ 5 files changed, 79 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index e0c0d7872e45..ca80b6194cac 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -1222,6 +1222,33 @@ int psp_memory_partition(struct psp_context *psp, int mode) return ret; } +int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int ret; + struct psp_context *psp = &adev->psp; + struct psp_gfx_cmd_resp *cmd = acquire_psp_cmd_buf(psp); + + cmd->cmd_id = GFX_CMD_ID_SET_MMHUB_ECO_SEC_LEVEL; + + ret = psp_cmd_submit_buf(psp, NULL, cmd, psp->fence_buf_mc_addr); + if (ret) { + dev_err(psp->adev->dev, + "PSP request failed to set mmuhub eco sec level with ret=%d\n", ret); + release_psp_cmd_buf(psp); + return ret; + } + + if (cmd->resp.status) { + dev_err(psp->adev->dev, + "MMHUB ECO SEC LEVEL command 0x%x failed, PSP response status: 0x%X\n", + cmd->cmd_id, cmd->resp.status); + ret = -EIO; + } + release_psp_cmd_buf(psp); + + return ret; +} + static int psp_ptl_fmt_verify(struct psp_context *psp, enum amdgpu_ptl_fmt fmt, uint32_t *ptl_fmt) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h index 2d838b1b2b11..3d7744d60fac 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h @@ -656,5 +656,6 @@ int amdgpu_psp_reg_program_no_ring(struct psp_context *psp, uint32_t val, void amdgpu_psp_debugfs_init(struct amdgpu_device *adev); int amdgpu_psp_get_fw_type(struct amdgpu_firmware_info *ucode, enum psp_gfx_fw_type *type); +int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c index e7546816baba..2c3e6d9f52a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c @@ -398,6 +398,25 @@ static void jpeg_v5_3_0_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx) WREG32_SOC15(JPEG, inst_idx, regUVD_JPEG_POWER_STATUS, reg_data); } +/** + * jpeg_v5_3_0_set_mmhub_eco_sec_level - set jpeg sec lvl reg + * + * @adev: amdgpu_device pointer + * + * request psp to set secure lvl + */ +static int jpeg_v5_3_0_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int r = 0; + + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { + /* Request to PSP to program JPEG secure lvl */ + r = psp_set_mmhub_eco_sec_level(adev); + } + + return r; +} + /** * jpeg_v5_3_0_start - start JPEG block * @@ -423,6 +442,11 @@ static int jpeg_v5_3_0_start(struct amdgpu_device *adev) if (r) return r; + /* program JPEG secure lvl register */ + r = jpeg_v5_3_0_set_mmhub_eco_sec_level(adev); + if (r) + return r; + /* JPEG disable CGC */ jpeg_v5_3_0_disable_clock_gating(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h b/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h index ac34bac3c839..ad618b78685f 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h +++ b/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h @@ -110,6 +110,7 @@ enum psp_gfx_cmd_id GFX_CMD_ID_PERF_HW = 0x0000004C, /* performance monitor */ GFX_CMD_ID_FB_FW_RESERV_ADDR = 0x00000050, /* Query FW reservation addr */ GFX_CMD_ID_FB_FW_RESERV_EXT_ADDR = 0x00000051, /* Query FW reservation extended addr */ + GFX_CMD_ID_SET_MMHUB_ECO_SEC_LEVEL = 0x0000005D, /* Set MMHUB ECO sec lvls on VCN block */ }; /* PSP boot config sub-commands */ diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c index 45580e9c4e0c..886e2c2074a9 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c @@ -795,6 +795,27 @@ static int vcn_v5_0_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, return 0; } +/** + * vcn_v5_0_0_set_mmhub_eco_sec_level - set vcn sec lvl reg + * + * @adev: amdgpu_device pointer + * + * request psp to set sec lvl + */ +static int vcn_v5_0_0_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int r = 0; + + if (amdgpu_ip_version(adev, VCN_HWIP, 0) == IP_VERSION(5, 3, 0)) { + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { + /* Request to PSP to program VCN secure lvl */ + r = psp_set_mmhub_eco_sec_level(adev); + } + } + + return r; +} + /** * vcn_v5_0_0_start - VCN start * @@ -819,6 +840,11 @@ static int vcn_v5_0_0_start(struct amdgpu_vcn_inst *vinst) fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr; + /* program VCN secure lvl register */ + r = vcn_v5_0_0_set_mmhub_eco_sec_level(adev); + if (r) + return r; + if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) return vcn_v5_0_0_start_dpg_mode(vinst, adev->vcn.inst[i].indirect_sram); -- cgit v1.2.3 From 6e7566ba4739dd573c331adde1c96690f7a567bd Mon Sep 17 00:00:00 2001 From: William Palacek Date: Mon, 6 Jul 2026 10:25:58 -0400 Subject: drm/amdkfd: Add bounds check for CRAT subtype length The CRAT parser validates that the subtype header fits within the image, but does not verify that the advertised subtype length fits. A malformed CRAT table with an oversized length field causes out-of-bounds reads when kfd_parse_subtype() casts the header to specific subtype structures. Add validation that sub_type_hdr + length does not exceed the image boundary before parsing the subtype contents. Signed-off-by: William Palacek Reviewed-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit 48e1d1e6e8798aef0312e68d8e586021b5b3cf4d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_crat.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index 2a239f45fc24..6e0df685503d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -1412,6 +1412,15 @@ int kfd_parse_crat_table(void *crat_image, struct list_head *device_list, break; } + /* Validate subtype fits within remaining image */ + if ((char *)sub_type_hdr + sub_type_hdr->length > + (char *)crat_image + image_len) { + pr_warn("CRAT subtype length %u exceeds image bounds\n", + sub_type_hdr->length); + ret = -EINVAL; + break; + } + if (sub_type_hdr->flags & CRAT_SUBTYPE_FLAGS_ENABLED) { ret = kfd_parse_subtype(sub_type_hdr, device_list); if (ret) -- cgit v1.2.3 From 17da8410554906a95bb9ef30acbdf0c5abc1a4ef Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 15 Jul 2026 19:32:56 +0530 Subject: drm/amdgpu: dont pin wptr bo instead use eviction fence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of pinning the wptr bo attach the eviction fence to the bo to make sure it remains valid all the time. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 7264bc10c7c657a54603c7fc058bf8e15f18ce12) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 19 +++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 17 +++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 572f2949cb64..3efe1ae27386 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -965,6 +965,7 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr) struct amdgpu_vm *vm = &fpriv->vm; unsigned long key, tmp_key; struct amdgpu_bo_va *bo_va; + struct amdgpu_usermode_queue *queue; struct amdgpu_bo *bo; struct drm_exec exec; struct xarray xa; @@ -1080,6 +1081,24 @@ retry_lock: dma_fence_wait(bo_va->last_pt_update, false); dma_fence_wait(vm->last_update, false); + xa_for_each(&uq_mgr->userq_xa, tmp_key, queue) { + bo = queue->wptr_obj.obj; + if (!bo) { + ret = -EINVAL; + goto unlock_all; + } + + ret = amdgpu_ttm_alloc_gart(&bo->tbo); + if (unlikely(ret)) { + drm_file_err(uq_mgr->file, + "failed to bind wptr bo to gart on resume, qid=%lu ret=%d\n", + tmp_key, ret); + goto unlock_all; + } + + queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(bo); + } + ret = amdgpu_evf_mgr_rearm(&fpriv->evf_mgr, &exec); if (ret) drm_file_err(uq_mgr->file, "Failed to replace eviction fence\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 16625c31bfd3..4e44a581a78a 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -70,27 +70,23 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, ret = -EINVAL; goto fail_map; } - - /* TODO use eviction fence instead of pinning. */ - ret = amdgpu_bo_pin(wptr_obj->obj, AMDGPU_GEM_DOMAIN_GTT); + /* Keep WPTR BO under eviction-fence control instead of pinning. */ + ret = amdgpu_evf_mgr_attach_fence(&uq_mgr_to_fpriv(uq_mgr)->evf_mgr, wptr_obj->obj); if (ret) { - DRM_ERROR("Failed to pin wptr bo. ret %d\n", ret); + DRM_ERROR("Failed to attach eviction fence to wptr bo. ret %d\n", ret); goto fail_map; } ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo); if (ret) { - DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret); - goto fail_alloc_gart; + DRM_ERROR("Failed to bind wptr bo to GART. ret %d\n", ret); + goto fail_map; } queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj); drm_exec_fini(&exec); return 0; - -fail_alloc_gart: - amdgpu_bo_unpin(wptr_obj->obj); fail_map: amdgpu_bo_unref(&wptr_obj->obj); fail_lock: @@ -468,9 +464,6 @@ static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue) amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, &queue->mqd.cpu_ptr); - amdgpu_bo_reserve(queue->wptr_obj.obj, true); - amdgpu_bo_unpin(queue->wptr_obj.obj); - amdgpu_bo_unreserve(queue->wptr_obj.obj); amdgpu_bo_unref(&queue->wptr_obj.obj); } -- cgit v1.2.3 From e3a721753f60c1d4643a729eaf5a8976d285fa0f Mon Sep 17 00:00:00 2001 From: Bob Zhou Date: Thu, 16 Jul 2026 10:44:36 +0800 Subject: drm/amdgpu: skip clearing empty freed VM list on GEM close amdgpu_vm_clear_freed() allocates an amdgpu_sync object and walks the VM reservation fences via amdgpu_sync_resv() before checking whether vm->freed has anything to clear. Return early when the list is empty to skip this overhead on a hot path (every GEM close and command submission). Signed-off-by: Bob Zhou Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 8ba869e852d4f1b1c0e5ae9225c77f7ceccbe056) --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index f224d33c4bc4..dc6a9d7dd0b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1553,6 +1553,8 @@ int amdgpu_vm_clear_freed(struct amdgpu_device *adev, struct amdgpu_sync sync; int r; + if (list_empty(&vm->freed)) + return 0; /* * Implicitly sync to command submissions in the same VM before -- cgit v1.2.3 From c216b39fbbc4b007fd6984cffd85039d49a55154 Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Fri, 3 Jul 2026 09:14:49 +0800 Subject: drm/amd/display: Increase HDMI AV mute wait from 2 to 3 frames Some HDMI sinks need additional GCP packets to properly process the mute state before the timing generator is disabled, especially after link re-establishment with HDMI 2.0 scrambling enabled. Waiting for only 2 frames is insufficient for certain monitor firmware, resulting in garbled display output on resume from suspend. Increase the AV mute wait in dcn30_set_avmute() from 2 to 3 frames to ensure the sink receives enough GCP packets. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5167 Assisted-by: Cursor:Claude-Opus-4.6 Reviewed-by: Wayne Lin Signed-off-by: Ray Wu Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 0c0d5174b09640d8b560764aa5a177630e076e93) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index a7c85a2302ab..2d3587a31bc8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -847,13 +847,19 @@ void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) pipe_ctx->stream_res.stream_enc, enable); - /* Wait for two frame to make sure AV mute is sent out */ + /* Wait for three frames to make sure AV mute is sent out. + * Some HDMI sinks need additional GCP packets to properly + * process the mute state, especially after link re-establishment + * with HDMI 2.0 scrambling enabled. + */ if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { + int i; + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + for (i = 0; i < 3; i++) { + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + } } } } -- cgit v1.2.3 From 38b73293f38658a4685ffcea666462024f858ad9 Mon Sep 17 00:00:00 2001 From: Vladimir Marioukhine Date: Mon, 20 Jul 2026 11:53:30 -0400 Subject: drm/amdkfd: fix QID bit leak in pqm_create_queue() When MES is enabled and amdgpu_amdkfd_alloc_kernel_mem() fails during the first queue creation for a process, pqm_create_queue() returns early via 'return retval' without going through the err_create_queue cleanup label. This means clear_bit(*qid, pqm->queue_slot_bitmap) is never called, leaving the reserved QID bit permanently set in queue_slot_bitmap. Over time this leaks QID slots, potentially exhausting all available queue slots. Fix this by replacing 'return retval' with 'goto err_allocate_pqn' so that clear_bit() is always called on the error path without touching the uninitialized pqn pointer. AILIKFD-813 Reported-by: Deucher, Alexander Signed-off-by: Vladimir Marioukhine Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit a107f74c38edbb80d6ab64dcaeeb292c14e9779f) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9ccbc6e5b27b..503176f00204 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -383,7 +383,7 @@ int pqm_create_queue(struct process_queue_manager *pqm, false); if (retval) { dev_err(dev->adev->dev, "failed to allocate process context bo\n"); - return retval; + goto err_allocate_pqn; } memset(pdd->proc_ctx_cpu_ptr, 0, AMDGPU_MES_PROC_CTX_SIZE); } -- cgit v1.2.3 From 3cbb92ca935f964f6d415cd1c0ac91d061aafa63 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 9 Jul 2026 16:33:11 -0600 Subject: drm/amdgpu: Fix __rcu fence pointer accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building for ARCH=um with W=1 C=1 makes sparse report "incompatible types in comparison expression (different address spaces)" warnings in the KFD code, exposed after UML builds were enabled: - amdgpu_amdkfd_fence.c compares the __rcu-annotated dma_fence.ops pointer directly in to_amdgpu_amdkfd_fence(). - amdgpu_amdkfd_gpuvm.c compares the __rcu eviction fence pointer directly in amdgpu_amdkfd_gpuvm_restore_process_bos(). Fixes: af3f2f5db265 ("drm/amdgpu: Remove UML build exclusion from Kconfig") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607091659.SHEscT0c-lkp@intel.com/ Cc: Harry Wentland Assisted-by: Copilot:Claude-Opus-4.8 Signed-off-by: Alex Hung Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 764f241ad227bb942e5b0b8b4d9898f1a4175605) --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c index 6a364357522b..b0299d861903 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c @@ -92,7 +92,7 @@ struct amdgpu_amdkfd_fence *to_amdgpu_amdkfd_fence(struct dma_fence *f) return NULL; fence = container_of(f, struct amdgpu_amdkfd_fence, base); - if (f->ops == &amdkfd_fence_ops) + if (rcu_access_pointer(f->ops) == &amdkfd_fence_ops) return fence; return NULL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 35fe2c974699..f0f516a79424 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -3096,7 +3096,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu * process_info->eviction_fence = new_fence; replace_eviction_fence(ef, dma_fence_get(&new_fence->base)); } else { - WARN_ONCE(*ef != &process_info->eviction_fence->base, + WARN_ONCE(rcu_access_pointer(*ef) != &process_info->eviction_fence->base, "KFD eviction fence doesn't match KGD process_info"); } -- cgit v1.2.3 From 43c9f7cba9f9fcd09a5e23686ab4f0f67b62d888 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 9 Jul 2026 16:33:12 -0600 Subject: drm/amdgpu: Pack nested ucode_info struct Building for ARCH=um with W=1 C=1 makes the "amd_sriov_msg_vf2pf_info must be 1 KB" static assertion in amdgv_sriovmsg.h fail under sparse, exposed after UML builds were enabled. Sparse does not honor #pragma pack(push, 1) for the nested ucode_info struct, so it sizes each element as 8 bytes instead of 5 and computes the surrounding structure as larger than 1 KB. The compilers get this right via the enclosing pragma, but the annotation should be explicit. Fixes: af3f2f5db265 ("drm/amdgpu: Remove UML build exclusion from Kconfig") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607091659.SHEscT0c-lkp@intel.com/ Cc: Harry Wentland Assisted-by: Copilot:Claude-Opus-4.8 Signed-off-by: Alex Hung Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher (cherry picked from commit 1d8cfeb69daa863a70134b8ed6df8055c418a5b0) --- drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h index d80f01c0e754..b02561f41b58 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h @@ -379,7 +379,7 @@ struct amd_sriov_msg_vf2pf_info { struct { uint8_t id; uint32_t version; - } ucode_info[AMD_SRIOV_MSG_RESERVE_UCODE]; + } __packed ucode_info[AMD_SRIOV_MSG_RESERVE_UCODE]; uint64_t dummy_page_addr; /* FB allocated for guest MES to record UQ info */ uint64_t mes_info_addr; -- cgit v1.2.3 From 0ebf413b444ba22a6422f750c48509d4e91f8555 Mon Sep 17 00:00:00 2001 From: Fares Soliman Date: Mon, 13 Jul 2026 09:18:41 -0400 Subject: drm/amdgpu: Update message IDs to PMFW to correctly gather GFXOFF residency logs Updates PPSMC_MSGs and set/get functions for gathering GFXOFF logs on Van Gogh. Logs are now gathered live rather than starting then stopping logging and reading an average value afterwards. This is in accordance to changes made in PMFW. In regards to messageID 0x52, the old interface uses a start/stop parameter, and the new one doesn't. The firmware is checked to determine which method to use. v2: added firmware guard to new interface, old interface kept as fallback Signed-off-by: Fares Soliman Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 482e2cadea8c34ae4e733f269a640d6b04fc9262) --- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 4 +- drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 2 +- .../drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h | 6 ++- drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h | 4 +- drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c | 50 +++++++++++++++++----- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index 389bad724273..662ec01961f4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -1319,8 +1319,8 @@ err: * @size: Number of bytes to read * @pos: Offset to seek to * - * Read the last residency value logged. It doesn't auto update, one needs to - * stop logging before getting the current value. + * Read a live GFXOFF residency sample from firmware. One needs to start logging + * before getting the current value. */ static ssize_t amdgpu_debugfs_gfxoff_residency_read(struct file *f, char __user *buf, size_t size, loff_t *pos) diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index e3a89e9a9df4..64a64973fb86 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -1348,7 +1348,7 @@ struct pptable_funcs { u32 (*set_gfx_off_residency)(struct smu_context *smu, bool start); /** - * @get_gfx_off_residency: Average GFXOFF residency % during the logging interval + * @get_gfx_off_residency: Live GFXOFF residency percentage */ u32 (*get_gfx_off_residency)(struct smu_context *smu, uint32_t *residency); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h index 7471e2df2828..4206514765cd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h @@ -110,8 +110,10 @@ #define PPSMC_MSG_GetSlowPPTLimit 0x4C #define PPSMC_MSG_GetGfxOffStatus 0x50 #define PPSMC_MSG_GetGfxOffEntryCount 0x51 -#define PPSMC_MSG_LogGfxOffResidency 0x52 -#define PPSMC_Message_Count 0x53 +#define PPSMC_MSG_GfxOffResidencyLogReadSample 0x52 +#define PPSMC_MSG_StopGfxOffResidencyLogging 0x53 +#define PPSMC_MSG_StartGfxOffResidencyLogging 0x56 +#define PPSMC_Message_Count 0x57 //Argument for PPSMC_MSG_GfxDeviceDriverReset enum { diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h index 636ff90923d9..acf03838d49d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h @@ -252,7 +252,9 @@ __SMU_DUMMY_MAP(DriverMode2Reset), \ __SMU_DUMMY_MAP(GetGfxOffStatus), \ __SMU_DUMMY_MAP(GetGfxOffEntryCount), \ - __SMU_DUMMY_MAP(LogGfxOffResidency), \ + __SMU_DUMMY_MAP(StartGfxOffResidencyLogging), \ + __SMU_DUMMY_MAP(GfxOffResidencyLogReadSample), \ + __SMU_DUMMY_MAP(StopGfxOffResidencyLogging), \ __SMU_DUMMY_MAP(SetNumBadMemoryPagesRetired), \ __SMU_DUMMY_MAP(SetBadMemoryPagesRetiredFlagsPerChannel), \ __SMU_DUMMY_MAP(AllowGpo), \ diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index 016a5c893fee..c54675b00a96 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -72,6 +72,12 @@ static const struct smu_feature_bits vangogh_dpm_features = { } }; +/* + * SMU support new GFXOFF residency log interface since version 4.63.62.00, + * use this to get live readings of GFXOFF residency + */ +#define SUPPORT_LIVE_RESIDENCY_MSG_VERSION 0x043f3e00 + static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 0), MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 0), @@ -142,7 +148,9 @@ static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(GetSlowPPTLimit, PPSMC_MSG_GetSlowPPTLimit, 0), MSG_MAP(GetGfxOffStatus, PPSMC_MSG_GetGfxOffStatus, 0), MSG_MAP(GetGfxOffEntryCount, PPSMC_MSG_GetGfxOffEntryCount, 0), - MSG_MAP(LogGfxOffResidency, PPSMC_MSG_LogGfxOffResidency, 0), + MSG_MAP(StartGfxOffResidencyLogging, PPSMC_MSG_StartGfxOffResidencyLogging, 0), + MSG_MAP(GfxOffResidencyLogReadSample, PPSMC_MSG_GfxOffResidencyLogReadSample, 0), + MSG_MAP(StopGfxOffResidencyLogging, PPSMC_MSG_StopGfxOffResidencyLogging, 0), }; static struct cmn2asic_mapping vangogh_feature_mask_map[SMU_FEATURE_COUNT] = { @@ -2449,19 +2457,32 @@ static int vangogh_set_power_limit(struct smu_context *smu, static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start) { int ret = 0; - u32 residency; struct amdgpu_device *adev = smu->adev; if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) return 0; - ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_LogGfxOffResidency, - start, &residency); - if (ret) - return ret; + if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) { + u32 residency; + + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxOffResidencyLogReadSample, + start, &residency); + if (ret) + return ret; - if (!start) - adev->gfx.gfx_off_residency = residency; + if (!start) + adev->gfx.gfx_off_residency = residency; + } else { + if (start) { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StartGfxOffResidencyLogging, NULL); + if (ret) + return ret; + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StopGfxOffResidencyLogging, NULL); + if (ret) + return ret; + } + } return ret; } @@ -2478,11 +2499,20 @@ static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start) */ static u32 vangogh_get_gfxoff_residency(struct smu_context *smu, uint32_t *residency) { + int ret = 0; struct amdgpu_device *adev = smu->adev; - *residency = adev->gfx.gfx_off_residency; + if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) + return 0; + + if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) { + *residency = adev->gfx.gfx_off_residency; + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GfxOffResidencyLogReadSample, + residency); + } - return 0; + return ret; } /** -- cgit v1.2.3 From a9cdc85839e4fe2c760aa4ca6cc341c31ad1918a Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 21 Jul 2026 09:30:07 -0400 Subject: drm/amdkfd: Handle invalid event type in CRIU event restore In kfd_criu_restore_event, there was no handling for the event priv data having an invalid event type. The priv data here is untrusted and can be invalid. In that case, fail with EINVAL. Signed-off-by: David Francis Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit 2e8e9963cd5c41aa14fd5316bf9ec92e7a0e3097) 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 2e97da597b3d..78abfc0f7780 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -524,6 +524,9 @@ int kfd_criu_restore_event(struct file *devkfd, ret = create_other_event(p, ev, &ev_priv->event_id); break; + default: + ret = -EINVAL; + break; } mutex_unlock(&p->event_mutex); -- cgit v1.2.3 From 119b828afb87d6a0b2b4235fafb28b3bd2da9fa0 Mon Sep 17 00:00:00 2001 From: Kanala Ramalingeswara Reddy Date: Thu, 16 Jul 2026 22:25:52 +0530 Subject: drm/amdgpu: Update driver if header for SMU V15.0.5 Update smu v15.0.0 driver if header to be v15.0.5 compatible. Signed-off-by: Kanala Ramalingeswara Reddy Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit 3ee6561f8ae0ae3c80d04429361a6e06589693fc) --- .../pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h | 38 ++++ drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h | 2 +- .../gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c | 220 ++++++++++++++++++++- 3 files changed, 253 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h index 47ddf533b08f..a5d3e8d88860 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h @@ -144,6 +144,44 @@ typedef struct { uint32_t MaxGfxClk; } DpmClocks_t; +//Freq in MHz +//Voltage in milli volts with 2 fractional bits +typedef struct { + uint32_t DcfClocks[NUM_DCFCLK_DPM_LEVELS]; + uint32_t DispClocks[NUM_DISPCLK_DPM_LEVELS]; + uint32_t DppClocks[NUM_DPPCLK_DPM_LEVELS]; + uint32_t SocClocks[NUM_SOCCLK_DPM_LEVELS]; + + uint32_t VPEClocks[NUM_VPE_DPM_LEVELS]; + uint32_t FclkClocks_Freq[NUM_FCLK_DPM_LEVELS]; + uint32_t FclkClocks_Voltage[NUM_FCLK_DPM_LEVELS]; + uint32_t SocVoltage[NUM_SOC_VOLTAGE_LEVELS]; + MemPstateTable_t MemPstateTable[NUM_MEM_PSTATE_LEVELS]; + + uint8_t NumDcfClkLevelsEnabled; + uint8_t NumDispClkLevelsEnabled; //Applies to both Dispclk and Dppclk + uint8_t NumSocClkLevelsEnabled; + + uint8_t VpeClkLevelsEnabled; + + uint8_t NumMemPstatesEnabled; + uint8_t NumFclkLevelsEnabled; + uint8_t Spare1; + uint8_t Spare2; + + uint8_t Spare3; + uint8_t Spare4; + uint8_t Spare5[2]; + + uint32_t MinGfxClk; + uint32_t MaxGfxClk; + + uint32_t Spare6[8]; + uint32_t Spare7[8]; + uint32_t Spare8[8]; + uint32_t Spare9[8]; +} DpmClocks_t_v15_0_5; + typedef struct { uint16_t CoreFrequency[16]; //Target core frequency [MHz] uint16_t CorePower[16]; //CAC calculated core power [mW] diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h index e6fd8be2cc4a..0fa32fd041da 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h @@ -26,7 +26,7 @@ #include "amdgpu_smu.h" #define SMU15_DRIVER_IF_VERSION_INV 0xFFFFFFFF -#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x7 +#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x9 #define FEATURE_MASK(feature) (1ULL << feature) 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 bb8d09e73c7d..faa7d661f3bf 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 @@ -168,7 +168,7 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu) SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); - SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t), + SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t_v15_0_5), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); @@ -178,7 +178,7 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu) goto err0_out; smu_table->metrics_time = 0; - smu_table->clocks_table = kzalloc_obj(DpmClocks_t); + smu_table->clocks_table = kzalloc_obj(DpmClocks_t_v15_0_5); if (!smu_table->clocks_table) goto err1_out; @@ -752,11 +752,48 @@ static int smu_v15_0_0_get_dpm_freq_by_index(struct smu_context *smu, return 0; } +static int smu_v15_0_5_get_dpm_freq_by_index(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t dpm_level, + uint32_t *freq) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + if (!clk_table || clk_type >= SMU_CLK_COUNT) + return -EINVAL; + + switch (clk_type) { + case SMU_SOCCLK: + if (dpm_level >= clk_table->NumSocClkLevelsEnabled) + return -EINVAL; + *freq = clk_table->SocClocks[dpm_level]; + break; + case SMU_UCLK: + case SMU_MCLK: + if (dpm_level >= clk_table->NumMemPstatesEnabled) + return -EINVAL; + *freq = clk_table->MemPstateTable[dpm_level].MemClk; + break; + case SMU_FCLK: + if (dpm_level >= clk_table->NumFclkLevelsEnabled) + return -EINVAL; + *freq = clk_table->FclkClocks_Freq[dpm_level]; + break; + default: + return -EINVAL; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_freq_by_index(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t dpm_level, uint32_t *freq) { + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq); + else smu_v15_0_0_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq); return 0; @@ -907,12 +944,116 @@ failed: return ret; } +static int smu_v15_0_5_get_dpm_ultimate_freq(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *min, + uint32_t *max) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + uint32_t clock_limit; + uint32_t max_dpm_level, min_dpm_level; + int ret = 0; + + if (!smu_v15_0_0_clk_dpm_is_enabled(smu, clk_type)) { + switch (clk_type) { + case SMU_MCLK: + case SMU_UCLK: + clock_limit = smu->smu_table.boot_values.uclk; + break; + case SMU_FCLK: + clock_limit = smu->smu_table.boot_values.fclk; + break; + case SMU_GFXCLK: + case SMU_SCLK: + clock_limit = smu->smu_table.boot_values.gfxclk; + break; + case SMU_SOCCLK: + clock_limit = smu->smu_table.boot_values.socclk; + break; + default: + clock_limit = 0; + break; + } + + /* clock in Mhz unit */ + if (min) + *min = clock_limit / 100; + if (max) + *max = clock_limit / 100; + + return 0; + } + + if (max) { + switch (clk_type) { + case SMU_GFXCLK: + case SMU_SCLK: + *max = clk_table->MaxGfxClk; + break; + case SMU_MCLK: + case SMU_UCLK: + max_dpm_level = 0; + break; + case SMU_FCLK: + max_dpm_level = clk_table->NumFclkLevelsEnabled - 1; + break; + case SMU_SOCCLK: + max_dpm_level = clk_table->NumSocClkLevelsEnabled - 1; + break; + default: + ret = -EINVAL; + goto failed; + } + + if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) { + ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type, + max_dpm_level, max); + if (ret) + goto failed; + } + } + + if (min) { + switch (clk_type) { + case SMU_GFXCLK: + case SMU_SCLK: + *min = clk_table->MinGfxClk; + break; + case SMU_MCLK: + case SMU_UCLK: + min_dpm_level = clk_table->NumMemPstatesEnabled - 1; + break; + case SMU_FCLK: + min_dpm_level = 0; + break; + case SMU_SOCCLK: + min_dpm_level = 0; + break; + default: + ret = -EINVAL; + goto failed; + } + + if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) { + ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type, + min_dpm_level, min); + if (ret) + goto failed; + } + } + +failed: + return ret; +} + static int smu_v15_0_common_get_dpm_ultimate_freq(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *min, uint32_t *max) { - if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_ultimate_freq(smu, clk_type, min, max); + else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) smu_v15_0_0_get_dpm_ultimate_freq(smu, clk_type, min, max); return 0; @@ -986,11 +1127,36 @@ static int smu_v15_0_0_get_dpm_level_count(struct smu_context *smu, return 0; } +static int smu_v15_0_5_get_dpm_level_count(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *count) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + switch (clk_type) { + case SMU_SOCCLK: + *count = clk_table->NumSocClkLevelsEnabled; + break; + case SMU_MCLK: + *count = clk_table->NumMemPstatesEnabled; + break; + case SMU_FCLK: + *count = clk_table->NumFclkLevelsEnabled; + break; + default: + break; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_level_count(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *count) { - if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_level_count(smu, clk_type, count); + else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) smu_v15_0_0_get_dpm_level_count(smu, clk_type, count); return 0; @@ -1363,9 +1529,24 @@ static int smu_v15_0_0_set_fine_grain_gfx_freq_parameters(struct smu_context *sm return 0; } +static int smu_v15_0_5_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + smu->gfx_default_hard_min_freq = clk_table->MinGfxClk; + smu->gfx_default_soft_max_freq = clk_table->MaxGfxClk; + smu->gfx_actual_hard_min_freq = 0; + smu->gfx_actual_soft_max_freq = 0; + + return 0; +} + static int smu_v15_0_common_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) { - smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu); + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_set_fine_grain_gfx_freq_parameters(smu); + else + smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu); return 0; } @@ -1405,9 +1586,36 @@ static int smu_v15_0_0_get_dpm_table(struct smu_context *smu, struct dpm_clocks return 0; } +static int smu_v15_0_5_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + uint8_t idx; + + /* + * Only the Clock information of SOC and + * VPE is copied to provide VPE DPM settings for use. + */ + for (idx = 0; idx < NUM_SOCCLK_DPM_LEVELS; idx++) { + clock_table->SocClocks[idx].Freq = + (idx < clk_table->NumSocClkLevelsEnabled) ? clk_table->SocClocks[idx]:0; + clock_table->SocClocks[idx].Vol = 0; + } + + for (idx = 0; idx < NUM_VPE_DPM_LEVELS; idx++) { + clock_table->VPEClocks[idx].Freq = + (idx < clk_table->VpeClkLevelsEnabled) ? clk_table->VPEClocks[idx]:0; + clock_table->VPEClocks[idx].Vol = 0; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table) { - smu_v15_0_0_get_dpm_table(smu, clock_table); + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_table(smu, clock_table); + else + smu_v15_0_0_get_dpm_table(smu, clock_table); return 0; } -- cgit v1.2.3 From d8726ef11512754a68c0ab53c57634a569b8feff Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Mon, 22 Jun 2026 10:11:22 +0200 Subject: drm/amdgpu: move debug_vm handling to amdgpu_cs_parser_fini MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit referenced below restarts the CS if the validation is still in progress. When debug_vm is enabled, all BOs from the CS are invalidated so we will hit an infinite loop. To avoid that, defer BO invalidation to amdgpu_cs_parser_fini. Fixes: 59720bfd8c6d ("drm/amdgpu: restart the CS if some parts of the VM are still invalidated") Signed-off-by: Pierre-Eric Pelloux-Prayer Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 8c990ee9daa295462df24982ce6878db997a380a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index e714cee2997a..5445f75741b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1177,19 +1177,6 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) job->vm_pd_addr = amdgpu_gmc_pd_addr(vm->root.bo); } - if (adev->debug_vm) { - /* Invalidate all BOs to test for userspace bugs */ - amdgpu_bo_list_for_each_entry(e, p->bo_list) { - struct amdgpu_bo *bo = e->bo; - - /* ignore duplicates */ - if (!bo) - continue; - - amdgpu_vm_bo_invalidate(bo, false); - } - } - return 0; } @@ -1378,6 +1365,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, /* Cleanup the parser structure */ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) { + struct amdgpu_device *adev = parser->adev; + struct amdgpu_bo_list_entry *e; unsigned int i; amdgpu_sync_free(&parser->sync); @@ -1393,8 +1382,21 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) if (parser->ctx) amdgpu_ctx_put(parser->ctx); - if (parser->bo_list) + if (parser->bo_list) { + if (adev->debug_vm) { + /* Invalidate all BOs to test for userspace bugs */ + amdgpu_bo_list_for_each_entry(e, parser->bo_list) { + struct amdgpu_bo *bo = e->bo; + + /* ignore duplicates */ + if (!bo) + continue; + + amdgpu_vm_bo_invalidate(bo, false); + } + } amdgpu_bo_list_put(parser->bo_list); + } for (i = 0; i < parser->nchunks; i++) kvfree(parser->chunks[i].kdata); -- cgit v1.2.3 From 99b2fe4f19e3be0a8d0a0b5ea98d855970889653 Mon Sep 17 00:00:00 2001 From: Gang Ba Date: Tue, 14 Jul 2026 15:08:57 -0400 Subject: drm/amdkfd: Fix missing authorization check in KFD_IOC_DBG_TRAP_DISABLE Prevent unauthorized termination of active GPU debug sessions. Previously, users with /dev/kfd access could terminate another process's debug session without proper ownership or ptrace authorization. Signed-off-by: Gang Ba Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit 4db4c5ffd5585b72622ecf6ffedf2da258ee23f5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index c7edebd2fd8a..e1689b3d2add 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -3109,10 +3109,14 @@ static int kfd_ioctl_set_debug_trap(struct file *filep, struct kfd_process *p, v goto out; } - /* Check if target is still PTRACED. */ + /* + * Verify debugger has permission to debug target process. + * For cross-process debugging, require active ptrace relationship. + * This applies to ALL operations to prevent unauthorized interference. + */ rcu_read_lock(); - if (target != p && args->op != KFD_IOC_DBG_TRAP_DISABLE - && ptrace_parent(target->lead_thread) != current) { + if (target != p && ptrace_parent(target->lead_thread) != current + && target->debugger_process != p) { pr_err("PID %i is not PTRACED and cannot be debugged\n", args->pid); r = -EPERM; } -- cgit v1.2.3 From a8d234a1133f02bfbbdaac18b889219949f28108 Mon Sep 17 00:00:00 2001 From: Shubhankar Milind Sardeshpande Date: Wed, 22 Jul 2026 10:51:56 +0530 Subject: drm/amdgpu: Enable support for PSP 15_0_5 Add PSP 15.0.5 related offsets for GFX to KMD interface and enable support for it. Co-developed-by: Pratik Vishwakarma Signed-off-by: Pratik Vishwakarma Signed-off-by: Shubhankar Milind Sardeshpande Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit b01e244c82c5d033d7424a64abe4079f3fceb869) --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 5 +- drivers/gpu/drm/amd/amdgpu/psp_v15_0.c | 93 ++++++++++++++++++++++----- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 2860f12915c0..5e83edbd313b 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, 5): case IP_VERSION(15, 0, 9): amdgpu_device_ip_block_add(adev, &psp_v15_0_ip_block); break; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index ca80b6194cac..7139cb3b2db5 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, 5): case IP_VERSION(15, 0, 9): psp_v15_0_0_set_psp_funcs(psp); psp->boot_time_tmr = false; @@ -3503,8 +3504,10 @@ 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, 5) || amdgpu_ip_version(adev, MP0_HWIP, 0) == + 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 || diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c index 2a4d91368ac6..abaa40b6def9 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c @@ -33,9 +33,22 @@ MODULE_FIRMWARE("amdgpu/psp_15_0_0_toc.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_0_ta.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_5_toc.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_5_ta.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_9_toc.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_9_ta.bin"); +#define regMPASP_PCRU0_MPASP_C2PMSG_64 0x4280 +#define regMPASP_PCRU0_MPASP_C2PMSG_64_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_67 0x4283 +#define regMPASP_PCRU0_MPASP_C2PMSG_67_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_69 0x4285 +#define regMPASP_PCRU0_MPASP_C2PMSG_69_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_70 0x4286 +#define regMPASP_PCRU0_MPASP_C2PMSG_70_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_71 0x4287 +#define regMPASP_PCRU0_MPASP_C2PMSG_71_BASE_IDX 2 + static int psp_v15_0_0_init_microcode(struct psp_context *psp) { struct amdgpu_device *adev = psp->adev; @@ -71,14 +84,25 @@ static int psp_v15_0_0_ring_stop(struct psp_context *psp, ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_SMN_C2PMSG_101), 0x80000000, 0x80000000, false); } else { - /* Write the ring destroy command*/ - WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64, - GFX_CTRL_CMD_ID_DESTROY_RINGS); - /* there might be handshake issue with hardware which needs delay */ - mdelay(20); - /* Wait for response flag (bit 31) */ - ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), - 0x80000000, 0x80000000, false); + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) { + /* Write the ring destroy command*/ + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64, + GFX_CTRL_CMD_ID_DESTROY_RINGS); + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + /* Wait for response flag (bit 31) */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + } else { + /* Write the ring destroy command*/ + WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64, + GFX_CTRL_CMD_ID_DESTROY_RINGS); + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + /* Wait for response flag (bit 31) */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + } } return ret; @@ -118,14 +142,44 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp, 0x80000000, 0x8000FFFF, false); } else { - /* Wait for sOS ready for ring creation */ - ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), - 0x80000000, 0x80000000, false); + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) { + /* Wait for sOS ready for ring creation */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); if (ret) { DRM_ERROR("Failed to wait for trust OS ready for ring creation\n"); return ret; } + /* Write low address of the ring to C2PMSG_69 */ + psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr); + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_69, psp_ring_reg); + /* Write high address of the ring to C2PMSG_70 */ + psp_ring_reg = upper_32_bits(ring->ring_mem_mc_addr); + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_70, psp_ring_reg); + /* Write size of ring to C2PMSG_71 */ + psp_ring_reg = ring->ring_size; + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_71, psp_ring_reg); + /* Write the ring initialization command to C2PMSG_64 */ + psp_ring_reg = ring_type; + psp_ring_reg = psp_ring_reg << 16; + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64, psp_ring_reg); + + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + + /* Wait for response flag (bit 31) in C2PMSG_64 */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x8000FFFF, false); + } else { + /* Wait for sOS ready for ring creation */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + if (ret) { + DRM_ERROR("Failed to wait for trust OS ready for ring creation\n"); + return ret; + } + /* Write low address of the ring to C2PMSG_69 */ psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr); WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_69, psp_ring_reg); @@ -146,6 +200,7 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp, /* Wait for response flag (bit 31) in C2PMSG_64 */ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), 0x80000000, 0x8000FFFF, false); + } } return ret; @@ -176,8 +231,12 @@ static uint32_t psp_v15_0_0_ring_get_wptr(struct psp_context *psp) if (amdgpu_sriov_vf(adev)) data = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_102); - else - data = RREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67); + else { + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) + data = RREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_67); + else + data = RREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67); + } return data; } @@ -190,8 +249,12 @@ static void psp_v15_0_0_ring_set_wptr(struct psp_context *psp, uint32_t value) WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_102, value); WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_101, GFX_CTRL_CMD_ID_CONSUME_CMD); - } else - WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67, value); + } else { + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_67, value); + else + WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67, value); + } } static const struct psp_funcs psp_v15_0_0_funcs = { -- cgit v1.2.3 From acea01861838ddac70e3a89ef9fbd5f2073f3f91 Mon Sep 17 00:00:00 2001 From: Shubhankar Milind Sardeshpande Date: Thu, 16 Jul 2026 18:17:48 +0530 Subject: drm/amdgpu: Fix NBIO 7.11.5 offsets Fix NBIO 7.11.5 related offsets Signed-off-by: Shubhankar Milind Sardeshpande Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit dcc27ae3092211c913a1bea04618c4faf1234d48) --- drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c | 146 ++++++++++++++++--------------- 1 file changed, 76 insertions(+), 70 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c index c78f0598637f..000516b5845a 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c @@ -30,70 +30,76 @@ #include "ivsrcid/nbio/irqsrcs_nbif_7_4.h" #include -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10 0x4f0aeb -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10 0x4f0aec -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10 0x4f0aed -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10 0x4f0aee -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10 0x4f0aef -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10 0x4f0af0 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10 0x4f0af1 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10 0x4f0af2 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10 0x4f0af3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10 0x4f0af4 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10 0x4f0af5 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2 - -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5 0x8e13 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5 0x8e14 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5 0x8e15 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5 0x8e4d -#define regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5 0x8e4e -#define regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 - -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5 0xd000 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX0_BIF_FB_EN_nbio_7_11_5 0x8e20 -#define regBIF_BX0_BIF_FB_EN_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5 0x8e11 -#define regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5 0x8e12 -#define regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5 0x8e26 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5 0x8e27 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5 0x8e17 -#define regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10 0x4f0aeb +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10 0x4f0aec +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10 0x4f0aed +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10 0x4f0aee +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10 0x4f0aef +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10 0x4f0af0 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10 0x4f0af1 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10 0x4f0af2 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10 0x4f0af3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10 0x4f0af4 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10 0x4f0af5 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3 +#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021 +#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2 + +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5 0x8e13 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5 0x8e14 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5 0x8e15 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5_BASE_IDX 5 + +#define regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5 0x012d +#define regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5 0x012e +#define regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 + +#define regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5 0x0021 +#define regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5_BASE_IDX 2 + +#define regBIF_BX1_BIF_FB_EN_nbio_7_11_5 0x0100 +#define regBIF_BX1_BIF_FB_EN_nbio_7_11_5_BASE_IDX 2 + +#define regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5 0x00f1 +#define regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5 0x00f2 +#define regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5_BASE_IDX 2 + +#define regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5 0x0106 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5 0x0107 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5_BASE_IDX 2 + +#define regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5 0x00f7 +#define regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 + +//BIF_BX1_BIF_FB_EN +#define BIF_BX1_BIF_FB_EN__FB_READ_EN__SHIFT_nbio_7_11_5 0x0 +#define BIF_BX1_BIF_FB_EN__FB_WRITE_EN__SHIFT_nbio_7_11_5 0x1 +#define BIF_BX1_BIF_FB_EN__FB_READ_EN_MASK_nbio_7_11_5 0x00000001L +#define BIF_BX1_BIF_FB_EN__FB_WRITE_EN_MASK_nbio_7_11_5 0x00000002L static void nbif_v6_3_1_remap_hdp_registers(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) { - WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5, adev->rmmio_remap.reg_offset + KFD_MMIO_REMAP_HDP_MEM_FLUSH_CNTL); - WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5, adev->rmmio_remap.reg_offset + KFD_MMIO_REMAP_HDP_REG_FLUSH_CNTL); } else { WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL, @@ -110,7 +116,7 @@ static u32 nbif_v6_3_1_get_rev_id(struct amdgpu_device *adev) if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 4)) tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10); else if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5); + tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5); else tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0); @@ -124,11 +130,11 @@ static void nbif_v6_3_1_mc_access_enable(struct amdgpu_device *adev, bool enable { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) { if (enable) - WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN_nbio_7_11_5, - BIF_BX0_BIF_FB_EN__FB_READ_EN_MASK | - BIF_BX0_BIF_FB_EN__FB_WRITE_EN_MASK); + WREG32_SOC15(NBIO, 0, regBIF_BX1_BIF_FB_EN_nbio_7_11_5, + BIF_BX1_BIF_FB_EN__FB_READ_EN_MASK_nbio_7_11_5 | + BIF_BX1_BIF_FB_EN__FB_WRITE_EN_MASK_nbio_7_11_5); else - WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN_nbio_7_11_5, 0); + WREG32_SOC15(NBIO, 0, regBIF_BX1_BIF_FB_EN_nbio_7_11_5, 0); } else { if (enable) WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN, @@ -383,12 +389,12 @@ static void nbif_v6_3_1_ih_control(struct amdgpu_device *adev) /* setup interrupt control */ if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5, adev->dummy_page_addr >> 8); else WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL2, adev->dummy_page_addr >> 8); - interrupt_cntl = RREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5); + interrupt_cntl = RREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5); /* * BIF_BX0_INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi * BIF_BX0_INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN @@ -401,7 +407,7 @@ static void nbif_v6_3_1_ih_control(struct amdgpu_device *adev) IH_REQ_NONSNOOP_EN, 0); if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5, interrupt_cntl); + WREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5, interrupt_cntl); else WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL, interrupt_cntl); } @@ -427,7 +433,7 @@ nbif_v6_3_1_get_clockgating_state(struct amdgpu_device *adev, static u32 nbif_v6_3_1_get_hdp_flush_req_offset(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5); + return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5); else return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_REQ); } @@ -435,7 +441,7 @@ static u32 nbif_v6_3_1_get_hdp_flush_req_offset(struct amdgpu_device *adev) static u32 nbif_v6_3_1_get_hdp_flush_done_offset(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5); + return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5); else return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_DONE); } @@ -622,7 +628,7 @@ static void nbif_v6_3_1_set_reg_remap(struct amdgpu_device *adev) } else { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, - regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5) << 2; + regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5) << 2; else adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL) << 2; -- cgit v1.2.3 From 83463a96ea3c7d8ae636a4d6a0ba63c9ce410724 Mon Sep 17 00:00:00 2001 From: William Palacek Date: Mon, 20 Jul 2026 12:51:34 -0400 Subject: drm/amdkfd: fix uint32_t overflow in EOP ring buffer size alignment eop_ring_buffer_size in struct queue_properties is a u32. In kfd_queue_acquire_buffers() the expected EOP buffer size is computed as ALIGN(eop_ring_buffer_size, PAGE_SIZE); ALIGN uses typeof(x), so the addition is done in 32-bit. A user-supplied size of 0xFFFFF001 wraps to 0, causing kfd_queue_buffer_get() to skip its exact-size check (gated on size != 0) and accept any BO mapped at the address. On GFX8/GFX9 the MQD cp_hqd_eop_control is then programmed for an 8KB EOP ring backed by a 4KB BO, so CP EOP writes can land past the buffer and fault the GPU. Cast the operand to u64 so the alignment is computed in 64-bit; the size check in kfd_queue_buffer_get() then rejects the oversized request. Fixes: 42ea9cf2f16b ("drm/amdkfd: Relax size checking during queue buffer get") Signed-off-by: William Palacek Reviewed-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit ae443117b742c357bfef3a7bddabf76fcf86e9ef) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index 98a5512b701b..b249e7d1af48 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -288,7 +288,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope } err = kfd_queue_buffer_get(vm, (void *)properties->eop_ring_buffer_address, &properties->eop_buf_bo, - ALIGN(properties->eop_ring_buffer_size, PAGE_SIZE)); + ALIGN((u64)properties->eop_ring_buffer_size, PAGE_SIZE)); if (err) goto out_err_unreserve; } -- cgit v1.2.3 From c2ac5a50c1804e22d5bc05c7e16693333751b3c0 Mon Sep 17 00:00:00 2001 From: Kanala Ramalingeswara Reddy Date: Thu, 23 Jul 2026 00:10:01 +0530 Subject: drm/amdgpu: enable mode2 reset for SMU IP v15.0.5 Set the default reset method to mode2 for SMU 15.0.5. Signed-off-by: Kanala Ramalingeswara Reddy Reviewed-by: Pratik Vishwakarma Signed-off-by: Alex Deucher (cherry picked from commit 314d49abe315cd0d0a872a43f68f08be43a305c8) --- drivers/gpu/drm/amd/amdgpu/soc21.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index e0b80abcd075..e9d51e992a8c 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, 5): case IP_VERSION(15, 0, 9): return AMD_RESET_METHOD_MODE2; default: -- cgit v1.2.3 From bb493058c35c8676e48269ab6732688ea733d23c Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 24 Jul 2026 07:41:29 +0800 Subject: drm/amd/pm: fix pptable use-after-free amdgpu_dpm_get_pp_table() returns a pointer to a driver-owned power table after dropping adev->pm.mutex. The sysfs path then copies from that pointer. A concurrent pp_table write can replace and free the allocation during the copy, causing a use-after-free. Change the DPM interface to copy into caller-provided storage while the mutex is held. Keep the size-only query for attribute discovery without exposing the driver-owned pointer. Fixes: 1684d3ba4885 ("drm/amd/amdgpu: change pptable output format from ASCII to binary") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit f6eed7acfd30099ef7baeb6ba45bb59daad80631) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 14 +++++++++++--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 13 +++---------- drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 3 ++- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index f76ba6753551..c787e772b3cc 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1183,12 +1183,14 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table) +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + char *pptable = NULL; int ret = 0; - if (!table) + if ((!table && size) || (table && !size)) return -EINVAL; if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) @@ -1196,7 +1198,13 @@ int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table) mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle, - table); + &pptable); + if (ret > 0 && !pptable) { + ret = -EINVAL; + } else if (ret > 0 && table) { + ret = min_t(size_t, ret, size); + memcpy(table, pptable, ret); + } mutex_unlock(&adev->pm.mutex); return ret; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 97da01aff76c..d94ea54c33c3 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -564,25 +564,19 @@ static ssize_t amdgpu_get_pp_table(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - char *table = NULL; int size, ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; - size = amdgpu_dpm_get_pp_table(adev, &table); + size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1); amdgpu_pm_put_access(adev); if (size <= 0) return size; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, table, size); - return size; } @@ -2726,10 +2720,9 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ *states = ATTR_STATE_UNSUPPORTED; } else if (DEVICE_ATTR_IS(pp_table)) { int ret; - char *tmp = NULL; - ret = amdgpu_dpm_get_pp_table(adev, &tmp); - if (ret == -EOPNOTSUPP || !tmp) + ret = amdgpu_dpm_get_pp_table(adev, NULL, 0); + if (ret <= 0) *states = ATTR_STATE_UNSUPPORTED; else *states = ATTR_STATE_SUPPORTED; diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index aa3f427819a0..e95cd22a31cf 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -487,7 +487,8 @@ int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev, int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, enum amd_pp_task task_id, enum amd_pm_state_type *user_state); -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table); +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size); int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev, uint32_t type, long *input, -- cgit v1.2.3 From 048f4541b71fb19645fb79d6e62e6e4da23a4035 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 23 Jul 2026 23:18:33 +0800 Subject: drm/amd/pm: fix torn gpu metrics reads amdgpu_dpm_get_gpu_metrics() returns a pointer to the shared metrics cache after dropping adev->pm.mutex. The sysfs path then copies from that pointer. Another reader can refresh the cache in place during the copy and return a snapshot containing data from two generations. Pass caller-provided storage through the DPM interface and copy the metrics while the mutex is held. This keeps the cache pointer private and makes each sysfs read observe one complete sample. Fixes: 25c933b1c4fc ("drm/amd/powerplay: add new sysfs interface for retrieving gpu metrics(V2)") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit 862333bb48693ecafcae25af0c9d9ec31015ac77) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 12 +++++++++--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 8 +------- drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 3 ++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index c787e772b3cc..6d1ad4d5b8f0 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1434,17 +1434,23 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table) +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - int ret = 0; + void *table; + ssize_t ret; if (!pp_funcs->get_gpu_metrics) return 0; mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle, - table); + &table); + if (ret > 0) { + ret = min_t(ssize_t, ret, size); + memcpy(buf, table, ret); + } mutex_unlock(&adev->pm.mutex); return ret; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index d94ea54c33c3..763947fbf16f 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -1774,7 +1774,6 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - void *gpu_metrics; ssize_t size = 0; int ret; @@ -1782,15 +1781,10 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, if (ret) return ret; - size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics); + size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1); if (size <= 0) goto out; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, gpu_metrics, size); - out: amdgpu_pm_put_access(adev); diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index e95cd22a31cf..8d1b097a3c3c 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -518,7 +518,8 @@ int amdgpu_dpm_get_power_profile_mode(struct amdgpu_device *adev, char *buf); int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, long *input, uint32_t size); -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table); +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size); ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id, void *table); ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev, -- cgit v1.2.3 From 443290d70b01e9c35830c300e3247c06581b594c Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Mon, 13 Jul 2026 22:23:34 +0800 Subject: drm/amd/display: Add AV mute wait frames to dce110_set_avmute Port the three-frame wait logic from dcn30_set_avmute to dce110_set_avmute so that older DCN versions (1.0, 2.0) also wait for GCP packets to be sent out before proceeding. This ensures HDMI sinks properly process the mute state, preventing garbled display after link re-establishment. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5167 Reviewed-by: Wayne Lin Signed-off-by: Ray Wu Signed-off-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 414da24137ace80d8c59fefd43ba3ec9f5f854ba) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index 042602c50e35..53fffe58602f 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -1337,8 +1337,27 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx) void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) { - if (pipe_ctx != NULL && pipe_ctx->stream_res.stream_enc != NULL) + if (pipe_ctx == NULL || pipe_ctx->stream_res.stream_enc == NULL) + return; + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable); + + /* Wait for three frames to make sure AV mute is sent out. + * Some HDMI sinks need additional GCP packets to properly + * process the mute state, especially after link re-establishment + * with HDMI 2.0 scrambling enabled. + */ + if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { + int i; + + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + for (i = 0; i < 3; i++) { + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + } + } + } } enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) -- cgit v1.2.3 From f327e389c07cfc3a2f6ff54f6214e1a52d457edc Mon Sep 17 00:00:00 2001 From: George Zhang Date: Thu, 16 Jul 2026 17:00:01 -0400 Subject: drm/amd/display: Fix divide-by-zero in calculate_mcache_setting on zero viewport If a plane reaches calculate_mcache_setting with a zero-area viewport, calculate_mcache_setting exits early with num_mcaches == 0 and mvmpg_width/height == 0. This will cause a divide-by-zero panic and can also cause an underflow on num_mcaches. Fix this by changing calculate_mcache_setting to bool and adding guards after each calculate_mcache_row_bytes call. If num_mcaches or mvmpg_width/height is zero, return a false. Callers will propagate the failure as a rejected mode, which prevents the panic. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5302 Reviewed-by: Sun peng (Leo) Li Reviewed-by: Dillon Varone Signed-off-by: George Zhang Signed-off-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 29c0f7c655f47bcbd575ff75e58480df6ec3c9da) Cc: stable@vger.kernel.org --- .../dml21/src/dml2_core/dml2_core_dcn4_calcs.c | 31 +++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index f338e733318e..b4e2264ef408 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -2452,7 +2452,7 @@ static void calculate_mcache_row_bytes( DML_ASSERT(*p->num_mcaches > 0); } -static void calculate_mcache_setting( +static bool calculate_mcache_setting( struct dml2_core_internal_scratch *scratch, struct dml2_core_calcs_calculate_mcache_setting_params *p) { @@ -2478,7 +2478,7 @@ static void calculate_mcache_setting( *p->lc_comb_mcache = 0; if (!p->dcc_enable) - return; + return true; l->is_dual_plane = dml_is_420(p->source_format) || p->source_format == dml2_rgbe_alpha; @@ -2515,7 +2515,14 @@ static void calculate_mcache_setting( l->l_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_l; calculate_mcache_row_bytes(scratch, &l->l_p); - DML_ASSERT(*p->num_mcaches_l > 0); + if (*p->num_mcaches_l == 0 || + (p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l) == 0) { + DML_LOG_VERBOSE("DML::%s: degenerate luma viewport (num_mcaches_l=%u mvmpg_%s_l=%u) — mode not supported\n", + __func__, *p->num_mcaches_l, + p->surf_vert ? "height" : "width", + p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l); + return false; + } if (l->is_dual_plane) { l->c_p.num_chans = p->num_chans; @@ -2551,7 +2558,14 @@ static void calculate_mcache_setting( l->c_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_c; calculate_mcache_row_bytes(scratch, &l->c_p); - DML_ASSERT(*p->num_mcaches_c > 0); + if (*p->num_mcaches_c == 0 || + (p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c) == 0) { + DML_LOG_VERBOSE("DML::%s: degenerate chroma viewport (num_mcaches_c=%u mvmpg_%s_c=%u) — mode not supported\n", + __func__, *p->num_mcaches_c, + p->surf_vert ? "height" : "width", + p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c); + return false; + } } // Sharing for iMALL access @@ -2661,6 +2675,7 @@ static void calculate_mcache_setting( *p->mcache_shift_granularity_l = l->mvmpg_access_width_l; *p->mcache_shift_granularity_c = l->mvmpg_access_width_c; + return true; } static void calculate_mall_bw_overhead_factor( @@ -9457,7 +9472,10 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->ms.mall_comb_mcache_c[k]; calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->ms.lc_comb_mcache[k]; - calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params); + if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params)) { + mode_lib->ms.support.ModeSupport = false; + return false; + } } calculate_mall_bw_overhead_factor( @@ -10933,7 +10951,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex calculate_mcache_setting_params->mall_comb_mcache_l = &mode_lib->mp.mall_comb_mcache_l[k]; calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->mp.mall_comb_mcache_c[k]; calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->mp.lc_comb_mcache[k]; - calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params); + if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params)) + return false; } calculate_mall_bw_overhead_factor( -- cgit v1.2.3 From 000acb4ce7fb9feba3072ce468ad681f6585cd5d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 16 Feb 2026 09:32:53 -0500 Subject: drm/amd/display: check if dml21_add_phantom_plane() is successful Verify that the phantom plane was allocated to avoid a later segfault. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4970 Fixes: 70839da63605 ("drm/amd/display: Add new DCN401 sources") Reviewed-by: Dillon Varone Signed-off-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 5adb54abe5a8e82cbff7f8806db30a5f4924329f) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c index 835fece1d46a..50e445275b14 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c @@ -359,14 +359,13 @@ void dml21_handle_phantom_streams_planes(const struct dc *dc, struct dc_state *c main_plane = main_stream_status->plane_states[dc_plane_index]; /* create phantom planes for subvp enabled plane */ - dml21_add_phantom_plane(dml_ctx, - dc, - context, - phantom_stream, - main_plane, - &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index]); - - phantoms_added = true; + if (dml21_add_phantom_plane(dml_ctx, + dc, + context, + phantom_stream, + main_plane, + &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index])) + phantoms_added = true; } } } -- cgit v1.2.3 From ff8bc5a68a9a70bdc38d61a72c7a49c56063f9d2 Mon Sep 17 00:00:00 2001 From: William Palacek Date: Wed, 22 Jul 2026 11:20:56 -0400 Subject: drm/amdkfd: hold event_mutex while checkpointing CRIU events kfd_criu_checkpoint_events() counts the entries in p->event_idr via kfd_get_num_events(), allocates an array sized to that count, and then walks the same IDR to fill it. Neither the count nor the walk holds p->event_mutex. The CRIU checkpoint caller holds only p->mutex. Event create and destroy (kfd_event_create()/kfd_event_destroy()) take p->event_mutex and do not take p->mutex, so a second thread in the same process can insert or remove events between the count and the walk. If an event is inserted, the walk iterates more entries than were counted and writes past the end of the ev_privs allocation; if an event is removed, the walk dereferences an entry that is being freed. Hold p->event_mutex across the count and the walk so both observe a consistent view of p->event_idr. The lock is released before copy_to_user(), which only touches the local buffer. The caller already holds p->mutex and the create/destroy paths never take p->mutex, so the p->mutex -> p->event_mutex order is not inverted and no deadlock is introduced. Fixes: 40e8a766a761 ("drm/amdkfd: CRIU checkpoint and restore events") Signed-off-by: William Palacek Reviewed-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit ff57e223ab105795b05d3ef3f3c35a5a441bcbaa) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 78abfc0f7780..f705c61bdb1d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -548,15 +548,27 @@ int kfd_criu_checkpoint_events(struct kfd_process *p, int ret = 0; struct kfd_event *ev; uint32_t ev_id; + uint32_t num_events; - uint32_t num_events = kfd_get_num_events(p); + /* Serialize the count and the walk below against concurrent event + * create/destroy. Those paths take only p->event_mutex, not the + * p->mutex held by the CRIU checkpoint caller, so without this the + * event_idr can grow between kfd_get_num_events() and the loop and the + * walk writes past the ev_privs allocation. + */ + mutex_lock(&p->event_mutex); - if (!num_events) + num_events = kfd_get_num_events(p); + if (!num_events) { + mutex_unlock(&p->event_mutex); return 0; + } ev_privs = kvzalloc(num_events * sizeof(*ev_privs), GFP_KERNEL); - if (!ev_privs) + if (!ev_privs) { + mutex_unlock(&p->event_mutex); return -ENOMEM; + } idr_for_each_entry(&p->event_idr, ev, ev_id) { @@ -597,6 +609,8 @@ int kfd_criu_checkpoint_events(struct kfd_process *p, i++; } + mutex_unlock(&p->event_mutex); + ret = copy_to_user(user_priv_data + *priv_data_offset, ev_privs, num_events * sizeof(*ev_privs)); if (ret) { -- cgit v1.2.3 From 8ccb87b1c9be594fc2c36b0a4006a66f08dee1c8 Mon Sep 17 00:00:00 2001 From: Alan Swanson Date: Mon, 27 Jul 2026 17:01:26 +0100 Subject: drm/amd/display: Silence link_dpms I2C retimer failures Commit a4f01bf729b2 ("drm/amd/display: Refactor and fix link_dpms I2C") had also changed the "Set retimer failed" messages from DC_LOG_DEBUG() to DC_LOG_ERROR(). This unfortunately can create log spam. Change those back to DC_LOG_DEBUG() only. Fixes: a4f01bf729b2 ("drm/amd/display: Refactor and fix link_dpms I2C") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5520 Signed-off-by: Alan Swanson Signed-off-by: Alex Deucher (cherry picked from commit da8609eef18b0a3490d0e1fa9440659fadc8194d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/link/link_dpms.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index 42e7f0e9b3ce..ca5997748799 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -384,7 +384,7 @@ static bool write_i2c_retimer_vga( for (size_t i = 0; i < ARRAY_SIZE(vga_data); i++) { if (!write_i2c_retimer_offset_value(link, address, vga_data[i][0], vga_data[i][1])) { - DC_LOG_ERROR("Set retimer failed, vga index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, vga index: %zu\n", i); return false; } } @@ -405,7 +405,7 @@ static bool write_i2c_retimer_byte( return true; if (!write_i2c_retimer_offset_value(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value); + DC_LOG_DEBUG("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value); return false; } @@ -421,14 +421,14 @@ static bool write_i2c_retimer_byte( if (!link_query_ddc_data( link->ddc, address, &offset, 1, &value, 1 )) { - DC_LOG_ERROR("Set retimer failed, link_query_ddc_data\n"); + DC_LOG_DEBUG("Set retimer failed, link_query_ddc_data\n"); return false; } } value |= apply_rx_tx_change; if (!write_i2c_retimer_offset_value(link, address, offset, value)) { - DC_LOG_ERROR("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value); + DC_LOG_DEBUG("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value); return false; } } @@ -449,7 +449,7 @@ static bool write_i2c_retimer_setting( uint8_t value = settings->reg_settings[i].i2c_reg_val; if (!write_i2c_retimer_byte(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, index: %zu\n", i); return false; } } @@ -460,7 +460,7 @@ static bool write_i2c_retimer_setting( uint8_t value = settings->reg_settings_6g[i].i2c_reg_val; if (!write_i2c_retimer_byte(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, 6g index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, 6g index: %zu\n", i); return false; } } @@ -492,7 +492,7 @@ static bool write_i2c_default_retimer_setting( for (size_t i = 0; i < ARRAY_SIZE(data); i++) { if (!write_i2c_retimer_offset_value(link, address, data[i][0], data[i][1])) { - DC_LOG_ERROR("Set default retimer failed, index: %zu\n", i); + DC_LOG_DEBUG("Set default retimer failed, index: %zu\n", i); return false; } } @@ -524,7 +524,7 @@ static bool write_i2c_redriver_setting( ); if (!success) - DC_LOG_ERROR("Set redriver failed"); + DC_LOG_DEBUG("Set redriver failed"); return success; } -- cgit v1.2.3 From f931c54b241ce2f36bfc34955aec43a188276b8d Mon Sep 17 00:00:00 2001 From: Candice Li Date: Tue, 21 Jul 2026 21:38:58 +0800 Subject: drm/amdgpu: restore UMD profile pstate after runtime resume Runtime suspend runs GFX hw_fini and clears perfmon clock gating while the UMD profile DPM level remains set in software. Re-apply stable pstate after a successful runtime resume when a profile mode is active. Signed-off-by: Candice Li Reviewed-by: Hawking Zhang Reviewed-by: Yang Wang Signed-off-by: Alex Deucher (cherry picked from commit 138531c8850cc247aa12b104bb29ea387bcdcbb1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 4c0c77eafbd1..1aed121f4ddb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2908,6 +2908,19 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev) return 0; } +static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device *adev) +{ + enum amd_dpm_forced_level level; + uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; + + level = amdgpu_dpm_get_performance_level(adev); + if (level & profile_mode_mask) + amdgpu_asic_update_umd_stable_pstate(adev, true); +} + static int amdgpu_pmops_runtime_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -2952,6 +2965,8 @@ static int amdgpu_pmops_runtime_resume(struct device *dev) if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX) drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; + + amdgpu_restore_umd_profile_pstate_after_runpm(adev); adev->in_runpm = false; return 0; } -- cgit v1.2.3 From 1849a64165ccc23d3e5fc22b8be19227c21c1871 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 27 Jul 2026 12:23:17 +0800 Subject: drm/amd/pm: use milliwatts for GPU power sensors GPU average and input power backends report a mix of whole watts, milliwatts, Q24.8 watts and decimal-packed fractions. Q24.8 is inherited from the legacy PowerPlay sensor format. Milliwatts are a more natural unit for the hwmon and pm_info consumers in amdgpu_pm.c. A common decoder cannot distinguish these formats, and converting native milliwatts through Q24.8 also loses precision. Use milliwatts as the internal unit across all PPT and PowerPlay backends. Decode Q24.8 only at the legacy smu7 input boundary and encode it only for the raw amdgpu_sensors debugfs interface. This gives hwmon, pm_info and the sensor ioctl one unambiguous unit while preserving the format used by UMR. Fixes: 5b79d0482f3c ("drm/amd/pp: Remove struct pp_gpu_power") Fixes: 01992b121fb6 ("drm/amd/pm: fix amdgpu_pm_info power display units") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Reported-by: Lars Nieradzik Signed-off-by: Alex Deucher (cherry picked from commit 757ba0790bafec47a507e9662bf380f2e027d420) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 9 +++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 4 ++-- drivers/gpu/drm/amd/include/kgd_pp_interface.h | 6 ++++-- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 8 +++----- drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c | 4 ++-- drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c | 4 ++-- drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c | 2 +- drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c | 5 +++-- drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h | 5 +++++ drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c | 6 ++---- drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c | 8 ++++---- drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c | 7 ++++--- drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c | 9 +++------ drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c | 5 +++-- drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c | 3 ++- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c | 3 ++- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c | 4 ++-- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c | 3 ++- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 3 +-- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c | 3 +-- drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c | 3 ++- 27 files changed, 65 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index 662ec01961f4..df2588642817 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -43,6 +43,11 @@ #if defined(CONFIG_DEBUG_FS) +/* Encode milliwatts in the raw Q24.8 sensor report format used by UMR. */ +#define AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(power_mw) \ + DIV_ROUND_CLOSEST_ULL((u64)(power_mw) * BIT(8), \ + MILLIWATT_PER_WATT) + /** * amdgpu_debugfs_process_reg_op - Handle MMIO register reads/writes * @@ -1104,6 +1109,10 @@ static ssize_t amdgpu_debugfs_sensor_read(struct file *f, char __user *buf, return r; } + if (idx == AMDGPU_PP_SENSOR_GPU_AVG_POWER || + idx == AMDGPU_PP_SENSOR_GPU_INPUT_POWER) + values[0] = AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(values[0]); + if (size > valuesize) { amdgpu_virt_disable_access_debugfs(adev); return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 71272f40feef..8bb9c198ddec 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1210,7 +1210,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return -EINVAL; } } - ui32 >>= 8; + ui32 /= MILLIWATT_PER_WATT; break; case AMDGPU_INFO_SENSOR_GPU_INPUT_POWER: /* get input GPU power */ @@ -1219,7 +1219,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) (void *)&ui32, &ui32_size)) { return -EINVAL; } - ui32 >>= 8; + ui32 /= MILLIWATT_PER_WATT; break; case AMDGPU_INFO_SENSOR_VDDNB: /* get VDDNB in millivolts */ diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h index 6371f292f2d8..606d48115301 100644 --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h @@ -24,6 +24,8 @@ #ifndef __KGD_PP_INTERFACE_H__ #define __KGD_PP_INTERFACE_H__ +#include + extern const struct amdgpu_ip_block_version pp_smu_ip_block; extern const struct amdgpu_ip_block_version smu_v11_0_ip_block; extern const struct amdgpu_ip_block_version smu_v12_0_ip_block; @@ -150,8 +152,8 @@ enum amd_pp_sensors { AMDGPU_PP_SENSOR_MEM_TEMP, AMDGPU_PP_SENSOR_VCE_POWER, AMDGPU_PP_SENSOR_UVD_POWER, - AMDGPU_PP_SENSOR_GPU_AVG_POWER, - AMDGPU_PP_SENSOR_GPU_INPUT_POWER, + AMDGPU_PP_SENSOR_GPU_AVG_POWER, /* milliwatts */ + AMDGPU_PP_SENSOR_GPU_INPUT_POWER, /* milliwatts */ AMDGPU_PP_SENSOR_SS_APU_SHARE, AMDGPU_PP_SENSOR_SS_DGPU_SHARE, AMDGPU_PP_SENSOR_STABLE_PSTATE_SCLK, diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 763947fbf16f..85b089a3dc41 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -41,8 +41,6 @@ #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) -#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff)) - struct od_attribute { struct kobj_attribute attribute; struct list_head entry; @@ -3351,7 +3349,7 @@ static int amdgpu_hwmon_get_power(struct device *dev, return r; /* convert to microwatts */ - return power_2_mwatt(query) * 1000; + return query * 1000; } static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev, @@ -4919,7 +4917,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a seq_printf(m, "\t%u mV (VDDNB)\n", value); size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) { - mwatt = power_2_mwatt(query); + mwatt = query; centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100); @@ -4928,7 +4926,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a } size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) { - mwatt = power_2_mwatt(query); + mwatt = query; centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index bc82ba2e4c9b..f8a5648095d1 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -4062,7 +4062,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) (adev->asic_type != CHIP_FIJI) && (adev->asic_type != CHIP_TONGA)) { smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0, &tmp); - *query = tmp; + *query = PP_PWR_Q24_8_TO_MW(tmp); if (tmp != 0) return 0; @@ -4081,7 +4081,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) if (tmp != 0) break; } - *query = tmp; + *query = PP_PWR_Q24_8_TO_MW(tmp); return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c index 4b92b52aba2b..c283309efe87 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c @@ -3934,8 +3934,8 @@ static int vega10_get_gpu_power(struct pp_hwmgr *hwmgr, if (ret) return ret; - /* SMC returning actual watts, keep consistent with legacy asics, low 8 bit as 8 fractional bits */ - *query = value << 8; + /* SMC returns whole Watts, while power sensors use milliwatts. */ + *query = value * MILLIWATT_PER_WATT; return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c index 69a9074058ce..b466b08e4a67 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c @@ -1419,7 +1419,7 @@ static int vega12_get_gpu_power(struct pp_hwmgr *hwmgr, uint32_t *query) if (ret) return ret; - *query = metrics_table.CurrSocketPower << 8; + *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT; return ret; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c index 7b8f4c1b80eb..e9c194056e32 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c @@ -2154,12 +2154,13 @@ static int vega20_get_gpu_power(struct pp_hwmgr *hwmgr, int idx, switch (idx) { case AMDGPU_PP_SENSOR_GPU_AVG_POWER: if (hwmgr->smu_version == 0x282e00) - *query = metrics_table.AverageSocketPower << 8; + *query = metrics_table.AverageSocketPower * + MILLIWATT_PER_WATT; else ret = -EOPNOTSUPP; break; case AMDGPU_PP_SENSOR_GPU_INPUT_POWER: - *query = metrics_table.CurrSocketPower << 8; + *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT; break; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index ca71efaa1656..368200f7cf16 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -35,6 +35,11 @@ struct pp_hwmgr; struct phm_fan_speed_info; struct pp_atomctrl_voltage_table; +/* Decode legacy unsigned Q24.8 watts to internal milliwatts. */ +#define PP_PWR_Q24_8_TO_MW(power) \ + DIV_ROUND_CLOSEST_ULL((u64)(power) * MILLIWATT_PER_WATT, \ + BIT(8)) + #define VOLTAGE_SCALE 4 #define VOLTAGE_VID_OFFSET_SCALE1 625 #define VOLTAGE_VID_OFFSET_SCALE2 100 diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c index 54d3dba7d354..cfad8cd74773 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c @@ -658,7 +658,7 @@ static int arcturus_get_smu_metrics_data(struct smu_context *smu, *value = metrics->VcnActivityPercentage; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c index e6e009df9840..bb9620666dae 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c @@ -158,12 +158,10 @@ cyan_skillfish_get_smu_metrics_data(struct smu_context *smu, *value = metrics->Current.MemclkFrequency; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->Current.CurrentSocketPower << 8) / - 1000; + *value = metrics->Current.CurrentSocketPower; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->Average.CurrentSocketPower << 8) / - 1000; + *value = metrics->Average.CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->Current.GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index cd0457e13f54..6dfc3b585c6b 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -602,7 +602,7 @@ static int navi10_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -691,7 +691,7 @@ static int navi10_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -777,7 +777,7 @@ static int navi12_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -866,7 +866,7 @@ static int navi12_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index f799e489b481..dccca6ddd5ad 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -855,9 +855,10 @@ static int sienna_cichlid_get_smu_metrics_data(struct smu_context *smu, metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = use_metrics_v3 ? metrics_v3->AverageSocketPower << 8 : - use_metrics_v2 ? metrics_v2->AverageSocketPower << 8 : - metrics->AverageSocketPower << 8; + *value = use_metrics_v3 ? metrics_v3->AverageSocketPower : + use_metrics_v2 ? metrics_v2->AverageSocketPower : + metrics->AverageSocketPower; + *value *= MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = (use_metrics_v3 ? metrics_v3->TemperatureEdge : diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index c54675b00a96..3e3c68448ba9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -318,8 +318,7 @@ static int vangogh_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / - 1000 ; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * @@ -387,12 +386,10 @@ static int vangogh_get_smu_metrics_data(struct smu_context *smu, *value = metrics->Current.UvdActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->Average.CurrentSocketPower << 8) / - 1000; + *value = metrics->Average.CurrentSocketPower; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->Current.CurrentSocketPower << 8) / - 1000; + *value = metrics->Current.CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->Current.GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c index 75335da224c7..175c44190caa 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c @@ -1215,9 +1215,10 @@ static int renoir_get_smu_metrics_data(struct smu_context *smu, ((amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(12, 0, 0)) && (adev->pm.fw_version >= 0x373200))) - *value = metrics->CurrentSocketPower << 8; + *value = metrics->CurrentSocketPower * + MILLIWATT_PER_WATT; else - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = (metrics->GfxTemperature / 100) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c index 9d8b1227388f..18c0b43b93b9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c @@ -677,7 +677,8 @@ static int aldebaran_get_smu_metrics_data(struct smu_context *smu, case METRICS_AVERAGE_SOCKETPOWER: /* Valid power data is available only from primary die */ if (aldebaran_is_primary(smu)) - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * + MILLIWATT_PER_WATT; else ret = -EOPNOTSUPP; break; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index acbd7046d8a5..0be34fbdac26 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -807,7 +807,7 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu, metrics->Vcn1ActivityPercentage); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c index fe929bd89058..8d476a60d62f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c @@ -434,7 +434,8 @@ int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(metrics->DramBandwidthUtilization); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(metrics->SocketPower) << 8; + *value = SMUQ10_ROUND(metrics->SocketPower) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(metrics->MaxSocketTemperature) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c index ba91bf590eed..a9d558f5b739 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c @@ -338,10 +338,10 @@ static int smu_v13_0_4_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->AverageSocketPower << 8) / 1000; + *value = metrics->AverageSocketPower; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c index 27372a60e83d..f95b15af436d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c @@ -295,7 +295,7 @@ static int smu_v13_0_5_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index b12388134489..41608574bc61 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -1321,7 +1321,8 @@ static int smu_v13_0_6_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, version)); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) << 8; + *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, version)) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 42c9ceeb4f7d..5d11ed8be4c3 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -817,7 +817,7 @@ static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu, *value = smu_safe_u16_nn(metrics->AverageUclkActivity); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c index 7bf88ffd311b..edc3fedca9f8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c @@ -372,7 +372,7 @@ static int yellow_carp_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index 3d73f2050bbe..568665265a13 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -312,8 +312,7 @@ static int smu_v14_0_0_get_smu_metrics_data(struct smu_context *smu, break; case METRICS_AVERAGE_SOCKETPOWER: case METRICS_CURR_SOCKETPOWER: - *value = (metrics->SocketPower / 1000 << 8) + - (metrics->SocketPower % 1000 / 10); + *value = metrics->SocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * 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 a6a88e7b2668..bd054cbfbae0 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 @@ -695,7 +695,7 @@ static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu, metrics->Vcn1ActivityPercentage); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * 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 faa7d661f3bf..0da1ffb3a567 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 @@ -378,8 +378,7 @@ static int smu_v15_0_0_get_smu_metrics_data(struct smu_context *smu, break; case METRICS_AVERAGE_SOCKETPOWER: case METRICS_CURR_SOCKETPOWER: - *value = (metrics->SocketPower / 1000 << 8) + - (metrics->SocketPower % 1000 / 10); + *value = metrics->SocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c index 2105a1d7bb34..118eda5429ff 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c @@ -411,7 +411,8 @@ static int smu_v15_0_8_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(metrics->DramBandwidthUtilization); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(metrics->SocketPower) << 8; + *value = SMUQ10_ROUND(metrics->SocketPower) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(metrics->MaxSocketTemperature) * -- cgit v1.2.3 From 5e70f6804b4d6256058c360b10e044ee04ea4a4e Mon Sep 17 00:00:00 2001 From: Harkirat Gill Date: Mon, 27 Jul 2026 14:37:56 -0400 Subject: drm/amdgpu: cap GTT size to physical RAM on APUs On APUs, the GTT pool is backed by system RAM, but its size is not bound to the non-carveout memory that actually backs it. A user can end up with GTT + VRAM exceeding total physical memory through the following sequence: - Have a large non-carveout memory space (~128GB) and accordingly set a large GTT (~100GB) via the ttm module parameter. - Lower the non-carveout memory space in BIOS by increasing the UMA Frame Buffer Size (VRAM) to 64GB. - The previously set GTT value (~100GB) persists, even though the new non-carveout space (64GB) can no longer back it. This leads to a case where kernel reports GTT (100GB) + VRAM (64GB) despite the sum being greater than total physical memory (128GB). Cap the GTT size to totalram_pages() on APUs. totalram_pages() already excludes the VRAM carveout, so the resulting GTT can never exceed the system RAM that actually backs it. Signed-off-by: Harkirat Gill Reviewed-by: David Francis Assisted-by: Claude:claude-opus-4 Signed-off-by: Alex Deucher (cherry picked from commit 5dafdd649280c7dc6c22c8f877da3f54fcc441e1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index eb8bbfc7e6d9..5fc91f45db5b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2173,6 +2173,18 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) gtt_size = configured_size; } + /* Cap GTT so that it does not exceed total physical RAM. */ + if (adev->flags & AMD_IS_APU) { + u64 phys_ram = (u64)totalram_pages() << PAGE_SHIFT; + + if (gtt_size > phys_ram) { + gtt_size = phys_ram; + dev_info(adev->dev, + "Capping GTT to %uM to not exceed available system memory\n", + (unsigned int)(gtt_size / (1024 * 1024))); + } + } + /* Initialize GTT memory pool */ r = amdgpu_gtt_mgr_init(adev, gtt_size); if (r) { -- cgit v1.2.3 From 114b42507b6a23d9d24e24e4ef165233332c64d4 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Thu, 23 Jul 2026 06:25:48 +0200 Subject: drm/amd/display: use proper context for logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same as the rest of the code, get_ss_info_from_atombios() uses calc_pll_cs->ctx->logger for logging. But calc_pll_cs->ctx is initialized only later in calc_pll_max_vco_construct(). Therefore, any output using DC_LOG_SYNC() leads to a NULL pointer deference in get_ss_info_from_atombios(). According to Sashiko, the very same problem exists in dce112_get_pix_clk_dividers() and dcn3_get_pix_clk_dividers() too. To avoid accessing the NULL context, use clk_src->base.ctx->logger everywhere. That context in base is initialized earlier in dce110_clk_src_construct() and dce112_clk_src_construct(). Before get_ss_info_from_atombios() or Sashiko's get_pix_clk_dividers functions above are actually called. This is done by redefining DC_LOGGER to CTX->logger. Before: dce110_clk_src_construct() did: -> sets clk_src->base.ctx = ctx; -> ss_info_from_atombios_create() -> get_ss_info_from_atombios() <- uses calc_pll_cs->ctx # BOOM -> calc_pll_max_vco_construct() <- sets calc_pll_cs->ctx After: dce110_clk_src_construct() does: -> sets clk_src->base.ctx = ctx; -> ss_info_from_atombios_create() -> get_ss_info_from_atombios() <- uses clk_src->base.ctx Closes: https://bugzilla.suse.com/show_bug.cgi?id=1271175 Closes: https://lore.kernel.org/all/a9ee54e6-2413-4156-9bde-d528ae3c63a3@kernel.org/ Fixes: 1296423bf23c ("drm/amd/display: define DC_LOGGER for logger") Reviewed-by: Bhawanpreet Lakha Signed-off-by: Jiri Slaby (SUSE) Cc: Lakha, Bhawanpreet Cc: Harry Wentland Cc: Leo Li Cc: Rodrigo Siqueira Cc: Alex Deucher Cc: "Christian König" Cc: David Airlie Cc: Simona Vetter Cc: amd-gfx@lists.freedesktop.org Signed-off-by: Alex Deucher (cherry picked from commit 6f16fcbb0c46a87e3d9685407e906573d60104b0) Cc: stable@vger.kernel.org --- .../gpu/drm/amd/display/dc/dce/dce_clock_source.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index ecb8493ec523..8e3862f6dac2 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -45,9 +45,7 @@ clk_src->base.ctx #define DC_LOGGER \ - calc_pll_cs->ctx->logger -#define DC_LOGGER_INIT() \ - struct calc_pll_clock_source *calc_pll_cs = &clk_src->calc_pll + CTX->logger #undef FN #define FN(reg_name, field_name) \ @@ -287,6 +285,7 @@ static bool calc_pll_dividers_in_range( } static uint32_t calculate_pixel_clock_pll_dividers( + struct dce110_clk_src *clk_src, struct calc_pll_clock_source *calc_pll_cs, struct pll_settings *pll_settings) { @@ -474,7 +473,7 @@ static uint32_t dce110_get_pix_clk_dividers_helper ( { uint32_t field = 0; uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; - DC_LOGGER_INIT(); + /* Check if reference clock is external (not pcie/xtalin) * HW Dce80 spec: * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB @@ -517,12 +516,14 @@ static uint32_t dce110_get_pix_clk_dividers_helper ( /*Calculate Dividers by HDMI object, no SS case or SS case */ pll_calc_error = calculate_pixel_clock_pll_dividers( + clk_src, &clk_src->calc_pll_hdmi, pll_settings); else /*Calculate Dividers by default object, no SS case or SS case */ pll_calc_error = calculate_pixel_clock_pll_dividers( + clk_src, &clk_src->calc_pll, pll_settings); @@ -568,7 +569,6 @@ static uint32_t dce110_get_pix_clk_dividers( { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; - DC_LOGGER_INIT(); if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { @@ -600,7 +600,6 @@ static uint32_t dce112_get_pix_clk_dividers( struct pll_settings *pll_settings) { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - DC_LOGGER_INIT(); if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { @@ -1442,8 +1441,6 @@ static uint32_t dcn3_get_pix_clk_dividers( unsigned long long actual_pix_clk_100Hz = pix_clk_params ? pix_clk_params->requested_pix_clk_100hz : 0; struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - DC_LOGGER_INIT(); - if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { DC_LOG_ERROR( @@ -1513,7 +1510,6 @@ static const struct clock_source_funcs dce110_clk_src_funcs = { .get_dp_dto_frequency_100hz = get_dp_dto_frequency_100hz }; - static void get_ss_info_from_atombios( struct dce110_clk_src *clk_src, enum as_signal_type as_signal, @@ -1526,7 +1522,7 @@ static void get_ss_info_from_atombios( struct spread_spectrum_info *ss_info_cur; struct spread_spectrum_data *ss_data_cur; uint32_t i; - DC_LOGGER_INIT(); + if (ss_entries_num == NULL) { DC_LOG_SYNC( "Invalid entry !!!\n"); @@ -1657,6 +1653,7 @@ static void ss_info_from_atombios_create( } static bool calc_pll_max_vco_construct( + struct dce110_clk_src *clk_src, struct calc_pll_clock_source *calc_pll_cs, struct calc_pll_clock_source_init_data *init_data) { @@ -1808,6 +1805,7 @@ bool dce110_clk_src_construct( ss_info_from_atombios_create(clk_src); if (!calc_pll_max_vco_construct( + clk_src, &clk_src->calc_pll, &calc_pll_cs_init_data)) { ASSERT_CRITICAL(false); @@ -1822,7 +1820,7 @@ bool dce110_clk_src_construct( if (!calc_pll_max_vco_construct( - &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + clk_src, &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { ASSERT_CRITICAL(false); goto unexpected_failure; } -- cgit v1.2.3 From 1833ce36b35426504c64600c94f322437ea44bb2 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 16 Jul 2026 06:42:18 -0700 Subject: mm: memcg: initialize *locked in memcg1_oom_prepare() stub mem_cgroup_oom() passes an uninitialized "locked" to memcg1_oom_prepare() and reads it back in memcg1_oom_finish(): bool locked, ret; ... if (!memcg1_oom_prepare(memcg, &locked)) return false; ret = mem_cgroup_out_of_memory(memcg, mask, order); memcg1_oom_finish(memcg, locked); This relies on memcg1_oom_prepare() setting *locked whenever it returns true. The CONFIG_MEMCG_V1=y version does, but the stub used when CONFIG_MEMCG_V1=n returns true without touching *locked, so memcg1_oom_finish() consumes an uninitialized value. On a memcg OOM this is reported by UBSAN: UBSAN: invalid-load in mm/memcontrol.c:1932:27 load of value 0 is not a valid value for type 'bool' (aka '_Bool') Initialize *locked to false in the stub; with cgroup v1 compiled out there is no OOM lock to take. Link: https://lore.kernel.org/20260716-memcg-oom-uninit-locked-v2-1-63631d878eb4@debian.org Fixes: e93d4166b40a ("mm: memcg: put cgroup v1-specific code under a config option") Signed-off-by: Breno Leitao Reviewed-by: Joshua Hahn Acked-by: Johannes Weiner Reviewed-by: SeongJae Park Acked-by: Shakeel Butt Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Cc: Shakeel Butt Cc: Signed-off-by: Andrew Morton --- mm/memcontrol-v1.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mm/memcontrol-v1.h b/mm/memcontrol-v1.h index f92f81108d5e..4fa6e2bc8413 100644 --- a/mm/memcontrol-v1.h +++ b/mm/memcontrol-v1.h @@ -107,7 +107,11 @@ static inline void memcg1_remove_from_trees(struct mem_cgroup *memcg) {} static inline void memcg1_soft_limit_reset(struct mem_cgroup *memcg) {} static inline void memcg1_css_offline(struct mem_cgroup *memcg) {} -static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) { return true; } +static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) +{ + *locked = false; + return true; +} static inline void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked) {} static inline void memcg1_oom_recover(struct mem_cgroup *memcg) {} -- cgit v1.2.3 From 4bd0c3515a041d9aa1558c4e8ea0efdb56509f9a Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 17 Jul 2026 00:44:59 -0600 Subject: mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio() Patch series "mm: fix PMD level mTHP accounting bugs", v2. While running selftests I noticed the PMD level per-mTHP stats (nr_anon) remained elevated after each run. After further investigation I noticed this accounting error occurs for both the migration.private_anon_htlb_test and the HMM tests. In the HMM case this is due to folio_add_new_anon_rmap() incrementing the mTHP stats, but never containing a corresponding decrement in free_zone_device_folio(). We solve this by making sure to decrement the counter when freeing device memory. In the migration case, we are incrementing this counter without first checking whether this folio is a hugetlb folio, which relies on a separate accounting system. We solve this by adding the proper hugetlb check before incrementing this counter. With these changes in place, the two tests no longer cause elevated PMD level accounting issues. This patch (of 2): When a zone device folio is mapped as anonymous, folio_add_new_anon_rmap() increments MTHP_STAT_NR_ANON. The corresponding decrement lives in __free_pages_prepare() in page_alloc.c, but zone device folios are freed via free_zone_device_folio() which never calls __free_pages_prepare(). This causes nr_anon to remain permanently elevated after zone device folios are freed. Add the missing mod_mthp_stat() decrement to free_zone_device_folio() so that the counter is properly balanced. Link: https://lore.kernel.org/20260717064502.1980173-1-npache@redhat.com Link: https://lore.kernel.org/20260717064502.1980173-2-npache@redhat.com Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size") Co-developed-by: David Hildenbrand Signed-off-by: David Hildenbrand Signed-off-by: Nico Pache Reviewed-by: Zi Yan Cc: Alistair Popple Cc: Barry Song Cc: Byungchul Park Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Oscar Salvador Cc: Rakie Kim Cc: Baolin Wang Signed-off-by: Andrew Morton --- mm/memremap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/memremap.c b/mm/memremap.c index 81766d822400..accba23aef28 100644 --- a/mm/memremap.c +++ b/mm/memremap.c @@ -425,6 +425,7 @@ void free_zone_device_folio(struct folio *folio) mem_cgroup_uncharge(folio); if (folio_test_anon(folio)) { + mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, -1); for (i = 0; i < nr; i++) __ClearPageAnonExclusive(folio_page(folio, i)); } -- cgit v1.2.3 From d09a8fd521476950079ee01a1408e6f5b7a0aa3e Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 17 Jul 2026 00:45:00 -0600 Subject: mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __folio_migrate_mapping() increments MTHP_STAT_NR_ANON for the destination folio when `folio_test_anon(folio) && folio_test_large(folio)` is true. However, hugetlb folios satisfy both conditions despite having a completely separate accounting system — they use hugetlb_add_anon_rmap() which does not touch mTHP stats, and their free path also bypasses the mTHP decrement in __free_pages_prepare(). This causes MTHP_STAT_NR_ANON to be incremented on each hugetlb migration without a corresponding decrement, permanently inflating the nr_anon counter. Add a !folio_test_hugetlb() check to __folio_migrate_mapping() so that only actual mTHP folios are counted. Link: https://lore.kernel.org/20260717064502.1980173-3-npache@redhat.com Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size") Co-developed-by: David Hildenbrand Signed-off-by: David Hildenbrand Signed-off-by: Nico Pache Reviewed-by: Zi Yan Reviewed-by: Baolin Wang Cc: Alistair Popple Cc: Barry Song Cc: Byungchul Park Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Oscar Salvador Cc: Rakie Kim Signed-off-by: Andrew Morton --- mm/migrate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/migrate.c b/mm/migrate.c index d9b23909d716..dd15a84b2a52 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -590,7 +590,8 @@ static int __folio_migrate_mapping(struct address_space *mapping, /* No turning back from here */ newfolio->index = folio->index; newfolio->mapping = folio->mapping; - if (folio_test_anon(folio) && folio_test_large(folio)) + if (folio_test_anon(folio) && folio_test_large(folio) && + !folio_test_hugetlb(folio)) mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1); if (folio_test_swapbacked(folio)) __folio_set_swapbacked(newfolio); -- cgit v1.2.3 From 45ebe4540817cb540a59e4dc04014ac5dddc9990 Mon Sep 17 00:00:00 2001 From: Jiakai Xu Date: Thu, 16 Jul 2026 11:53:25 +0000 Subject: riscv/mm: use physical alignment for vmemmap_start_pfn RISC-V computes vmemmap_start_pfn by rounding phys_ram_base down to VMEMMAP_ADDR_ALIGN. That alignment must therefore be expressed in the physical-address domain. Commit 476849b0fba4 ("riscv/mm: align vmemmap to maximal folio size") attempted to account for the maximal folio alignment by feeding MAX_FOLIO_VMEMMAP_ALIGN directly into VMEMMAP_ADDR_ALIGN. However, MAX_FOLIO_VMEMMAP_ALIGN is measured in bytes of struct page storage, whereas VMEMMAP_ADDR_ALIGN is used to align a physical address. The mask-based compound_info encoding requires pfn_to_page(0) to be naturally aligned to MAX_FOLIO_VMEMMAP_ALIGN. Commit 9f94db4c7eaa ("mm/sparse: check memmap alignment for compound_info_has_mask()") added a check for that requirement and exposed the unit mismatch on systems such as QEMU virt, where the DRAM base is not aligned to MAX_FOLIO_NR_PAGES * PAGE_SIZE. Here is the log: [ 0.000000][ C0] ------------[ cut here ]------------ [ 0.000000][ C0] WARNING: mm/sparse.c:365 at sparse_init+0x58a/0x6fe, CPU#0: swapper/0 [ 0.000000][ C0] Modules linked in: [ 0.000000][ C0] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 7.2.0-rc3-g1d8304bdd65f #2 PREEMPT [ 0.000000][ C0] Hardware name: riscv-virtio,qemu (DT) [ 0.000000][ C0] epc : sparse_init+0x58a/0x6fe [ 0.000000][ C0] ra : sparse_init+0x58a/0x6fe [ 0.000000][ C0] epc : ffffffff86851c88 ra : ffffffff86851c88 sp : ffffffff88807a30 [ 0.000000][ C0] gp : ffffffff8a3bf240 tp : ffffffff88842080 t0 : ff600000ffab6000 [ 0.000000][ C0] t1 : 000000017fab6000 t2 : 65203a6573726363 s0 : ffffffff88807bc0 [ 0.000000][ C0] s1 : 000000000e000000 a0 : 0000000000000007 a1 : 0000000000000000 [ 0.000000][ C0] a2 : 0000000000000002 a3 : ffffffff86851c88 a4 : 0000000000000000 [ 0.000000][ C0] a5 : ffffffff88843080 a6 : 0000000000000003 a7 : 0000000000000000 [ 0.000000][ C0] s2 : ff60000000000000 s3 : 0040000000000000 s4 : 0004000000000000 [ 0.000000][ C0] s5 : ffffffff8a4d92e0 s6 : ff600000ffab55e0 s7 : ffffffff88384d00 [ 0.000000][ C0] s8 : 0000000000000003 s9 : ffffffff88384cc1 s10: ffffffff88384cc0 [ 0.000000][ C0] s11: ffffffff8a4daae0 t3 : ffffffff915e8b20 t4 : ffffffff915e8b20 [ 0.000000][ C0] t5 : ffffffff915e8b20 t6 : ffffffff915e8bc8 ssp : 0000000000000000 [ 0.000000][ C0] status: 0000000200000100 badaddr: ffffffff86851c88 cause: 0000000000000003 [ 0.000000][ C0] [] sparse_init+0x58a/0x6fe [ 0.000000][ C0] [] mm_core_init_early+0x116/0x1e30 [ 0.000000][ C0] [] start_kernel+0xd2/0x848 Convert MAX_FOLIO_VMEMMAP_ALIGN to the equivalent physical alignment before using it in VMEMMAP_ADDR_ALIGN. This keeps the existing round_down() logic while making the resulting vmemmap base satisfy the mask-alignment requirement. Link: https://lore.kernel.org/20260716115326.3466926-1-xujiakai2025@iscas.ac.cn Fixes: 476849b0fba4 ("riscv/mm: align vmemmap to maximal folio size") Signed-off-by: Jiakai Xu Reviewed-by: Kiryl Shutsemau (Meta) Cc: Albert Ou Cc: Alexandre Ghiti Cc: David Hildenbrand Cc: Guo Ren Cc: Mike Rapoport Cc: Muchun Song Cc: Nam Cao Cc: Palmer Dabbelt Cc: Vishal Moola (Oracle) Assisted-by: YuanSheng:DeepSeek-V4-Flash Cc: Signed-off-by: Andrew Morton --- arch/riscv/mm/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 3e450890be07..422efa11824b 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -63,7 +63,8 @@ EXPORT_SYMBOL(phys_ram_base); #ifdef CONFIG_SPARSEMEM_VMEMMAP #define VMEMMAP_ADDR_ALIGN max(1ULL << SECTION_SIZE_BITS, \ - MAX_FOLIO_VMEMMAP_ALIGN) + PFN_PHYS(MAX_FOLIO_VMEMMAP_ALIGN / \ + sizeof(struct page))) unsigned long vmemmap_start_pfn __ro_after_init; EXPORT_SYMBOL(vmemmap_start_pfn); -- cgit v1.2.3 From e923bd21058ea02fd0dcd3549d151d143fd036e5 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Thu, 16 Jul 2026 10:54:24 +0100 Subject: mm/huge_memory: unlock i_mmap_rwsem before releasing after-split folios __folio_split() keeps dereferencing the mapping after the split: shmem_uncharge(mapping->host) and remap_page() while the folios are still frozen/locked, and i_mmap_unlock_read(mapping) at the very end, after the after-split folios have been unlocked and freed. Nothing holds an inode reference across that. The split relies on @folio -- which the beyond-EOF drop loop never removes, as it starts at folio_next(folio) -- staying locked and in the page cache to hold off eviction. But the unlock loop unlocks @folio before i_mmap_unlock_read() runs. If the caller's @lock_at is a tail beyond EOF, as memory_failure() passes when splitting a poisoned tail of a shmem THP that reaches past i_size during truncation, it too is gone from the page cache; so once @folio is unlocked no locked, in-cache folio pins the inode, and a concurrent final iput() can evict and RCU-free it before i_mmap_unlock_read() touches i_mmap_rwsem: BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790 i_mmap_unlock_read include/linux/fs.h:537 [inline] __folio_split+0x732/0x1640 mm/huge_memory.c:4100 try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675 memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470 Freed by task 4601: shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177 evict+0x57f/0xac0 fs/inode.c:870 Do every mapping dereference while @folio still pins the inode: drop i_mmap_rwsem right after remap_page(), before the loop that unlocks and frees the after-split folios, and clear @mapping so the exit path does not unlock it again. shmem_uncharge() and remap_page() already run before that point, so after this nothing past the unlock loop touches the inode or the mapping. This is now a rule the split depends on, alongside keeping @folio frozen until the page cache is updated: no inode or mapping dereference once the after-split folios start being unlocked. Link: https://lore.kernel.org/20260716095424.471052-1-kirill@shutemov.name Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()") Signed-off-by: Kiryl Shutsemau (Meta) Reported-by: Hao Zhang Closes: https://lore.kernel.org/linux-mm/20260710071344.GA106129@zh-pc Co-developed-by: Hao Zhang Signed-off-by: Hao Zhang Acked-by: David Hildenbrand (Arm) Reviewed-by: Zi Yan Reviewed-by: Baolin Wang Reviewed-by: Miaohe Lin Cc: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Lance Yang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Naoya Horiguchi Cc: Nico Pache Cc: Ryan Roberts Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 032702a4637b..58cabe6af33d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4113,6 +4113,18 @@ fail: remap_page(folio, 1 << old_order, ttu_flags); + /* + * Drop the mapping while the inode is still pinned. @folio stays + * locked and present in the page cache until the loop below, so + * eviction cannot free the inode yet; @lock_at is not enough, it may + * be a tail beyond EOF that the split already dropped from the page + * cache. Nothing past this point may touch the inode or the mapping. + */ + if (mapping) { + i_mmap_unlock_read(mapping); + mapping = NULL; + } + /* * Unlock all after-split folios except the one containing * @lock_at page. If @folio is not split, it will be kept locked. -- cgit v1.2.3 From 598356522b3a7c337fbf643561ee9b2ba868840e Mon Sep 17 00:00:00 2001 From: Burak Emir Date: Sat, 18 Jul 2026 17:13:03 +0200 Subject: MAINTAINERS: update address for Burak Emir Update MAINTAINERS and .mailmap to point to my gmail.com address: burak.emir@gmail.com. Link: https://lore.kernel.org/20260718151303.2649-1-burak.emir@gmail.com Signed-off-by: Burak Emir Cc: Alice Ryhl Cc: Jakub Kacinski Cc: Martin Kepplinger Cc: Yury Norov (NVIDIA) Signed-off-by: Andrew Morton --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index a8be42f87e02..de68ca3cbd23 100644 --- a/.mailmap +++ b/.mailmap @@ -176,6 +176,7 @@ Brian Cain Brian King Brian Silverman Bryan Tan +Burak Emir Cai Huoqing Casey Connolly Casey Connolly diff --git a/MAINTAINERS b/MAINTAINERS index 2f9472c1a090..39c9413355f3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4634,7 +4634,7 @@ F: rust/helpers/cpumask.c BITMAP API [RUST] M: Alice Ryhl -M: Burak Emir +M: Burak Emir R: Yury Norov S: Maintained F: lib/find_bit_benchmark_rust.rs -- cgit v1.2.3 From 1fcac73551425c6924cca5cc29f10a5caf80907b Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sat, 18 Jul 2026 10:29:23 -0700 Subject: arm64, mailmap: update email address for Peter Collingbourne I am no longer at Google. Link: https://lore.kernel.org/20260718172923.8297-1-peter@pcc.me.uk Signed-off-by: Peter Collingbourne Cc: Catalin Marinas Cc: Ian Rogers Cc: Jakub Kacinski Cc: Martin Kepplinger Cc: Nick Desaulniers Cc: Will Deacon Signed-off-by: Andrew Morton --- .mailmap | 1 + arch/arm64/kernel/pi/relocate.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index de68ca3cbd23..c9c3a6b2b1f5 100644 --- a/.mailmap +++ b/.mailmap @@ -696,6 +696,7 @@ Paulo Alcantara Paulo Alcantara Pavankumar Kondeti Peter A Jonsson +Peter Collingbourne Peter Hilber Peter Oruba Peter Oruba diff --git a/arch/arm64/kernel/pi/relocate.c b/arch/arm64/kernel/pi/relocate.c index 2407d2696398..82592f3a5c1c 100644 --- a/arch/arm64/kernel/pi/relocate.c +++ b/arch/arm64/kernel/pi/relocate.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright 2023 Google LLC // Authors: Ard Biesheuvel -// Peter Collingbourne +// Peter Collingbourne #include #include -- cgit v1.2.3 From 8c7372c5b155fff151a111f03ec0fb87c4c063fe Mon Sep 17 00:00:00 2001 From: "Nico Pache (Red Hat)" Date: Tue, 21 Jul 2026 04:18:11 -0600 Subject: MAINTAINERS: update Nico Pache's email address Switch my entry in MAINTAINERS and .mailmap to my @linux.dev email address Link: https://lore.kernel.org/20260721101811.115954-1-nico.pache@linux.dev Signed-off-by: Nico Pache Signed-off-by: Nico Pache (Red Hat) Acked-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index c9c3a6b2b1f5..ca6dc2575802 100644 --- a/.mailmap +++ b/.mailmap @@ -649,6 +649,7 @@ Nicholas Piggin Nicholas Piggin Nicholas Piggin Nicolas Ferre +Nico Pache Nicolas Pitre Nicolas Pitre Nicolas Saenz Julienne diff --git a/MAINTAINERS b/MAINTAINERS index 39c9413355f3..48b2baa1541f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17253,7 +17253,7 @@ M: Lorenzo Stoakes R: Zi Yan R: Baolin Wang R: Liam R. Howlett -R: Nico Pache +R: Nico Pache R: Ryan Roberts R: Dev Jain R: Barry Song -- cgit v1.2.3 From dc37771a43d4a8762f05060c28463b0c20e6dc9b Mon Sep 17 00:00:00 2001 From: Richard Chang Date: Mon, 20 Jul 2026 04:41:03 +0000 Subject: mm: vmscan: abort proactive reclaim early when freezing for suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proactive reclaim (triggered via memory.reclaim or node sysfs) checks for pending signals in its outer loop in user_proactive_reclaim(). However, the inner reclaim loops—specifically scanning cgroups in shrink_many() and evicting/aging folios in try_to_shrink_lruvec()—can run for a long time before returning to the outer loop, especially on systems with many cgroups or large memory sizes. During system suspend, the PM freezer attempts to freeze all tasks by sending fake signals (setting TIF_SIGPENDING). Because the inner loops do not check for pending signals, the proactive reclaim task can remain stuck in kernel space for seconds, failing to enter the refrigerator in a timely manner. This leads to suspend failures due to freeze timeouts, a behavior observed on Android devices. This latency issue is specific to proactive reclaim because of its large, user-defined reclaim targets (could be gigabytes). Since commit 287d5fedb377 ("mm: memcg: use larger batches for proactive reclaim"), proactive reclaim uses larger decaying batch sizes (starting at 1/4 of the remaining target) to maintain throughput. This keeps the task in the inner reclaim loop for extended periods. In contrast, reactive reclaim (global/memcg) uses small targets (SWAP_CLUSTER_MAX, typically 32 pages), allowing it to return to the outer loop and check signals frequently. To fix this, add a signal_pending() check to should_abort_scan() for proactive reclaim paths. Since should_abort_scan() is called within the inner scanning and eviction loops, this allows proactive reclaim to abort early and return to the outer loop in user_proactive_reclaim(). Additionally, return -ERESTARTSYS instead of -EINTR in user_proactive_reclaim(). When interrupted by system suspend, returning -ERESTARTSYS allows the task to enter the refrigerator and automatically restart the syscall upon resume, making the freezer transparent to userspace. For real signals, the signal layer will either restart the syscall (if SA_RESTART is set) or return -EINTR to userspace. This fix specifically targets Multi-Gen LRU (MGLRU). Classic LRU's scan targets per iteration are strictly bounded by get_scan_count(), which ensures it returns to the outer loop more frequently. The check in should_abort_scan() is limited to proactive reclaim (sc->proactive) to avoid inadvertently affecting reactive reclaim paths, and is wrapped in unlikely() as it is a slow path. Link: https://lore.kernel.org/20260720044103.905191-1-richardycc@google.com Fixes: 287d5fedb377 ("mm: memcg: use larger batches for proactive reclaim") Fixes: 94968384dde1 ("memcg: introduce per-memcg reclaim interface") Suggested-by: Michal Hocko Suggested-by: Oleg Nesterov Signed-off-by: Richard Chang Acked-by: Michal Hocko Cc: Axel Rasmussen Cc: Barry Song Cc: David Hildenbrand Cc: Johannes Weiner Cc: Kairui Song Cc: Lorenzo Stoakes Cc: Martin Liu Cc: Minchan Kim Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: T.J. Mercier Cc: Wei Xu Cc: Yuanchu Xie Signed-off-by: Andrew Morton --- mm/vmscan.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 1a142c58700d..56708d1d2dfd 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4926,6 +4926,9 @@ static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc) int i; enum zone_watermarks mark; + if (unlikely(sc->proactive && signal_pending(current))) + return true; + if (sc->nr_reclaimed >= max(sc->nr_to_reclaim, compact_gap(sc->order))) return true; @@ -7906,8 +7909,15 @@ int user_proactive_reclaim(char *buf, unsigned long batch_size = (nr_to_reclaim - nr_reclaimed) / 4; unsigned long reclaimed; + /* + * Return -ERESTARTSYS to allow the freezer to interrupt the + * task. The syscall will be transparently restarted upon + * resume. For real signals, it either restarts the syscall + * (if SA_RESTART is set) or is converted to -EINTR by the + * signal layer. + */ if (signal_pending(current)) - return -EINTR; + return -ERESTARTSYS; /* * This is the final attempt, drain percpu lru caches in the -- cgit v1.2.3 From 0b45f6927a14914ff685fe0e6f9d11232a1e03df Mon Sep 17 00:00:00 2001 From: Link Lin Date: Tue, 21 Jul 2026 00:55:33 +0000 Subject: mm/page_reporting: use system_freezable_wq to fix UAF during suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During PM freeze (e.g. S3 suspend or S4 hibernation), device drivers like virtio_balloon reset their underlying virtio devices and delete their virtqueues via vdev->config->del_vqs(). However, page reporting work (page_reporting_process) was scheduled on the global system_wq. Because system_wq lacks the WQ_FREEZABLE flag, the PM freezer skips it, leaving page_reporting_process active during suspend. If pages are freed into the buddy allocator while suspending (for example, when core MM invokes the balloon shrinker during S4 hibernation image saving), page reporting triggers virtballoon_free_page_report() on deleted virtqueues, resulting in a Use-After-Free / General Protection Fault: [ 196.795226] general protection fault, probably for non-canonical address 0xaa1436fe70dae6df: 0000 [#1] SMP NOPTI [ 196.825967] Workqueue: events page_reporting_process [ 196.831038] RIP: 0010:virtqueue_add_split+0x233/0x4c0 [virtio_ring] [ 196.927073] virtballoon_free_page_report+0x3a/0xe0 [virtio_balloon] [ 196.946943] page_reporting_process+0x370/0x4f0 Fix this by switching page reporting work to system_freezable_wq. This ensures that the PM freezer pauses page_reporting_process before device drivers destroy their reporting virtqueues. Because the reporting worker is frozen, memory reclamation/freeing (e.g. via shrinker execution) can safely return pages to MM during freeze without triggering unfrozen reporting work on deleted virtqueues. This aligns with the driver's existing design. The comment in virtballoon_freeze() states: /* * The workqueue is already frozen by the PM core before this * function is called. */ Testing: I have verified these fixes using Google’s virtualization infrastructure by running continuous suspend/resume iterations (40+ cycles) while churning memory using stress-ng (`stress-ng --vm 4 --vm-bytes 60% --timeout 1`) to constantly create free pages for the buddy allocator. We also set the `page_reporting_order` parameter to 0 to make the page reporting worker highly sensitive, forcing it to pick up any 4K free pages. This confirmed that the UAF crashes are no longer reproducible. Link: https://lore.kernel.org/20260721005603.1710551-1-linkl@google.com Fixes: 36e66c554b5c ("mm: introduce Reported pages") Signed-off-by: Link Lin Suggested-by: David Hildenbrand (Arm) Suggested-by: Michael S. Tsirkin Acked-by: David Rientjes Acked-by: David Hildenbrand (Arm) Acked-by: Michael S. Tsirkin Cc: Alexander Duyck Cc: Greg Thelen Cc: James Houghton Cc: Jason Wang Cc: Jiaqi Yan Cc: Vlastimil Babka Cc: Xuan Zhuo Cc: Signed-off-by: Andrew Morton --- mm/page_reporting.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mm/page_reporting.c b/mm/page_reporting.c index 942e84b6908a..3e30731b940e 100644 --- a/mm/page_reporting.c +++ b/mm/page_reporting.c @@ -80,7 +80,8 @@ __page_reporting_request(struct page_reporting_dev_info *prdev) * now we are limiting this to running no more than once every * couple of seconds. */ - schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY); + queue_delayed_work(system_freezable_wq, &prdev->work, + PAGE_REPORTING_DELAY); } /* notify prdev of free page reporting request */ @@ -340,7 +341,8 @@ err_out: */ state = atomic_cmpxchg(&prdev->state, state, PAGE_REPORTING_IDLE); if (state == PAGE_REPORTING_REQUESTED) - schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY); + queue_delayed_work(system_freezable_wq, &prdev->work, + PAGE_REPORTING_DELAY); } static DEFINE_MUTEX(page_reporting_mutex); -- cgit v1.2.3 From 8f6f9fd93cd7a5dd607ad5cd910476dd68fff3ed Mon Sep 17 00:00:00 2001 From: Chris Gellermann Date: Wed, 22 Jul 2026 15:02:45 +0200 Subject: selftests/clone3: fix wild pointer access of getline due to missing init Patch series "selftests: Add missing initalization of pointer passed to getline", v2. This patch (of 2): Clone3_set_tid uses getline(&line, ...) in a loop to read the child's process status. The code expects that getline allocates the buffer for the line on the first loop iteration. According to the Open Group Spec[1], char *line has to be null pointer for this: > ssize_t getline(char **restrict lineptr, ...); > If *lineptr is a null pointer or if the object pointed to by *lineptr > is of insufficient size, an object shall be allocated as if by malloc() > or the object shall be reallocated as if by realloc()[...]. However, char *line is only declared, leading to an undefined value that is potentially non-null. In an example run with Musl v1.2.6, the realloc call[2] of getdelim, which implements getline, triggers a segfault: ./run_kselftest.sh --test clone3:clone3_set_tid [ 1366.165898] kselftest: Running tests in clone3 ... [ 1367.799244] clone3_set_tid[811]: unhandled signal 11 code 0x1 at 0x0000000000000000 in libc.so[68184,3fbf69f000+4c000] [ 1367.802808] CPU: 0 UID: 0 PID: 811 Comm: clone3_set_tid Not tainted .. [ 1367.804188] epc: 0x0000003fbf6b0184 [ 1367.804188] ra : 0x0000003fbf6d4664 [ 1367.804188] sp : 0x0000003fce5f2e40 [ 1367.805314] gp : 0x0000002aaab0dfb8 [ 1367.805314] tp : 0x0000003fbf6f14a8 [ 1367.805314] t0 : 0x0000003fbf63d000 ... Looking at the realloc implementation, Musl mallocs for a null pointer memory. But for a non-null pointer, it assumes it's passed a valid pointer to the heap and tries to access its meta-data. This leads to the segfault we see: void *realloc(void *p, size_t n) { if (!p) return malloc(n); if (size_overflows(n)) return 0; struct meta *g = get_meta(p); ... } Fix this by properly initializing the line pointer to NULL. Link: https://lore.kernel.org/20260722130246.2135563-1-christian.gellermann@codasip.com Link: https://lore.kernel.org/20260722130246.2135563-2-christian.gellermann@codasip.com Link: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getline.html [1] Link: https://git.musl-libc.org/cgit/musl/tree/src/stdio/getdelim.c#n38 [2] Fixes: 41585bbeeef9 ("selftests: add tests for clone3() with *set_tid") Signed-off-by: Chris Gellermann Acked-by: David Hildenbrand (arm) Reviewed-by: Lorenzo Stoakes Cc: Christian Brauner Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/clone3/clone3_set_tid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/clone3/clone3_set_tid.c b/tools/testing/selftests/clone3/clone3_set_tid.c index 5c944aee6b41..485efa7c9eed 100644 --- a/tools/testing/selftests/clone3/clone3_set_tid.c +++ b/tools/testing/selftests/clone3/clone3_set_tid.c @@ -141,7 +141,7 @@ int main(int argc, char *argv[]) { FILE *f; char buf; - char *line; + char *line = NULL; int status; int ret = -1; size_t len = 0; -- cgit v1.2.3 From 9f1d75a4ce04095afdb63d8e540092ff8151dacf Mon Sep 17 00:00:00 2001 From: Chris Gellermann Date: Wed, 22 Jul 2026 15:02:46 +0200 Subject: selftests/mm: fix potential wild pointer access of getline due to missing init This is another occurrence of using getline where the code assumes that getline allocates memory to store the line, but the pointer passed to it is uninitialized and potentially a non-null pointer. This violates the Open Group Spec[1] and caused a segfault in a similar situation in selftest/clone3/clone3_set_tid. Fix it by initializing the line pointer to NULL. The issue has been found by simply grepping through the selftest code after running into the issue in clone3_set_tid. Whether it segfaults in its current state is unknown to me. But it's good to be addressed due to defensive reasons. Link: https://lore.kernel.org/20260722130246.2135563-3-christian.gellermann@codasip.com Link: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getline.html [1] Fixes: 26b4224d9961 ("selftests: expanding more mlock selftest") Signed-off-by: Chris Gellermann Acked-by: David Hildenbrand (arm) Reviewed-by: Lorenzo Stoakes Cc: Christian Brauner Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/mlock-random-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/mlock-random-test.c b/tools/testing/selftests/mm/mlock-random-test.c index 9d349c151360..16294bc7dae6 100644 --- a/tools/testing/selftests/mm/mlock-random-test.c +++ b/tools/testing/selftests/mm/mlock-random-test.c @@ -84,7 +84,7 @@ int get_proc_locked_vm_size(void) int get_proc_page_size(unsigned long addr) { FILE *smaps; - char *line; + char *line = NULL; unsigned long mmupage_size = 0; size_t size; -- cgit v1.2.3 From 0fe1e3e8f3380d7862296a73b528d164e96c76b8 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Sun, 26 Jul 2026 17:08:05 +0200 Subject: net: phylink: put link_gpio if phylink_create fails In phylink_create() if phylink_register_sfp() returns an error, link_gpio obtained by phylink_parse_fixedlink() is never released. While this is a very unlikely scenario, it's worth to fix/handle this. This was present from the very first implementation of phylink but got relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") where additional function were added after phylink_parse_fixedlink() making the release of link_gpio needed if such additional function errored out. While at it, restructure the exit condition of phylink_create() with the goto pattern to reduce code duplication on handling error conditions. Fixes: ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") Signed-off-by: Christian Marangi Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260726150806.2437-1-ansuelsmth@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 087ac63f9193..18d2ead97aa5 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1875,8 +1875,8 @@ struct phylink *phylink_create(struct phylink_config *config, } else if (config->type == PHYLINK_DEV) { pl->dev = config->dev; } else { - kfree(pl); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto free_pl; } pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops); @@ -1909,28 +1909,29 @@ struct phylink *phylink_create(struct phylink_config *config, phylink_validate(pl, pl->supported, &pl->link_config); ret = phylink_parse_mode(pl, fwnode); - if (ret < 0) { - kfree(pl); - return ERR_PTR(ret); - } + if (ret < 0) + goto free_pl; if (pl->cfg_link_an_mode == MLO_AN_FIXED) { ret = phylink_parse_fixedlink(pl, fwnode); - if (ret < 0) { - kfree(pl); - return ERR_PTR(ret); - } + if (ret < 0) + goto release_link_gpio; } pl->req_link_an_mode = pl->cfg_link_an_mode; ret = phylink_register_sfp(pl, fwnode); - if (ret < 0) { - kfree(pl); - return ERR_PTR(ret); - } + if (ret < 0) + goto release_link_gpio; return pl; + +release_link_gpio: + if (pl->link_gpio) + gpiod_put(pl->link_gpio); +free_pl: + kfree(pl); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(phylink_create); -- cgit v1.2.3 From 2e1d2e65e773d67dab163127f11a47dab0fbca9f Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 9 Jul 2026 10:01:33 +0900 Subject: ata: libata-scsi: terminate deferred commands on time out If a command times out while we have deferred non-NCQ commands waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred commands are never issued nor completed, thus leaving the SCSI host in a busy state (shost->host_failed != scsi_host_busy(shost)) which prevents the SCSI EH task from being woken up. Eventually, when the deferred commands also time out, the SCSI EH task is woken up and the timeout processing occurs. Avoid this unnecessary SCSI EH task wake-up additional time by scheduling a retry of all waiting deferred QCs, using the eh_timed_out SCSI host template operation. The function ata_scsi_eh_timed_out() is introduced to implement this operation. However, terminating deferred commands with DID_REQUEUE to force a retry by calling the function ata_scsi_requeue_deferred_qc() may still keep the SCSI host in a busy state because the block layer may immediately re-issue these commands. The solution to this is to schedule libata EH for the port which suffered the command timeout to prevent accepting any new command. ata_scsi_requeue_deferred_qc() is modified to add a call to ata_port_schedule_eh() for this purpose. In addition to this change, ata_scsi_requeue_deferred_qc() is also modified to take a new timedout_scmd scsi command argument which indicates the SCSI command that timed out. With this additional argument, ata_scsi_requeue_deferred_qc() can now also terminate with DID_TIME_OUT any timed out deferred qc, which simplifies ata_scsi_cmd_error_handler(). In this case, ata_scsi_requeue_deferred_qc() returns SCSI_EH_DONE, with this return value propagated back to the ata_scsi_eh_timed_out() operation to indicate to scsi_timeout() that the timed out command was handled and no further processing is needed. For non-timed out deferred qc that need to be retried, ata_scsi_requeue_deferred_qc() returns SCSI_EH_NOT_HANDLED, thus indicating to scsi_timeout() that the timed out command needs to go through the SCSI EH (and libata EH) processing by adding it to the EH work queue with scsi_eh_scmd_add(). One side effect of these changes is that the function atapi_qc_complete() needs to be modified to ensure that a deferred ATAPI command that needs to be retried is completed with DID_REQUEUE instead of the default SAM_STAT_GOOD status, and a command that timed out is completed with DID_TIME_OUT instead of SAM_STAT_CHECK_CONDITION. Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Reviewed-by: Igor Pylypiv Tested-by: Igor Pylypiv Reviewed-by: Niklas Cassel Reviewed-by: Martin K. Petersen --- drivers/ata/libata-eh.c | 29 +++--------- drivers/ata/libata-scsi.c | 112 +++++++++++++++++++++++++++++++++++++++++----- drivers/ata/libata.h | 3 +- include/linux/libata.h | 2 + 4 files changed, 111 insertions(+), 35 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 46947ed0c657..c154103d892c 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -658,29 +658,12 @@ int ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, set_host_byte(scmd, DID_OK); ata_qc_for_each_raw(ap, qc, i) { - if (qc->scsicmd != scmd) - continue; - if ((qc->flags & ATA_QCFLAG_ACTIVE) || - qc == qc->dev->link->deferred_qc) + if (qc->scsicmd == scmd && + qc->flags & ATA_QCFLAG_ACTIVE) break; } - if (i < ATA_MAX_QUEUE && qc == qc->dev->link->deferred_qc) { - /* - * This is a deferred command that timed out while - * waiting for the command queue to drain. Since the qc - * is not active yet (deferred_qc is still set, so the - * deferred qc work has not issued the command yet), - * simply signal the timeout by finishing the SCSI - * command and clear the deferred qc to prevent the - * deferred qc work from issuing this qc. - */ - WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE); - qc->dev->link->deferred_qc = NULL; - cancel_work(&qc->dev->link->deferred_qc_work); - set_host_byte(scmd, DID_TIME_OUT); - scsi_eh_finish_cmd(scmd, &ap->eh_done_q); - } else if (i < ATA_MAX_QUEUE) { + if (i < ATA_MAX_QUEUE) { /* the scmd has an associated qc */ if (!(qc->flags & ATA_QCFLAG_EH)) { /* which hasn't failed yet, timeout */ @@ -956,10 +939,10 @@ static void ata_eh_set_pending(struct ata_port *ap, bool fastdrain) ap->pflags |= ATA_PFLAG_EH_PENDING; /* - * If we have a deferred qc, requeue it so that it is retried once EH - * completes. + * If we have deferred QCs, requeue them so that the SCSI EH task can + * run. */ - ata_scsi_requeue_deferred_qc(ap); + ata_scsi_requeue_deferred_qc(ap, NULL); if (!fastdrain) return; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 5868526301a2..02b04de925f9 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1685,26 +1685,80 @@ void ata_scsi_deferred_qc_work(struct work_struct *work) spin_unlock_irqrestore(ap->lock, flags); } -void ata_scsi_requeue_deferred_qc(struct ata_port *ap) +enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *timedout_scmd) { + enum scsi_timeout_action action = SCSI_EH_NOT_HANDLED; + struct ata_queued_cmd *qc; struct ata_link *link; + u32 host_byte; lockdep_assert_held(ap->lock); /* - * If we have a deferred qc when a reset occurs or NCQ commands fail, - * do not try to be smart about what to do with this deferred command - * and simply requeue it by completing it with DID_REQUEUE. + * If we have deferred QCs when a reset, a timeout or an NCQ command + * fails, do not try to be smart about what to do with the deferred + * commands and simply terminate them and let the SCSI layer decide + * what to do. */ ata_for_each_link(link, ap, PMP_FIRST) { - struct ata_queued_cmd *qc = link->deferred_qc; + qc = link->deferred_qc; + if (!qc) + continue; + + /* + * Clear the deferred QC so that the deferred work does not try + * to issue it. + */ + link->deferred_qc = NULL; + cancel_work(&link->deferred_qc_work); + + /* + * We are going to complete some scsi command, either with + * DID_TIME_OUT if the command timed out while waiting for being + * issued, or with DID_REQUEUE if another command timed out or + * we had a failed command. However, the block layer may re-issue + * these commands immediately, keeping the scsi host busy and + * thus preventing the SCSI EH task from running. + * So schedule EH on the port to prevent accepting new commands + * until everything is sorted out with the error or timeout that + * got us here in the first place. Note that we set EH pending + * on the port before calling ata_port_schedule_eh() so that we + * do not reenter this function from ata_eh_set_pending() with + * timedout_scmd being NULL and erroneously retry deferred QCs + * that have timed out on other links. + */ + if (!ata_port_eh_scheduled(ap)) { + ap->pflags |= ATA_PFLAG_EH_PENDING; + ata_port_schedule_eh(ap); + } - if (qc) { - link->deferred_qc = NULL; - cancel_work(&link->deferred_qc_work); - ata_scsi_qc_done(qc, true, DID_REQUEUE << 16); + /* + * If we are being called from scsi_timeout(), then we have a + * non-NULL timedout_scmd. If the timed out command is for a + * deferred QC, terminate that deferred QC with DID_TIME_OUT and + * requeue all other deferred QCs. In this case we need to + * return SCSI_EH_DONE, because the timed out command was + * handled. + * If the timed out command is not for a deferred QC, we need to + * requeue all deferred QCs, and return SCSI_EH_NOT_HANDLED so + * that the timed out command gets added to the EH work queue + * with scsi_eh_scmd_add(), for later handling with libata EH + * ata_scsi_cmd_error_handler(). + * If timedout_scmd is NULL, we simply need to requeue all + * deferred QCs and the return value does not matter as we were + * not called from scsi_timeout(). + */ + if (timedout_scmd && qc->scsicmd == timedout_scmd) { + host_byte = DID_TIME_OUT; + action = SCSI_EH_DONE; + } else { + host_byte = DID_REQUEUE; } + ata_scsi_qc_done(qc, true, host_byte << 16); } + + return action; } static void ata_scsi_schedule_deferred_qc(struct ata_link *link) @@ -1723,13 +1777,41 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link) return; if (ata_port_eh_scheduled(ap)) { - ata_scsi_requeue_deferred_qc(ap); + ata_scsi_requeue_deferred_qc(ap, NULL); return; } if (!ap->ops->qc_defer(qc)) queue_work(system_highpri_wq, &link->deferred_qc_work); } +static enum scsi_timeout_action +ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd) +{ + enum scsi_timeout_action action; + unsigned long flags; + + spin_lock_irqsave(ap->lock, flags); + action = ata_scsi_requeue_deferred_qc(ap, scmd); + spin_unlock_irqrestore(ap->lock, flags); + + return action; +} + +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd) +{ + struct ata_port *ap = ata_shost_to_port(scmd->device->host); + + /* + * ata_scsi_cmd_error_handler() takes care of commands that timed out + * while executing. However, if we have deferred QCs while a timeout + * triggers, we must requeue these commands for retry so that we do not + * unnecessarily delay starting the SCSI EH task until these deferred + * commands also time out. + */ + return ata_scsi_retry_deferred_qc(ap, scmd); +} +EXPORT_SYMBOL_GPL(ata_scsi_eh_timed_out); + static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) { struct ata_link *link = qc->dev->link; @@ -2936,7 +3018,15 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL && qc->dev->sdev) qc->dev->sdev->locked = 0; - ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION); + if (cmd->result) + ata_scsi_qc_done(qc, false, 0); + else + ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION); + return; + } + + if (cmd->result) { + ata_scsi_qc_done(qc, false, 0); return; } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 700627596ce1..8dacc1daabf0 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -180,7 +180,8 @@ enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_port *ap) __must_hold(ap->lock); void ata_scsi_deferred_qc_work(struct work_struct *work); -void ata_scsi_requeue_deferred_qc(struct ata_port *ap); +enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *scmd); /* libata-eh.c */ extern unsigned int ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); diff --git a/include/linux/libata.h b/include/linux/libata.h index 6c0d385b2cde..f10b3287b507 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1153,6 +1153,7 @@ extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd, #endif extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h, struct scsi_cmnd *cmd); +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd); #if IS_REACHABLE(CONFIG_ATA) bool ata_scsi_dma_need_drain(struct request *rq); #else @@ -1464,6 +1465,7 @@ extern const struct attribute_group *ata_common_sdev_groups[]; .ioctl = ata_scsi_ioctl, \ ATA_SCSI_COMPAT_IOCTL \ .queuecommand = ata_scsi_queuecmd, \ + .eh_timed_out = ata_scsi_eh_timed_out, \ .dma_need_drain = ata_scsi_dma_need_drain, \ .this_id = ATA_SHT_THIS_ID, \ .emulated = ATA_SHT_EMULATED, \ -- cgit v1.2.3 From e7468b3f9b404ac7c1329ef07c146c3356dfbd01 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 23 Jul 2026 07:42:26 +0900 Subject: ata: libata-scsi: schedule deferred atapi command Modify atapi_qc_complete() to call ata_scsi_schedule_deferred_qc() to ensure that any deferred queued command can execute. This is similar to ata_scsi_qc_complete() function for regular ATA devices. Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal --- drivers/ata/libata-scsi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 02b04de925f9..cd64787c4772 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2993,6 +2993,7 @@ static void atapi_fixup_inquiry(struct scsi_cmnd *cmd) static void atapi_qc_complete(struct ata_queued_cmd *qc) { + struct ata_link *link = qc->dev->link; struct scsi_cmnd *cmd = qc->scsicmd; unsigned int err_mask = qc->err_mask; @@ -3022,12 +3023,12 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) ata_scsi_qc_done(qc, false, 0); else ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION); - return; + goto schedule_deferred; } if (cmd->result) { ata_scsi_qc_done(qc, false, 0); - return; + goto schedule_deferred; } /* successful completion path */ @@ -3035,6 +3036,9 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) atapi_fixup_inquiry(cmd); ata_scsi_qc_done(qc, true, SAM_STAT_GOOD); + +schedule_deferred: + ata_scsi_schedule_deferred_qc(link); } /** * atapi_xlat - Initialize PACKET taskfile -- cgit v1.2.3 From de202d2251bc181c6019b1ad3c0ba8133e5ec68d Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 9 Jul 2026 16:02:23 +0900 Subject: scsi: libsas: terminate deferred commands on time out If a command times out while we have deferred non-NCQ commands waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred commands are never issued nor completed, thus leaving the SCSI host in a busy state (shost->host_failed != scsi_host_busy(shost)) which prevents the SCSI EH task from being woken up. Eventually, when the deferred commands also time out, the SCSI EH task is woken up and the timeout processing occurs. Avoid this unnecessary additional SCSI EH wake up time with the same method as implemented in libata-scsi, using the eh_timed_out SCSI host template operation. The function sas_eh_timed_out() implements this operation and executes the function ata_scsi_retry_deferred_qc() for SATA devices. Co-developed-by: Igor Pylypiv Signed-off-by: Igor Pylypiv Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Reviewed-by: John Garry Reviewed-by: Hannes Reinecke Tested-by: Igor Pylypiv Reviewed-by: Niklas Cassel Reviewed-by: Martin K. Petersen --- drivers/ata/libata-scsi.c | 5 +++-- drivers/scsi/libsas/sas_scsi_host.c | 15 +++++++++++++++ include/linux/libata.h | 2 ++ include/scsi/libsas.h | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index cd64787c4772..d2160ee7ca7d 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1784,8 +1784,8 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link) queue_work(system_highpri_wq, &link->deferred_qc_work); } -static enum scsi_timeout_action -ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd) +enum scsi_timeout_action ata_scsi_retry_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *scmd) { enum scsi_timeout_action action; unsigned long flags; @@ -1796,6 +1796,7 @@ ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd) return action; } +EXPORT_SYMBOL_GPL(ata_scsi_retry_deferred_qc); enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd) { diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index c83282733ec4..97ac3db5dccb 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -502,6 +502,21 @@ int sas_eh_target_reset_handler(struct scsi_cmnd *cmd) } EXPORT_SYMBOL_GPL(sas_eh_target_reset_handler); +/* + * Handle deferred QCs in case of a command timeout. + * See ata_scsi_eh_timed_out() for details. + */ +enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd) +{ + struct domain_device *dev = cmd_to_domain_dev(cmd); + + if (dev_is_sata(dev)) + return ata_scsi_retry_deferred_qc(dev->sata_dev.ap, cmd); + + return SCSI_EH_NOT_HANDLED; +} +EXPORT_SYMBOL_GPL(sas_eh_timed_out); + /* Try to reset a device */ static int try_to_reset_cmd_device(struct scsi_cmnd *cmd) { diff --git a/include/linux/libata.h b/include/linux/libata.h index f10b3287b507..1827502b9cf2 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1153,6 +1153,8 @@ extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd, #endif extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h, struct scsi_cmnd *cmd); +enum scsi_timeout_action ata_scsi_retry_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *scmd); enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd); #if IS_REACHABLE(CONFIG_ATA) bool ata_scsi_dma_need_drain(struct request *rq); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 163f23c92b41..c7017ae76c61 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -705,6 +705,7 @@ void sas_task_abort(struct sas_task *); int sas_eh_abort_handler(struct scsi_cmnd *cmd); int sas_eh_device_reset_handler(struct scsi_cmnd *cmd); int sas_eh_target_reset_handler(struct scsi_cmnd *cmd); +enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd); extern void sas_target_destroy(struct scsi_target *); extern int sas_sdev_init(struct scsi_device *); @@ -743,6 +744,7 @@ void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event, .this_id = -1, \ .eh_device_reset_handler = sas_eh_device_reset_handler, \ .eh_target_reset_handler = sas_eh_target_reset_handler, \ + .eh_timed_out = sas_eh_timed_out, \ .target_destroy = sas_target_destroy, \ .ioctl = sas_ioctl, \ -- cgit v1.2.3 From 9c33222bd387312874fbe36ca8002e5c945b9653 Mon Sep 17 00:00:00 2001 From: TanZheng Date: Fri, 24 Jul 2026 15:58:50 +0800 Subject: scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE In the iblock_execute_pr_out() function, PRO_PREEMPT, PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability checks through ops->pr_clear. The error check allows unimplemented hooks to pass through the gate, resulting dereferencing a NULL function pointer. Check whether the hooks that need to be called are supported. Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock") Signed-off-by: TanZheng Reviewed-by: Mike Christie Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_iblock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 1087d1d17c36..ea65d39fef88 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -906,7 +906,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, break; case PRO_PREEMPT: case PRO_PREEMPT_AND_ABORT: - if (!ops->pr_clear) { + if (!ops->pr_preempt) { pr_err("block_device does not support pr_preempt.\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } @@ -916,8 +916,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, sa == PRO_PREEMPT_AND_ABORT); break; case PRO_RELEASE: - if (!ops->pr_clear) { - pr_err("block_device does not support pr_pclear.\n"); + if (!ops->pr_release) { + pr_err("block_device does not support pr_release.\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } -- cgit v1.2.3 From 93dde0bf2f39a0f9f57fd610aa3201ce5b753433 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Sun, 12 Jul 2026 20:37:39 +0200 Subject: scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write resp_report_zones() sizes the reply buffer from the CDB allocation length. The v3 fix rounds alloc_len up with ALIGN() before deriving the descriptor count: rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> ilog2(RZONES_DESC_HD); arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1); For alloc_len in 0xFFFFFFC1..0xFFFFFFFF, ALIGN() rounds up to 0x100000000, so arr_len is 4 GB. On 32-bit, kzalloc()'s size_t is 32-bit and truncates 0x100000000 to 0; kzalloc(0) returns ZERO_SIZE_PTR, which passes the !arr check, and desc = arr + 64 is then dereferenced in the loop -> out-of-bounds write / panic. Clamp rep_max_zones to devip->nr_zones. The loop already stops at sdebug_capacity (after nr_zones zones), so a report can never hold more than nr_zones descriptors; the clamp does not change the report, it only bounds arr_len to (nr_zones + 1) * RZONES_DESC_HD, a real device property that can never reach 0x100000000. Fixes: 7db0e0c8190a ("scsi: scsi_debug: Fix buffer size of REPORT ZONES command") Suggested-by: Damien Le Moal Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Assisted-by: AuditCode-AI:2026.07 Reviewed-by: Damien Le Moal Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260712183739.83915-1-security@auditcode.ai Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 9d1c9c41d0f9..643051332132 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -5890,6 +5890,7 @@ static int resp_report_zones(struct scsi_cmnd *scp, u32 alloc_len, rep_opts, rep_len; bool partial; u64 lba, zs_lba; + u64 arr_len; u8 *arr = NULL, *desc; u8 *cmd = scp->cmnd; struct sdeb_zone_state *zsp = NULL; @@ -5911,9 +5912,12 @@ static int resp_report_zones(struct scsi_cmnd *scp, return check_condition_result; } - rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD); + rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> + ilog2(RZONES_DESC_HD); + rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones); + arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1); - arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN); + arr = kzalloc(arr_len, GFP_ATOMIC | __GFP_NOWARN); if (!arr) { mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); -- cgit v1.2.3 From f71b4a30983b846b4075bf544e835121e70e6a43 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Jul 2026 01:27:26 +0800 Subject: scsi: ufs: core: Cancel RTC work in active-active suspend UFS RTC support schedules ufs_rtc_update_work to periodically update the device RTC. The work can issue query commands and access the UFS host controller. A previous change moved the RTC work cancellation before the PRE_CHANGE vendor suspend callback to close a race in the common suspend path. However, the active-active path jumps directly to vops_suspend after flushing exception handling work and therefore bypasses the cancellation. If the RTC work runs while the vendor suspend callback is gating or otherwise changing hardware state, it can access the controller during suspend and trigger an SError. Cancel the RTC work before entering the vendor suspend callback in the active-active path. Since this path now cancels the work, move the RTC work scheduling outside the device and link state restoration block in the resume path. This restarts RTC updates after an active-active suspend and resume cycle. Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend") Signed-off-by: Guangshuo Li Reviewed-by: Peter Wang Reviewed-by: Bean Huo Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index d3044a3089b5..c3b105b2678e 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -10269,6 +10269,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) req_link_state == UIC_LINK_ACTIVE_STATE) { ufshcd_disable_auto_bkops(hba); flush_work(&hba->eeh_work); + cancel_delayed_work_sync(&hba->ufs_rtc_update_work); goto vops_suspend; } @@ -10478,10 +10479,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (ret) goto set_old_link_state; ufshcd_set_timestamp_attr(hba); - schedule_delayed_work(&hba->ufs_rtc_update_work, - msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); } + schedule_delayed_work(&hba->ufs_rtc_update_work, + msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); + if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) ufshcd_enable_auto_bkops(hba); else -- cgit v1.2.3 From 8a309036f557d3ff4efb2beea5132ba91172d934 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 15 Jul 2026 10:12:28 -0700 Subject: scsi: ufs: core: Revert "Delegate the interrupt service routine to a threaded IRQ handler" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There have been multiple reports of performance regressions caused by commit 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler"). Hence this revert. This patch reverts most of the following commits: * 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler") * 6475cfb81fc4 ("scsi: ufs: core: Avoid IRQ thread wakeup during active UIC command") This patch preserves the following commits: * 034d319c8899 ("scsi: ufs: core: Fix interrupt handling for MCQ Mode") * eabcac808ca3 ("scsi: ufs: core: Fix IRQ lock inversion for the SCSI host lock") Cc: Neil Armstrong Cc: 孙魁 (Kui Sun) Cc: André Draszik Cc: Gregory CLEMENT Cc: Sebastian Andrzej Siewior Fixes: 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler") Signed-off-by: Bart Van Assche Reviewed-by: Sebastian Andrzej Siewior Tested-by: André Draszik # on Pixel 6 Reviewed-by: André Draszik Link: https://patch.msgid.link/b70eb60a01f971bed68c42c5b555929db5f835df.1784135511.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index c3b105b2678e..34228beb3f59 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -7357,7 +7357,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) } /** - * ufshcd_threaded_intr - Threaded interrupt service routine + * ufshcd_intr - Main interrupt service routine * @irq: irq number * @__hba: pointer to adapter instance * @@ -7365,7 +7365,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ -static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) +static irqreturn_t ufshcd_intr(int irq, void *__hba) { u32 last_intr_status, intr_status, enabled_intr_status = 0; irqreturn_t retval = IRQ_NONE; @@ -7404,38 +7404,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) return retval; } -/** - * ufshcd_intr - Main interrupt service routine - * @irq: irq number - * @__hba: pointer to adapter instance - * - * Return: - * IRQ_HANDLED - If interrupt is valid - * IRQ_WAKE_THREAD - If handling is moved to threaded handled - * IRQ_NONE - If invalid interrupt - */ -static irqreturn_t ufshcd_intr(int irq, void *__hba) -{ - struct ufs_hba *hba = __hba; - u32 intr_status, enabled_intr_status; - - /* - * Handle interrupt in thread if MCQ or ESI is disabled, - * and no active UIC command. - */ - if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && - !hba->active_uic_cmd) - return IRQ_WAKE_THREAD; - - intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); - - /* Directly handle interrupts since MCQ ESI handlers does the hard job */ - return ufshcd_sl_intr(hba, enabled_intr_status); -} - static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) { int err = 0; @@ -11238,8 +11206,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) ufshcd_readl(hba, REG_INTERRUPT_ENABLE); /* IRQ registration */ - err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr, - IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba); + err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); if (err) { dev_err(hba->dev, "request irq failed\n"); goto out_disable; -- cgit v1.2.3 From b601fa590e667bd9643feed8c869b6b3e418480d Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Mon, 20 Jul 2026 09:27:36 +0200 Subject: scsi: zfcp: Fix memory leak during adapter release by destroying gid_pn_req When releasing an adapter we don't free the mempool 'gid_pn_req' that is allocated during the enqueue. This leaks memory: unreferenced object 0xd8d29297de700 (size 256): comm "(udev-worker)", pid 2105, jiffies 4294945794 hex dump (first 32 bytes): 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... backtrace (crc 4a5b5da2): [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 [<000dc45f643d8238>] online_store+0x298/0x5b0 [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 [<000dc45f64dc2224>] system_call+0x74/0xa0 Fix this by destroying the mempool during the adapter's release. Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") Signed-off-by: Benjamin Block Tested-by: M Nikhil Acked-by: M Nikhil Reviewed-by: Chinmaya Kajagar Reviewed-by: Nihar Panda Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_aux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 8ff7db7921b5..fea573e00f9d 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -253,6 +253,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { mempool_destroy(adapter->pool.erp_req); + mempool_destroy(adapter->pool.gid_pn_req); mempool_destroy(adapter->pool.scsi_req); mempool_destroy(adapter->pool.scsi_abort); mempool_destroy(adapter->pool.qtcb_pool); -- cgit v1.2.3 From a8ddfd2425bbbafadae8700d63ed8a61a4109878 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 22 Jul 2026 09:30:10 +0300 Subject: scsi: target: Clear cmd_cnt when initial counter enrollment fails When target_get_sess_cmd() fails during session shutdown because percpu_ref_tryget_live() returns false, the command keeps the se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier without owning a reference. Final release through target_release_cmd_kref() then issues an unmatched percpu_ref_put(). Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") moved the cmd_cnt assignment ahead of the reference acquisition. Clear se_cmd->cmd_cnt whenever the initial target_get_sess_cmd() fails in target_init_cmd() and target_submit_tmr(), so release performs exactly one matching put per acquired reference. Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") Signed-off-by: Leon Romanovsky Reviewed-by: Mike Christie Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_transport.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index fad03a15c969..dcfe94594916 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1734,6 +1734,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, u32 data_length, int task_attr, int data_dir, int flags) { struct se_portal_group *se_tpg; + int ret; se_tpg = se_sess->se_tpg; BUG_ON(!se_tpg); @@ -1763,7 +1764,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second * kref_put() to happen during fabric packet acknowledgement. */ - return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) + se_cmd->cmd_cnt = NULL; + + return ret; } EXPORT_SYMBOL_GPL(target_init_cmd); @@ -2039,8 +2044,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, * allocation failure. */ ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); - if (ret < 0) + if (ret < 0) { + se_cmd->cmd_cnt = NULL; return -ENOMEM; + } if (tm_type == TMR_ABORT_TASK) se_cmd->se_tmr_req->ref_task_tag = tag; @@ -2048,6 +2055,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, /* See target_submit_cmd for commentary */ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); if (ret) { + se_cmd->cmd_cnt = NULL; core_tmr_release_req(se_cmd->se_tmr_req); return ret; } -- cgit v1.2.3 From 3bd438a58e910db5dc369aa25dfed1fc95f1b596 Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Wed, 22 Jul 2026 13:42:29 +0530 Subject: octeontx2-af: Block VFs from clobbering special CGX PKIND state PF and VF NIX LFs that share a CGX LMAC reuse the same hardware PKIND programming. When HiGig2 or EDSA parsing is enabled, a VF NIX LF alloc must not reset the LMAC RX PKIND or default TX parse config over the PF setup. Add cgx_get_pkind() and rvu_cgx_is_pkind_config_permitted() so VFs skip cgx_set_pkind(), rvu_npc_set_pkind(), and NIX_AF_LFX_TX_PARSE_CFG updates when the LMAC is using NPC_RX_HIGIG_PKIND or NPC_RX_EDSA_PKIND. Fixes: 94d942c5fb97 ("octeontx2-af: Config pkind for CGX mapped PFs") Cc: Geetha sowjanya Signed-off-by: Hariprasad Kelam Signed-off-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260722081229.1653619-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/cgx.c | 12 ++++ drivers/net/ethernet/marvell/octeontx2/af/cgx.h | 1 + drivers/net/ethernet/marvell/octeontx2/af/rvu.h | 2 + .../net/ethernet/marvell/octeontx2/af/rvu_cgx.c | 79 ++++++++++++++++++++++ .../net/ethernet/marvell/octeontx2/af/rvu_nix.c | 22 ++++-- .../net/ethernet/marvell/octeontx2/af/rvu_npc.c | 29 +++++--- 6 files changed, 131 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 2e94d5105016..f5fd6138c352 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -518,6 +518,18 @@ int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind) return 0; } +int cgx_get_pkind(void *cgxd, u8 lmac_id, int *pkind) +{ + struct cgx *cgx = cgxd; + + if (!is_lmac_valid(cgx, lmac_id)) + return -ENODEV; + + *pkind = cgx_read(cgx, lmac_id, cgx->mac_ops->rxid_map_offset); + *pkind = *pkind & 0x3F; + return 0; +} + static u8 cgx_get_lmac_type(void *cgxd, int lmac_id) { struct cgx *cgx = cgxd; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h index 92ccf343dfe0..8411a75dd723 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h @@ -141,6 +141,7 @@ int cgx_get_cgxid(void *cgxd); int cgx_get_lmac_cnt(void *cgxd); void *cgx_get_pdata(int cgx_id); int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind); +int cgx_get_pkind(void *cgxd, u8 lmac_id, int *pkind); int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id); int cgx_lmac_evh_unregister(void *cgxd, int lmac_id); int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 7f3505ae6860..9d5b7b51bdfa 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -1115,6 +1115,8 @@ void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, u8 *intf, u8 *ena); int npc_config_cntr_default_entries(struct rvu *rvu, bool enable); bool is_cgx_config_permitted(struct rvu *rvu, u16 pcifunc); +bool rvu_cgx_check_permission_and_set_pkind(struct rvu *rvu, u16 pcifunc, int pkind); +bool rvu_cgx_is_pkind_config_permitted(struct rvu *rvu, u16 pcifunc); bool is_mac_feature_supported(struct rvu *rvu, int pf, int feature); u32 rvu_cgx_get_fifolen(struct rvu *rvu); void *rvu_first_cgx_pdata(struct rvu *rvu); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index 4ff3935ed3fe..87d21889dc49 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -1355,3 +1355,82 @@ void rvu_mac_reset(struct rvu *rvu, u16 pcifunc) if (mac_ops->mac_reset(cgxd, lmac, !is_vf(pcifunc))) dev_err(rvu->dev, "Failed to reset MAC\n"); } + +/* Do not allow CGX-mapped VFs to overwrite PKIND when special parse kinds + * (HiGig, EDSA, etc.) are in use on the shared LMAC. VFs must not program + * NPC_TX_DEF_PKIND on NIX_AF_LFX_TX_PARSE_CFG in that case: the PF owns + * parse mode and no separate NPC_TX_HIGIG_PKIND is installed on the VF LF. + * TX-parse callers skip the write when denied; rvu_lf_reset() clears each LF + * before alloc so the next permitted owner programs NPC_TX_DEF_PKIND. + */ +bool rvu_cgx_is_pkind_config_permitted(struct rvu *rvu, u16 pcifunc) +{ + int pf, err, rxpkind; + u8 cgx_id, lmac_id; + void *cgxd; + + pf = rvu_get_pf(rvu->pdev, pcifunc); + + if (!(pcifunc & RVU_PFVF_FUNC_MASK)) + return true; + + if (!is_pf_cgxmapped(rvu, pf)) + return true; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + err = cgx_get_pkind(cgxd, lmac_id, &rxpkind); + if (err) + return false; + + switch (rxpkind) { + case NPC_RX_HIGIG_PKIND: + case NPC_RX_EDSA_PKIND: + return false; + default: + return true; + } +} + +/* Do not allow CGX-mapped VFs to overwrite PKIND when special parse kinds + * (HiGig, EDSA, etc.) are in use on the shared LMAC. + */ +bool rvu_cgx_check_permission_and_set_pkind(struct rvu *rvu, u16 pcifunc, int pkind) +{ + int pf, err, rxpkind; + u8 cgx_id, lmac_id; + struct cgx *cgxd; + + pf = rvu_get_pf(rvu->pdev, pcifunc); + + if (!is_pf_cgxmapped(rvu, pf)) + return false; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + + mutex_lock(&cgxd->lock); + if (!is_vf(pcifunc)) + goto set; + + err = cgx_get_pkind(cgxd, lmac_id, &rxpkind); + if (err) + goto err; + + switch (rxpkind) { + case NPC_RX_HIGIG_PKIND: + case NPC_RX_EDSA_PKIND: + goto err; + default: + break; + } + +set: + cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind); + mutex_unlock(&cgxd->lock); + return true; + +err: + mutex_unlock(&cgxd->lock); + return false; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 6a0ce2665031..964bcaae098e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -363,8 +363,8 @@ static int nix_interface_init(struct rvu *rvu, u16 pcifunc, int type, int nixlf, pfvf->tx_chan_cnt = 1; rsp->tx_link = cgx_id * hw->lmac_per_cgx + lmac_id; - cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, pkind); - rvu_npc_set_pkind(rvu, pkind, pfvf); + if (rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, pkind)) + rvu_npc_set_pkind(rvu, pkind, pfvf); break; case NIX_INTF_TYPE_LBK: vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1; @@ -1505,13 +1505,15 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, struct nix_lf_alloc_req *req, struct nix_lf_alloc_rsp *rsp) { - int nixlf, qints, hwctx_size, intf, rc = 0; + int nixlf, qints, hwctx_size, intf, rc = 0, pf; u16 bcast, mcast, promisc, ucast; struct rvu_hwinfo *hw = rvu->hw; u16 pcifunc = req->hdr.pcifunc; + u8 cgx_id = 0, lmac_id = 0; bool rules_created = false; struct rvu_block *block; struct rvu_pfvf *pfvf; + struct cgx *cgxd; u64 cfg, ctx_cfg; int blkaddr; @@ -1685,8 +1687,20 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu, rvu_write64(rvu, blkaddr, NIX_AF_LFX_RX_CFG(nixlf), req->rx_cfg); /* Configure pkind for TX parse config */ + + pf = rvu_get_pf(rvu->pdev, pcifunc); cfg = NPC_TX_DEF_PKIND; - rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg); + + if (is_pf_cgxmapped(rvu, pf) && is_vf(pcifunc)) { + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + mutex_lock(&cgxd->lock); + if (rvu_cgx_is_pkind_config_permitted(rvu, pcifunc)) + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg); + mutex_unlock(&cgxd->lock); + } else { + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), cfg); + } if (is_rep_dev(rvu, pcifunc)) { pfvf->tx_chan_base = RVU_SWITCH_LBK_CHAN; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index c7bc0b3a29b9..38554d51164e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -19,6 +19,7 @@ #include "cn20k/npc.h" #include "rvu_npc.h" #include "cn20k/reg.h" +#include "lmac_common.h" #define RSVD_MCAM_ENTRIES_PER_PF 3 /* Broadcast, Promisc and AllMulticast */ #define RSVD_MCAM_ENTRIES_PER_NIXLF 1 /* Ucast for LFs */ @@ -4200,10 +4201,11 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir, { struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc); - int blkaddr, nixlf, rc, intf_mode; int pf = rvu_get_pf(rvu->pdev, pcifunc); + int blkaddr, nixlf, rc, intf_mode; + u8 cgx_id = 0, lmac_id = 0; u64 rxpkind, txpkind; - u8 cgx_id, lmac_id; + struct cgx *cgxd; /* use default pkind to disable edsa/higig */ rxpkind = rvu_npc_get_pkind(rvu, pf); @@ -4227,12 +4229,8 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir, /* rx pkind set req valid only for cgx mapped PFs */ if (!is_cgx_config_permitted(rvu, pcifunc)) return 0; - rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); - - rc = cgx_set_pkind(rvu_cgx_pdata(cgx_id, rvu), lmac_id, - rxpkind); - if (rc) - return rc; + if (!rvu_cgx_check_permission_and_set_pkind(rvu, pcifunc, rxpkind)) + return -EINVAL; } if (dir & PKIND_TX) { @@ -4241,8 +4239,19 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir, if (rc) return rc; - rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), - txpkind); + if (is_pf_cgxmapped(rvu, pf) && is_vf(pcifunc)) { + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, + &lmac_id); + cgxd = rvu_cgx_pdata(cgx_id, rvu); + mutex_lock(&cgxd->lock); + if (rvu_cgx_is_pkind_config_permitted(rvu, pcifunc)) + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), + txpkind); + mutex_unlock(&cgxd->lock); + } else { + rvu_write64(rvu, blkaddr, NIX_AF_LFX_TX_PARSE_CFG(nixlf), + txpkind); + } } pfvf->intf_mode = intf_mode; -- cgit v1.2.3 From 356d8241cf3c7b07a4a491dbab43b5a41513ca86 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 28 Jul 2026 13:13:11 +0200 Subject: ata: libata-core: Disable LPM on some WD drives According to a user report WDC WD100EFGX-68CPLN0 and WDC WD102KFBX-68M95N0 have problems with LPM. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220693 Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c43bd28b20b1..15781b4f0489 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4413,6 +4413,14 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { { "WDC WD3000JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, { "WDC WD3200JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, + /* + * WD drives with LPM issues (irrespective of supported SATA speeds). + * (Unlike ATA_QUIRK_WD_BROKEN_LPM, which is only applied if the drive + * exposes SATA Gen1 speed support, and SATA Gen1 speed support only.) + */ + { "WDC WD100EFGX-68CPLN0", NULL, ATA_QUIRK_NOLPM }, + { "WDC WD102KFBX-68M95N0", NULL, ATA_QUIRK_NOLPM }, + /* * This sata dom device goes on a walkabout when the ATA_LOG_DIRECTORY * log page is accessed. Ensure we never ask for this log page with -- cgit v1.2.3 From d549df8a0ace611bff0a336a907f93420648b462 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 28 Jul 2026 13:13:12 +0200 Subject: ata: libata-core: Disable LPM on WD Green 2.5 480GB According to a user report, the WD Green 2.5 480GB has problems with LPM. Link: https://lore.kernel.org/linux-ide/CAGiKK17Fg4SgS+y0GzD3new44QzD_yPZU5V3ZeHdUr9mFnfn1Q@mail.gmail.com/ Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 15781b4f0489..961d03fbc4d9 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4420,6 +4420,7 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { */ { "WDC WD100EFGX-68CPLN0", NULL, ATA_QUIRK_NOLPM }, { "WDC WD102KFBX-68M95N0", NULL, ATA_QUIRK_NOLPM }, + { "WD Green 2.5 480GB", NULL, ATA_QUIRK_NOLPM }, /* * This sata dom device goes on a walkabout when the ATA_LOG_DIRECTORY -- cgit v1.2.3 From 3fd70e96914d761c17c376aadd0b0d1a3c9badba Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 28 Jul 2026 13:38:42 +0200 Subject: ata: libata-sata: fix ata_scsi_lpm_supported() iteration The inner loop of ata_scsi_lpm_supported() uses the wrong variable when iterating. It should obviously use the link that we are currently iterating over, rather than always using the host link. ata_scsi_lpm_supported() is used to control if a user should be allowed to change lpm policy (from the default) via sysfs. Thus, this bug could potentially disallow users to change the LPM policy for certain SATA devices via sysfs. Cc: stable@vger.kernel.org Fixes: 0060beec0bfa ("ata: libata-sata: Add link_power_management_supported sysfs attribute") Reported-by: Sashiko Link: https://lore.kernel.org/linux-ide/20260728112200.B99F21F000E9@smtp.kernel.org/ Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-sata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index b0706c30da05..ad40f516c6d4 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -913,7 +913,7 @@ static bool ata_scsi_lpm_supported(struct ata_port *ap) return false; ata_for_each_link(link, ap, EDGE) { - ata_for_each_dev(dev, &ap->link, ENABLED) { + ata_for_each_dev(dev, link, ENABLED) { if (dev->quirks & ATA_QUIRK_NOLPM) return false; } -- cgit v1.2.3 From ccff8c92571500fcfed21281e33daaf645bf692f Mon Sep 17 00:00:00 2001 From: Chandrakanth Patil Date: Fri, 24 Jul 2026 23:22:31 +0530 Subject: scsi: mpi3mr: Fix potential deadlock in mpi3mr_fault_uevent_emit mpi3mr_fault_uevent_emit() runs from the fault watchdog and reset paths where host I/O may already be blocked. GFP_KERNEL allocations here, both the local kzalloc_obj() and the ones inside kobject_uevent_env() itself, can trigger reclaim that waits on that blocked I/O and deadlock. Use memalloc_noio_save()/restore() to cover the whole call instead of just the local allocation. Fixes: ec54b348f274 ("scsi: mpi3mr: Record and report controller firmware faults") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260724164630.924288-1-chandrakanth.patil%40broadcom.com Signed-off-by: Chandrakanth Patil Link: https://patch.msgid.link/20260724175231.935192-1-chandrakanth.patil@broadcom.com Signed-off-by: Martin K. Petersen --- drivers/scsi/mpi3mr/mpi3mr_fw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index 31b19ed1528e..681868716ebd 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -9,6 +9,7 @@ #include "mpi3mr.h" #include +#include static int mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason); @@ -1287,11 +1288,14 @@ out_failed: static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc) { struct kobj_uevent_env *env; + unsigned int noio_flag; int ret; + noio_flag = memalloc_noio_save(); + env = kzalloc_obj(*env); if (!env) - return; + goto out_restore; ret = add_uevent_var(env, "DRIVER=%s", mrioc->driver_name); if (ret) @@ -1326,7 +1330,8 @@ static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc) out_free: kfree(env); - +out_restore: + memalloc_noio_restore(noio_flag); } /** -- cgit v1.2.3 From 0279fd451a9971c0d5b959fc59f3e11b55e1694e Mon Sep 17 00:00:00 2001 From: Ao Sun Date: Thu, 23 Jul 2026 03:45:30 +0000 Subject: scsi: ufs: core: Initialize hba->rpmbs list in ufshcd Initialize the hba->rpmbs list in ufshcd_alloc_host() to prevent NULL pointer dereference in the device teardown path if ufs_rpmb_probe() fails. Fixes: b06b8c421485 ("scsi: ufs: core: Add OP-TEE based RPMB driver for UFS devices") Co-developed-by: Jiazi Li Signed-off-by: Jiazi Li Signed-off-by: Ao Sun Reviewed-by: Bean Huo Link: https://patch.msgid.link/20260723034440.217-1-ao.sun@transsion.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-rpmb.c | 2 -- drivers/ufs/core/ufshcd.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c index ffad049872b9..62120dc2e9da 100644 --- a/drivers/ufs/core/ufs-rpmb.c +++ b/drivers/ufs/core/ufs-rpmb.c @@ -152,8 +152,6 @@ int ufs_rpmb_probe(struct ufs_hba *hba) return -EINVAL; } - INIT_LIST_HEAD(&hba->rpmbs); - struct rpmb_descr descr = { .type = RPMB_TYPE_UFS, .route_frames = ufs_rpmb_route_frames, diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 34228beb3f59..13e6357a8bbb 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -10958,6 +10958,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle) hba->nop_out_timeout = NOP_OUT_TIMEOUT; ufshcd_set_sg_entry_size(hba, sizeof(struct ufshcd_sg_entry)); INIT_LIST_HEAD(&hba->clk_list_head); + INIT_LIST_HEAD(&hba->rpmbs); spin_lock_init(&hba->outstanding_lock); *hba_handle = hba; -- cgit v1.2.3 From cf6c993c0feca7984797e634deba3c80342e199a Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Sat, 25 Jul 2026 16:00:04 +0800 Subject: fscrypt: use the mount idmap for the owner check in fscrypt_ioctl_set_policy() fscrypt_ioctl_set_policy() calls inode_owner_or_capable() with &nop_mnt_idmap before allowing an encryption policy to be set, instead of the idmap of the mount the ioctl was issued on. fscrypt is used by filesystems that support idmapped mounts (e.g. ext4, f2fs), so on such a mount this compares the caller's fsuid against the unmapped on-disk owner rather than the mapped owner: the actual owner can be wrongly denied with -EACCES and an unrelated caller wrongly allowed. Use file_mnt_idmap(filp) instead. Fixes: 14f3db5542e6 ("ext4: support idmapped mounts") Cc: stable@vger.kernel.org Signed-off-by: Zhan Xusheng Link: https://patch.msgid.link/20260725080004.929328-1-zhanxusheng1024@gmail.com Signed-off-by: Eric Biggers --- fs/crypto/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 9915e39362db..c80b24a941ad 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -534,7 +534,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) return -EFAULT; policy.version = version; - if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + if (!inode_owner_or_capable(file_mnt_idmap(filp), inode)) return -EACCES; ret = mnt_want_write_file(filp); -- cgit v1.2.3 From d2f96bcb89d36d488a10e3bcf819b98536968286 Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Mon, 27 Jul 2026 11:43:52 +0200 Subject: fs,fsverity: remove check for fsverity being enabled in setattr_prepare() The check that fs-verity is available in the kernel is not necessary here. Filesystems could have fsverity files even without fs-verity enabled. In that case, truncate on fsverity file will succeed, what this check is trying to prevent. Fixes: e9734653c523 ("fs,fsverity: reject size changes on fsverity files in setattr_prepare") Cc: stable@vger.kernel.org Signed-off-by: Andrey Albershteyn Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260727094352.1734826-1-aalbersh@kernel.org Signed-off-by: Eric Biggers --- fs/attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/attr.c b/fs/attr.c index 4f437fabb7f0..71888ac903c2 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -176,7 +176,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry, * covered by the open-time check because sys_truncate() takes a * path, not an open file. */ - if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode)) + if (IS_VERITY(inode)) return -EPERM; error = inode_newsize_ok(inode, attr->ia_size); -- cgit v1.2.3 From d0199ae1666ff9ae2d1d568d64c3430d4c47f0e5 Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Tue, 28 Jul 2026 20:17:16 +0000 Subject: ALSA: usb-audio: Fix DMA buffer out-of-bounds write when fill_max is set When a USB audio endpoint requests full packet transfers via the fill_max descriptor flag, data_ep_set_params() promotes ep->curpacksize to ep->maxpacksize. However, maxsize is left at the original sample-rate derived value. Since u->buffer_size is allocated as maxsize * packets, the resulting DMA buffer is far too small for the requested transfer length. When the USB host controller streams up to curpacksize bytes per packet, it writes past the end of the buffer via DMA, corrupting kernel heap memory. Update maxsize to curpacksize when fill_max is set so that the allocated DMA buffer size matches the actual transfer request size. [ changed to reassign maxsize only when ep->fill_max is set -- tiwai ] Fixes: 8fdff6a319e7 ("ALSA: snd-usb: implement new endpoint streaming model") Cc: stable@vger.kernel.org Assisted-by: Jetski:Gemini-3.6-Flash Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260728201716.2347726-1-sonalipradhan@google.com Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 24cd7692bd01..91611d0a4e48 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1168,10 +1168,12 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) << (16 - ep->datainterval); } - if (ep->fill_max) + if (ep->fill_max) { ep->curpacksize = ep->maxpacksize; - else + maxsize = ep->curpacksize; + } else { ep->curpacksize = maxsize; + } if (snd_usb_get_speed(chip->dev) != USB_SPEED_FULL) { packs_per_ms = 8 >> ep->datainterval; -- cgit v1.2.3 From 8d7a30c50c2e58a6839634ed0acde14466d1dc61 Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Tue, 28 Jul 2026 20:24:32 +0000 Subject: ALSA: usb-audio: Clamp frame size in implicit-feedback mode snd_usb_handle_sync_urb() scales received sync packet sizes by the sender's stride and stores the result directly in out_packet->packet_size[i]. If a connected USB device sends an oversized sync packet, this frame count can exceed ep->maxframesize. The un-clamped frame count then propagates to the playback endpoint queue, potentially driving packet transfers beyond the endpoint's hardware frame limits. Cap the calculated frame count against ep->maxframesize in snd_usb_handle_sync_urb() to prevent oversized packets from entering the playback queue. Fixes: 28acb12014fb ("ALSA: usb-audio: use sender stride for implicit feedback") Cc: stable@vger.kernel.org Assisted-by: Jetski:Gemini-3.6-Flash Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260728202432.2354994-1-sonalipradhan@google.com Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 91611d0a4e48..dca06ba1c67e 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1819,11 +1819,13 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, out_packet->packets = in_ctx->packets; for (i = 0; i < in_ctx->packets; i++) { - if (urb->iso_frame_desc[i].status == 0) - out_packet->packet_size[i] = + if (urb->iso_frame_desc[i].status == 0) { + unsigned int frames = urb->iso_frame_desc[i].actual_length / sender->stride; - else + out_packet->packet_size[i] = min(frames, ep->maxframesize); + } else { out_packet->packet_size[i] = 0; + } } spin_unlock_irqrestore(&ep->lock, flags); -- cgit v1.2.3 From e93bb6f3acec5b70a5567efe78f1a96148a7291f Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Wed, 29 Jul 2026 03:22:39 +0500 Subject: ALSA: usb-audio: Add GET_SAMPLE_RATE quirk for C-Media CM6206 The C-Media CM6206 (0d8c:0102) truncates the three-byte sample rate it returns for UAC_GET_CUR to its two low bytes. After the rate has been set to 96000 (0x017700) the device reports back 30464 (0x007700). At probe time the driver initializes every altsetting to its maximum rate, so altsetting 5 is set to 96000 and the warning appears on each plug-in, before anything has opened the device: usb 3-1.3: 1:5 Set sample rate 96000, clock 0 usb 3-1.3: current rate 30464 is different from the runtime rate 96000 That altsetting is the one parse_audio_format_rates_v1() already fixes up for this chip, so this affects every CM6206. Only the read-back is broken, the rate itself is applied: a 1 kHz sine rendered at 96 kHz is recovered at 1000.2 Hz, and a silent fallback to 48000 would have been reported as 0x00bb80 rather than as the low half of the requested rate. Add a QUIRK_FLAG_GET_SAMPLE_RATE entry for the device so the read-back is skipped. Setting the same flag through the quirk_flags module parameter makes the warning disappear while the 96000 init still happens. Signed-off-by: Mikhail Gavrilov Link: https://patch.msgid.link/20260728222239.62749-1-mikhail.v.gavrilov@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 40aa40fccb46..d2a111ee50ab 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2347,6 +2347,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0d8c, 0x0014, /* C-Media */ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x0d8c, 0x0102, /* C-Media CM6206 */ + QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ -- cgit v1.2.3 From 630c45e92db44b738d1e9224c5f3c032c76a33ca Mon Sep 17 00:00:00 2001 From: André Pragosa Date: Tue, 28 Jul 2026 23:11:25 +0100 Subject: ALSA: hda/realtek: Add mute LED quirk for HP Victus 16-e0xxx (MB 88ED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add subsystem ID 103c:88ed to the existing HP Victus 16-e0xxx mute LED quirk list. The HP Victus 16-e0xxx with subsystem ID 103c:88ed uses the same mute LED coefficient configuration as the already supported 103c:88eb variant. The mute LED was verified by manually toggling coefficient index 0x0b (bit 3) using hda-verb. After adding the quirk, the LED is registered as hda::mute and follows the audio mute state. Signed-off-by: André Pragosa Link: https://patch.msgid.link/20260728221129.14680-2-pragosa512@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index f620cd429f20..48a2e7f2ebb1 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7222,6 +7222,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x88d1, "HP Pavilion 15-eh1xxx (mainboard 88D1)", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x88eb, "HP Victus 16-e0xxx", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x88ed, "HP Victus 16-e0xxx (MB 88ED)", + ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), SND_PCI_QUIRK(0x103c, 0x88ee, "HP Victus 16-e0xxx (MB 88EE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), -- cgit v1.2.3 From d2fb981384b3a45f690616d550b29046e8ad16a4 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 28 Jul 2026 07:58:34 +0200 Subject: can: j1939: use netdevice_tracker for j1939_{priv,session,ecu} tracking syzbot is still reporting unregister_netdevice: waiting for vcan0 to become free. Usage count = 2 problem. A debug printk() patch in linux-next-20260508 identified that there is dev_hold()/dev_put() imbalance in j1939_priv management. Call trace for vcan0[26] +4 at __dev_hold include/linux/netdevice.h:4470 [inline] netdev_hold include/linux/netdevice.h:4513 [inline] dev_hold include/linux/netdevice.h:4536 [inline] j1939_priv_create net/can/j1939/main.c:140 [inline] j1939_netdev_start+0x36b/0xc10 net/can/j1939/main.c:268 j1939_sk_bind+0x853/0xb30 net/can/j1939/socket.c:506 __sys_bind_socket net/socket.c:1948 [inline] __sys_bind+0x2e9/0x410 net/socket.c:1979 Call trace for vcan0[28] -3 at __dev_put include/linux/netdevice.h:4456 [inline] netdev_put include/linux/netdevice.h:4523 [inline] dev_put include/linux/netdevice.h:4548 [inline] __j1939_priv_release net/can/j1939/main.c:166 [inline] kref_put include/linux/kref.h:65 [inline] j1939_priv_put+0x128/0x270 net/can/j1939/main.c:172 j1939_sk_sock_destruct+0x52/0x90 net/can/j1939/socket.c:388 __sk_destruct+0x8d/0x9d0 net/core/sock.c:2352 rcu_do_batch kernel/rcu/tree.c:2617 [inline] rcu_core kernel/rcu/tree.c:2869 [inline] rcu_cpu_kthread+0x99e/0x1470 kernel/rcu/tree.c:2957 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 This refcount leak in j1939_priv might be caused by a refcount leak in j1939_{session,ecu} because j1939_{session,ecu} holds a ref on j1939_priv. For further investigation using upstream kernels, enable netdevice_tracker in j1939_{priv,session,ecu} management. Signed-off-by: Tetsuo Handa Acked-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Link: https://patch.msgid.link/20260728055835.1151785-2-o.rempel@pengutronix.de Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/j1939/bus.c | 2 ++ net/can/j1939/j1939-priv.h | 3 +++ net/can/j1939/main.c | 8 ++++---- net/can/j1939/transport.c | 2 ++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c index dc374286eeb6..cdc3c0a71937 100644 --- a/net/can/j1939/bus.c +++ b/net/can/j1939/bus.c @@ -20,6 +20,7 @@ static void __j1939_ecu_release(struct kref *kref) struct j1939_priv *priv = ecu->priv; list_del(&ecu->list); + netdev_put(priv->ndev, &ecu->priv_dev_tracker); kfree(ecu); j1939_priv_put(priv); } @@ -155,6 +156,7 @@ struct j1939_ecu *j1939_ecu_create_locked(struct j1939_priv *priv, name_t name) if (!ecu) return ERR_PTR(-ENOMEM); kref_init(&ecu->kref); + netdev_hold(priv->ndev, &ecu->priv_dev_tracker, gfp_any()); ecu->addr = J1939_IDLE_ADDR; ecu->name = name; diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h index 81f58924b4ac..cf26352d1d8c 100644 --- a/net/can/j1939/j1939-priv.h +++ b/net/can/j1939/j1939-priv.h @@ -38,6 +38,7 @@ struct j1939_ecu { struct hrtimer ac_timer; struct kref kref; struct j1939_priv *priv; + netdevice_tracker priv_dev_tracker; /* count users, to help transport protocol decide for interaction */ int nusers; @@ -60,6 +61,7 @@ struct j1939_priv { rwlock_t lock; struct net_device *ndev; + netdevice_tracker dev_tracker; /* list of 256 ecu ptrs, that cache the claimed addresses. * also protected by the above lock @@ -230,6 +232,7 @@ enum j1939_session_state { struct j1939_session { struct j1939_priv *priv; + netdevice_tracker priv_dev_tracker; struct list_head active_session_list_entry; struct list_head sk_session_queue_entry; struct kref kref; diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c index 9937c04241bc..5e5e6c228f22 100644 --- a/net/can/j1939/main.c +++ b/net/can/j1939/main.c @@ -137,7 +137,7 @@ static struct j1939_priv *j1939_priv_create(struct net_device *ndev) priv->ndev = ndev; kref_init(&priv->kref); kref_init(&priv->rx_kref); - dev_hold(ndev); + netdev_hold(ndev, &priv->dev_tracker, GFP_KERNEL); netdev_dbg(priv->ndev, "%s : 0x%p\n", __func__, priv); @@ -163,7 +163,7 @@ static void __j1939_priv_release(struct kref *kref) WARN_ON_ONCE(!list_empty(&priv->ecus)); WARN_ON_ONCE(!list_empty(&priv->j1939_socks)); - dev_put(ndev); + netdev_put(ndev, &priv->dev_tracker); kfree(priv); } @@ -281,7 +281,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) */ kref_get(&priv_new->rx_kref); mutex_unlock(&j1939_netdev_lock); - dev_put(ndev); + netdev_put(ndev, &priv->dev_tracker); kfree(priv); return priv_new; } @@ -298,7 +298,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev) j1939_priv_set(ndev, NULL); mutex_unlock(&j1939_netdev_lock); - dev_put(ndev); + netdev_put(ndev, &priv->dev_tracker); kfree(priv); return ERR_PTR(ret); diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 8a31cb23bc76..98f96362b20f 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -283,6 +283,7 @@ static void j1939_session_destroy(struct j1939_session *session) kfree_skb(skb); } __j1939_session_drop(session); + netdev_put(session->priv->ndev, &session->priv_dev_tracker); j1939_priv_put(session->priv); kfree(session); } @@ -1526,6 +1527,7 @@ static struct j1939_session *j1939_session_new(struct j1939_priv *priv, INIT_LIST_HEAD(&session->active_session_list_entry); INIT_LIST_HEAD(&session->sk_session_queue_entry); kref_init(&session->kref); + netdev_hold(priv->ndev, &session->priv_dev_tracker, gfp_any()); j1939_priv_get(priv); session->priv = priv; -- cgit v1.2.3 From eb96c58907922546e415e545fe9a14ea63b02719 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 28 Jul 2026 07:58:35 +0200 Subject: can: j1939: transport: j1939_session_fresh_new(): initialize receive buffer Zero the allocated buffer in j1939_session_fresh_new() to ensure it contains no residual data. While there is a potential performance impact if users allocate maximum sized ETP buffers, most real-world use cases are not noticeably affected since the maximum known buffer size is typically around 65K. Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol") Reported-by: Ji'an Zhou Message-ID: Signed-off-by: Oleksij Rempel Link: https://patch.msgid.link/20260728055835.1151785-3-o.rempel@pengutronix.de Cc: stable@kernel.org [mkl: add Message-ID] Signed-off-by: Marc Kleine-Budde --- net/can/j1939/transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 98f96362b20f..8fcfd13e5e6f 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -1581,7 +1581,7 @@ j1939_session *j1939_session_fresh_new(struct j1939_priv *priv, } /* alloc data area */ - skb_put(skb, size); + skb_put_zero(skb, size); /* skb is recounted in j1939_session_new() */ return session; } -- cgit v1.2.3 From 050f010f920da17c1044a4f174766ad553e770b6 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Fri, 24 Jul 2026 20:15:25 +0200 Subject: can: isotp: fix timer drain order, wakeup handling and tx_gen ordering This patch is a follow-up to commit cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock") which addresses following sashiko-bot findings: - isotp_sendmsg(): drain so->txfrtimer first so a stale callback can't re-arm echotimer after the claim - isotp_release(): wake so->wait after forcing ISOTP_SHUTDOWN so a sleeping sendmsg() claim isn't stranded - isotp_sendmsg(): have both wait_event_interruptible() calls in isotp_sendmsg() also wake on ISOTP_SHUTDOWN and do not return claim to IDLE to avoid corrupting a concurrent isotp_release() process. - isotp_sendmsg(): handle potential claim of a new transfer when the wait_event_interruptible() call returns in CAN_ISOTP_WAIT_TX_DONE mode. Don't touch timers and states of the new transfer if a new thread incremented so->tx_gen before getting the lock at err_event_drop. - isotp_sendmsg(): handle a stuck can_send() and omit timer and state changes if a new transfer was claimed. wait_tx_done() returns the error recorded in so->tx_result[], tagged with the caller's own generation. - isotp_tx_timeout(): on a claimed timeout, record the ECOMM error for the timed-out transfer's own generation in so->tx_result[]; sk->sk_err is raised unconditionally, same as every other error path here. - isotp_tx_gen_done()/isotp_tx_timeout(): always read tx.state (acquire) before tx_gen - the reverse order let a weakly ordered CPU pair a fresh tx.state with a stale tx_gen/tx_result slot. - isotp_sendmsg(): wait_tx_done: drain sk_err via sock_error() once we have read the result from so->tx_result[], so an already-reported error doesn't stay latched for a later poll()/SO_ERROR. Also align the remaining lock-free so->tx.state/rx.state/cfecho accesses and use skb->hash as unique loopback echo frame indicator. Fixes: cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock") Signed-off-by: Oliver Hartkopp Link: https://patch.msgid.link/20260724181525.43556-1-socketcan@hartkopp.net Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- net/can/isotp.c | 317 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 230 insertions(+), 87 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 54becaf6898f..1f11c66b343c 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -127,6 +127,15 @@ MODULE_PARM_DESC(max_pdu_size, "maximum isotp pdu size (default " #define ISOTP_FC_TIMEOUT 1 /* 1 sec */ #define ISOTP_ECHO_TIMEOUT 2 /* 2 secs */ +/* so->tx_result[so->tx_gen % ISOTP_TX_RESULT_SLOTS] holds the packed value + * (err << ISOTP_TX_RESULT_GEN_BITS | gen) for each tx generation slot, so it + * can be handled with a single READ_ONCE()/WRITE_ONCE() access. + */ +#define ISOTP_TX_RESULT_SLOTS 4 +#define ISOTP_TX_RESULT_GEN_BITS 24 +#define ISOTP_TX_RESULT_GEN_MASK ((1U << ISOTP_TX_RESULT_GEN_BITS) - 1) +#define ISOTP_TX_RESULT_ERR_MASK 0xFF + enum { ISOTP_IDLE = 0, ISOTP_WAIT_FIRST_FC, @@ -166,7 +175,8 @@ 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 */ + u32 tx_gen; /* transfer generation, increased per new tx transfer */ + u32 tx_result[ISOTP_TX_RESULT_SLOTS]; /* per-generation result slots */ struct tpcon rx, tx; struct list_head notifier; wait_queue_head_t wait; @@ -177,6 +187,65 @@ static LIST_HEAD(isotp_notifier_list); static DEFINE_SPINLOCK(isotp_notifier_lock); static struct isotp_sock *isotp_busy_notifier; +/* increase (24 bit) tx generation value */ +static u32 isotp_inc_tx_gen(u32 gen) +{ + return (gen + 1) & ISOTP_TX_RESULT_GEN_MASK; +} + +/* store 8 bit error and 24 bit tx generation values in packed u32 element */ +static u32 isotp_pack_tx_result(u32 gen, int err) +{ + return gen | ((u32)err << ISOTP_TX_RESULT_GEN_BITS); +} + +/* get the 24 bit tx generation value from the tx result */ +static u32 isotp_get_tx_gen(u32 gen_err) +{ + return gen_err & ISOTP_TX_RESULT_GEN_MASK; +} + +/* get the 8 bit error value from the tx result */ +static u32 isotp_get_tx_err(u32 gen_err) +{ + return (gen_err >> ISOTP_TX_RESULT_GEN_BITS) & ISOTP_TX_RESULT_ERR_MASK; +} + +/* store transfer result in per-generation%4 so->tx_result[] slot */ +static void isotp_set_tx_result(struct isotp_sock *so, u32 gen, int err) +{ + WRITE_ONCE(so->tx_result[gen % ISOTP_TX_RESULT_SLOTS], + isotp_pack_tx_result(gen, err)); +} + +/* fetch the result recorded for 'gen', as a (negative) errno (0 for success) */ +static int isotp_get_tx_result(struct isotp_sock *so, u32 gen) +{ + u32 result = READ_ONCE(so->tx_result[gen % ISOTP_TX_RESULT_SLOTS]); + + if (isotp_get_tx_gen(result) != gen) { + pr_notice_once("can-isotp: tx_result[] slot reused before read\n"); + + /* report failure rather than risk a false success */ + return -ECOMM; + } + + return -(isotp_get_tx_err(result)); +} + +/* true if done, shut down or superseded ('gen' is no longer the active + * transfer). Reads tx.state first (acquire) so tx_gen/tx_result reads + * below see at least what that state write published (common sequence). + */ +static bool isotp_tx_gen_done(struct isotp_sock *so, u32 gen) +{ + /* read tx.state first for the common sequence */ + u32 state = smp_load_acquire(&so->tx.state); + + return state == ISOTP_IDLE || state == ISOTP_SHUTDOWN || + READ_ONCE(so->tx_gen) != gen; +} + static inline struct isotp_sock *isotp_sk(const struct sock *sk) { return (struct isotp_sock *)sk; @@ -199,7 +268,7 @@ static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer) rxtimer); struct sock *sk = &so->sk; - if (so->rx.state == ISOTP_WAIT_DATA) { + if (READ_ONCE(so->rx.state) == ISOTP_WAIT_DATA) { /* we did not get new data frames in time */ /* report 'connection timed out' */ @@ -208,7 +277,7 @@ static enum hrtimer_restart isotp_rx_timer_handler(struct hrtimer *hrtimer) sk_error_report(sk); /* reset rx state */ - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); } return HRTIMER_NORESTART; @@ -372,20 +441,19 @@ static void isotp_send_cframe(struct isotp_sock *so); static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) { struct sock *sk = &so->sk; + int tx_err = EBADMSG; /* default for unknown FC status */ - if (so->tx.state != ISOTP_WAIT_FC && - so->tx.state != ISOTP_WAIT_FIRST_FC) + if (READ_ONCE(so->tx.state) != ISOTP_WAIT_FC && + READ_ONCE(so->tx.state) != ISOTP_WAIT_FIRST_FC) return 0; 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. + * hrtimer_cancel() above waited for it to finish => recheck */ - if (so->tx.state != ISOTP_WAIT_FC && - so->tx.state != ISOTP_WAIT_FIRST_FC) + if (READ_ONCE(so->tx.state) != ISOTP_WAIT_FC && + READ_ONCE(so->tx.state) != ISOTP_WAIT_FIRST_FC) return 1; if ((cf->len < ae + FC_CONTENT_SZ) || @@ -396,13 +464,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) if (!sock_flag(sk, SOCK_DEAD)) sk_error_report(sk); - so->tx.state = ISOTP_IDLE; + isotp_set_tx_result(so, so->tx_gen, EBADMSG); + /* set to IDLE after publishing tx_result */ + smp_store_release(&so->tx.state, ISOTP_IDLE); wake_up_interruptible(&so->wait); return 1; } /* get static/dynamic communication params from first/every FC frame */ - if (so->tx.state == ISOTP_WAIT_FIRST_FC || + if (READ_ONCE(so->tx.state) == ISOTP_WAIT_FIRST_FC || so->opt.flags & CAN_ISOTP_DYN_FC_PARMS) { so->txfc.bs = cf->data[ae + 1]; so->txfc.stmin = cf->data[ae + 2]; @@ -426,13 +496,13 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) so->tx_gap = ktime_add_ns(so->tx_gap, (so->txfc.stmin - 0xF0) * 100000); - so->tx.state = ISOTP_WAIT_FC; + WRITE_ONCE(so->tx.state, ISOTP_WAIT_FC); } switch (cf->data[ae] & 0x0F) { case ISOTP_FC_CTS: so->tx.bs = 0; - so->tx.state = ISOTP_SENDING; + WRITE_ONCE(so->tx.state, ISOTP_SENDING); /* send CF frame and enable echo timeout handling */ hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); @@ -447,14 +517,19 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) case ISOTP_FC_OVFLW: /* overflow on receiver side - report 'message too long' */ - sk->sk_err = EMSGSIZE; - if (!sock_flag(sk, SOCK_DEAD)) - sk_error_report(sk); + tx_err = EMSGSIZE; fallthrough; default: - /* stop this tx job */ - so->tx.state = ISOTP_IDLE; + /* reserved/unknown flow status (tx_err defaults to EBADMSG) */ + + sk->sk_err = tx_err; + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); + + isotp_set_tx_result(so, so->tx_gen, tx_err); + /* set to IDLE after publishing tx_result */ + smp_store_release(&so->tx.state, ISOTP_IDLE); wake_up_interruptible(&so->wait); } return 0; @@ -467,7 +542,7 @@ static int isotp_rcv_sf(struct sock *sk, struct canfd_frame *cf, int pcilen, struct sk_buff *nskb; hrtimer_cancel(&so->rxtimer); - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); if (!len || len > cf->len - pcilen) return 1; @@ -501,7 +576,7 @@ static int isotp_rcv_ff(struct sock *sk, struct canfd_frame *cf, int ae) int ff_pci_sz; hrtimer_cancel(&so->rxtimer); - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); /* get the used sender LL_DL from the (first) CAN frame data length */ so->rx.ll_dl = padlen(cf->len); @@ -555,7 +630,7 @@ static int isotp_rcv_ff(struct sock *sk, struct canfd_frame *cf, int ae) /* initial setup for this pdu reception */ so->rx.sn = 1; - so->rx.state = ISOTP_WAIT_DATA; + WRITE_ONCE(so->rx.state, ISOTP_WAIT_DATA); /* no creation of flow control frames */ if (so->opt.flags & CAN_ISOTP_LISTEN_MODE) @@ -573,7 +648,7 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, struct sk_buff *nskb; int i; - if (so->rx.state != ISOTP_WAIT_DATA) + if (READ_ONCE(so->rx.state) != ISOTP_WAIT_DATA) return 0; /* drop if timestamp gap is less than force_rx_stmin nano secs */ @@ -588,11 +663,9 @@ 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. + * while hrtimer_cancel() above waited for it to finish => recheck */ - if (so->rx.state != ISOTP_WAIT_DATA) + if (READ_ONCE(so->rx.state) != ISOTP_WAIT_DATA) return 1; /* CFs are never longer than the FF */ @@ -613,7 +686,7 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, sk_error_report(sk); /* reset rx state */ - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); return 1; } so->rx.sn++; @@ -627,7 +700,7 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, if (so->rx.idx >= so->rx.len) { /* we are done */ - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); if ((so->opt.flags & ISOTP_CHECK_PADDING) && check_pad(so, cf, i + 1, so->opt.rxpad_content)) { @@ -698,8 +771,10 @@ static void isotp_rcv(struct sk_buff *skb, void *data) if (so->opt.flags & CAN_ISOTP_HALF_DUPLEX) { /* check rx/tx path half duplex expectations */ - if ((so->tx.state != ISOTP_IDLE && n_pci_type != N_PCI_FC) || - (so->rx.state != ISOTP_IDLE && n_pci_type == N_PCI_FC)) + if ((READ_ONCE(so->tx.state) != ISOTP_IDLE && + n_pci_type != N_PCI_FC) || + (READ_ONCE(so->rx.state) != ISOTP_IDLE && + n_pci_type == N_PCI_FC)) goto out_unlock; } @@ -794,6 +869,7 @@ static void isotp_send_cframe(struct isotp_sock *so) struct canfd_frame *cf; int can_send_ret; int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; + u32 old_cfecho; dev = dev_get_by_index(sock_net(sk), so->ifindex); if (!dev) @@ -814,6 +890,9 @@ static void isotp_send_cframe(struct isotp_sock *so) csx->can_iif = dev->ifindex; + /* set uid in tx skb to identify CF echo frames */ + can_set_skb_uid(skb); + cf = (struct canfd_frame *)skb->data; skb_put_zero(skb, so->ll.mtu); @@ -830,12 +909,15 @@ static void isotp_send_cframe(struct isotp_sock *so) skb->dev = dev; can_skb_set_owner(skb, sk); - /* cfecho should have been zero'ed by init/isotp_rcv_echo() */ - if (so->cfecho) - pr_notice_once("can-isotp: cfecho is %08X != 0\n", so->cfecho); + /* zero'ed by init/isotp_rcv_echo(); reached lock-free via + * isotp_txfr_timer_handler() too, so use READ_ONCE()/WRITE_ONCE() + */ + old_cfecho = READ_ONCE(so->cfecho); + if (old_cfecho) + pr_notice_once("can-isotp: cfecho is %08X != 0\n", old_cfecho); /* set consecutive frame echo tag */ - so->cfecho = *(u32 *)cf->data; + WRITE_ONCE(so->cfecho, skb->hash); /* send frame with local echo enabled */ can_send_ret = can_send(skb, 1); @@ -887,7 +969,6 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) { struct sock *sk = (struct sock *)data; struct isotp_sock *so = isotp_sk(sk); - 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) @@ -899,32 +980,35 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) spin_lock(&so->rx_lock); /* so->cfecho may since belong to a new transfer; recheck under lock */ - if (so->cfecho != *(u32 *)cf->data) + if (READ_ONCE(so->cfecho) != skb->hash) goto out_unlock; /* cancel local echo timeout */ hrtimer_cancel(&so->echotimer); /* local echo skb with consecutive frame has been consumed */ - so->cfecho = 0; + WRITE_ONCE(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) + if (READ_ONCE(so->tx.state) != ISOTP_SENDING) goto out_unlock; if (so->tx.idx >= so->tx.len) { /* we are done */ - so->tx.state = ISOTP_IDLE; + + isotp_set_tx_result(so, so->tx_gen, 0); + /* set to IDLE after publishing tx_result */ + smp_store_release(&so->tx.state, ISOTP_IDLE); wake_up_interruptible(&so->wait); goto out_unlock; } if (so->txfc.bs && so->tx.bs >= so->txfc.bs) { /* stop and wait for FC with timeout */ - so->tx.state = ISOTP_WAIT_FC; + WRITE_ONCE(so->tx.state, ISOTP_WAIT_FC); hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0), HRTIMER_MODE_REL_SOFT); goto out_unlock; @@ -946,16 +1030,20 @@ out_unlock: spin_unlock(&so->rx_lock); } -/* 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. +/* isotp_tx_timeout: we did not get any flow control or echo frame in time + * + * 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. + * + * tx.state is acquired before tx_gen. Common sequence in isotp_tx_gen_done(). + * cmpxchg() only orders itself, not the two preceding loads. */ static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so) { struct sock *sk = &so->sk; + /* read tx.state first for the common sequence */ + u32 old_state = smp_load_acquire(&so->tx.state); 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 (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN) @@ -965,14 +1053,14 @@ static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so) 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 */ + /* detected timeout: report 'communication error on send' */ - 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); - } + /* a stale read of this slot by a waiter still falls back to ECOMM */ + isotp_set_tx_result(so, gen, ECOMM); + + sk->sk_err = ECOMM; + if (!sock_flag(sk, SOCK_DEAD)) + sk_error_report(sk); wake_up_interruptible(&so->wait); @@ -1007,7 +1095,7 @@ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer) HRTIMER_MODE_REL_SOFT); /* cfecho should be consumed by isotp_rcv_echo() here */ - if (so->tx.state == ISOTP_SENDING && !so->cfecho) + if (READ_ONCE(so->tx.state) == ISOTP_SENDING && !READ_ONCE(so->cfecho)) isotp_send_cframe(so); return HRTIMER_NORESTART; @@ -1026,10 +1114,12 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT; struct hrtimer *tx_hrt = &so->echotimer; u32 new_state = ISOTP_SENDING; + u32 my_gen; + u32 old_cfecho; int off; int err; - if (!so->bound || so->tx.state == ISOTP_SHUTDOWN) + if (!so->bound || READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) return -EADDRNOTAVAIL; /* claim the socket under so->rx_lock: this serializes the claim @@ -1046,29 +1136,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if (msg->msg_flags & MSG_DONTWAIT) return -EAGAIN; - if (so->tx.state == ISOTP_SHUTDOWN) + if (READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN) return -EADDRNOTAVAIL; /* wait for complete transmission of current pdu */ err = wait_event_interruptible(so->wait, - so->tx.state == ISOTP_IDLE); + READ_ONCE(so->tx.state) == ISOTP_IDLE || + READ_ONCE(so->tx.state) == ISOTP_SHUTDOWN); if (err) 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); + /* txfrtimer's callback re-arms echotimer lock-free: drain it first */ + hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->echotimer); - hrtimer_cancel(&so->txfrtimer); - so->cfecho = 0; + + /* new transfer: increment so->tx_gen and set tx.state after barrier */ + my_gen = isotp_inc_tx_gen(READ_ONCE(so->tx_gen)); + isotp_set_tx_result(so, my_gen, ECOMM); /* prevent stale slot matching */ + WRITE_ONCE(so->tx_gen, my_gen); + smp_wmb(); /* see smp_load_acquire() in isotp_tx_[timeout|gen_done] */ + WRITE_ONCE(so->tx.state, ISOTP_SENDING); + WRITE_ONCE(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 + * unbound/rebound the socket meanwhile => recheck */ if (!so->bound) { err = -EADDRNOTAVAIL; @@ -1127,6 +1221,9 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) csx->can_iif = dev->ifindex; + /* set uid in tx skb to identify CF echo frames */ + can_set_skb_uid(skb); + so->tx.len = size; so->tx.idx = 0; @@ -1134,8 +1231,9 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) skb_put_zero(skb, so->ll.mtu); /* cfecho should have been zero'ed by init / former isotp_rcv_echo() */ - if (so->cfecho) - pr_notice_once("can-isotp: uninit cfecho %08X\n", so->cfecho); + old_cfecho = READ_ONCE(so->cfecho); + if (old_cfecho) + pr_notice_once("can-isotp: uninit cfecho %08X\n", old_cfecho); /* check for single frame transmission depending on TX_DL */ if (size <= so->tx.ll_dl - SF_PCI_SZ4 - ae - off) { @@ -1163,7 +1261,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) cf->data[ae] |= size; /* set CF echo tag for isotp_rcv_echo() (SF-mode) */ - so->cfecho = *(u32 *)cf->data; + WRITE_ONCE(so->cfecho, skb->hash); } else { /* send first frame */ @@ -1180,7 +1278,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) so->txfc.bs = 0; /* set CF echo tag for isotp_rcv_echo() (CF-mode) */ - so->cfecho = *(u32 *)cf->data; + WRITE_ONCE(so->cfecho, skb->hash); } else { /* standard flow control check */ new_state = ISOTP_WAIT_FIRST_FC; @@ -1190,12 +1288,12 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) tx_hrt = &so->txtimer; /* no CF echo tag for isotp_rcv_echo() (FF-mode) */ - so->cfecho = 0; + WRITE_ONCE(so->cfecho, 0); } } spin_lock_bh(&so->rx_lock); - if (so->tx.state == ISOTP_SHUTDOWN) { + if (READ_ONCE(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 */ @@ -1206,7 +1304,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return -EADDRNOTAVAIL; } /* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */ - so->tx.state = new_state; + WRITE_ONCE(so->tx.state, new_state); hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0), HRTIMER_MODE_REL_SOFT); spin_unlock_bh(&so->rx_lock); @@ -1223,20 +1321,49 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) __func__, ERR_PTR(err)); spin_lock_bh(&so->rx_lock); + + /* new transfer already claimed by a concurrent completion, + * timeout or sendmsg() while we were stuck in can_send()? + */ + if (READ_ONCE(so->tx_gen) != my_gen) { + /* don't touch timers and state of the new transfer */ + spin_unlock_bh(&so->rx_lock); + return err; + } + /* no transmission -> no timeout monitoring */ hrtimer_cancel(tx_hrt); goto err_out_drop_locked; } if (wait_tx_done) { - /* wait for complete transmission of current pdu */ - err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); + /* wake up for: + * - concurrent sendmsg() claiming a new transfer + * - complete transmission of current PDU + * - shutdown state change in isotp_release() + * isotp_tx_gen_done() uses common tx.state/tx_gen read sequence + */ + err = wait_event_interruptible(so->wait, + isotp_tx_gen_done(so, my_gen)); if (err) goto err_event_drop; - err = sock_error(sk); - if (err) - return err; + /* still our claim, but isotp_release() force-shut it down */ + if (smp_load_acquire(&so->tx.state) == ISOTP_SHUTDOWN && + READ_ONCE(so->tx_gen) == my_gen) { + err = -EADDRNOTAVAIL; + goto err_event_drop; + } + + /* own completion, or tx_gen moved on - either way this is + * what isotp_get_tx_result() recorded for my_gen + */ + err = isotp_get_tx_result(so, my_gen); + + /* drain to avoid stale error for a later poll()/SO_ERROR */ + sock_error(sk); + + return err ? err : size; } return size; @@ -1246,15 +1373,26 @@ err_out_drop: spin_lock_bh(&so->rx_lock); goto err_out_drop_locked; err_event_drop: - /* interrupted waiting on our own transfer - drain its timers */ + /* interrupted or shut down while waiting on our own transfer */ spin_lock_bh(&so->rx_lock); + + /* new transfer already started by concurrent sendmsg()? */ + if (READ_ONCE(so->tx_gen) != my_gen) { + /* don't touch timers and states of the new transfer */ + spin_unlock_bh(&so->rx_lock); + return err; + } + hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); 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; + WRITE_ONCE(so->cfecho, 0); + + /* only claim to IDLE if isotp_release() has not taken over */ + if (READ_ONCE(so->tx.state) != ISOTP_SHUTDOWN) + WRITE_ONCE(so->tx.state, ISOTP_IDLE); spin_unlock_bh(&so->rx_lock); wake_up_interruptible(&so->wait); @@ -1320,8 +1458,9 @@ static int isotp_release(struct socket *sock) /* 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) + while (READ_ONCE(so->tx.state) != ISOTP_IDLE && + wait_event_interruptible(so->wait, + READ_ONCE(so->tx.state) == ISOTP_IDLE) == 0) ; /* claim the socket under so->rx_lock like sendmsg() does, so its @@ -1329,9 +1468,12 @@ static int isotp_release(struct socket *sock) * unconditionally, even when a signal cut the wait above short */ spin_lock_bh(&so->rx_lock); - so->tx.state = ISOTP_SHUTDOWN; + WRITE_ONCE(so->tx.state, ISOTP_SHUTDOWN); spin_unlock_bh(&so->rx_lock); - so->rx.state = ISOTP_IDLE; + WRITE_ONCE(so->rx.state, ISOTP_IDLE); + + /* forced SHUTDOWN may have skipped IDLE (gave up on a signal) */ + wake_up_interruptible(&so->wait); spin_lock(&isotp_notifier_lock); while (isotp_busy_notifier == so) { @@ -1447,7 +1589,8 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l * 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) { + if (READ_ONCE(so->tx.state) != ISOTP_IDLE || + READ_ONCE(so->rx.state) != ISOTP_IDLE) { err = -EAGAIN; goto out; } @@ -1481,7 +1624,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l isotp_rcv, sk, "isotp", sk); /* no consecutive frame echo skb in flight */ - so->cfecho = 0; + WRITE_ONCE(so->cfecho, 0); /* register for echo skb's */ can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), @@ -1847,7 +1990,7 @@ static __poll_t isotp_poll(struct file *file, struct socket *sock, poll_table *w poll_wait(file, &so->wait, wait); /* Check for false positives due to TX state */ - if ((mask & EPOLLWRNORM) && (so->tx.state != ISOTP_IDLE)) + if ((mask & EPOLLWRNORM) && (READ_ONCE(so->tx.state) != ISOTP_IDLE)) mask &= ~(EPOLLOUT | EPOLLWRNORM); return mask; -- cgit v1.2.3 From c870f7e2890b9f78ac84515a9809cc5c183c975e Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 23 Jul 2026 10:18:19 +0800 Subject: net: sxgbe: free TX rings on RX allocation failure When RX descriptor ring allocation fails, init_dma_desc_rings() only frees the partially allocated RX rings and returns. The TX rings that were allocated earlier in the same function are leaked. Rearrange error labels to clean up TX rings upon RX failures. Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") Signed-off-by: Chenguang Zhao Reviewed-by: Vadim Fedorenko Signed-off-by: David S. Miller --- drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 5051ada43d2f..9b48a587d5c2 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -597,14 +597,13 @@ static int init_dma_desc_rings(struct net_device *netd) return 0; -txalloc_err: - while (queue_num--) - free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); - return ret; - rxalloc_err: while (queue_num--) free_rx_ring(priv->device, priv->rxq[queue_num], rx_rsize); + queue_num = SXGBE_TX_QUEUES; +txalloc_err: + while (queue_num--) + free_tx_ring(priv->device, priv->txq[queue_num], tx_rsize); return ret; } -- cgit v1.2.3 From 51b093a7ba27476e1f639455f005e8d2e75390e4 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Thu, 23 Jul 2026 10:18:20 +0800 Subject: net: sxgbe: check descriptor ring allocation failures sxgbe_open() ignores the return value of init_dma_desc_rings() and continues to program DMA with invalid ring addresses when allocation fails. Check the return value and disconnect the PHY on failure. Fixes: 1edb9ca69e8a ("net: sxgbe: add basic framework for Samsung 10Gb ethernet driver") Signed-off-by: Chenguang Zhao Reviewed-by: Vadim Fedorenko Signed-off-by: David S. Miller --- drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 9b48a587d5c2..70cf3619555f 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -1078,7 +1078,9 @@ static int sxgbe_open(struct net_device *dev) priv->dma_buf_sz = SXGBE_ALIGN(DMA_BUFFER_SIZE); priv->tx_tc = TC_DEFAULT; priv->rx_tc = TC_DEFAULT; - init_dma_desc_rings(dev); + ret = init_dma_desc_rings(dev); + if (ret) + goto init_phy_error; /* DMA initialization and SW reset */ ret = sxgbe_init_dma_engine(priv); @@ -1187,6 +1189,7 @@ static int sxgbe_open(struct net_device *dev) init_error: free_dma_desc_resources(priv); +init_phy_error: if (dev->phydev) phy_disconnect(dev->phydev); phy_error: -- cgit v1.2.3 From ef09a13c5afac41a3c4b5f22b8572820d9e7518c Mon Sep 17 00:00:00 2001 From: Minhong He Date: Wed, 29 Jul 2026 16:56:56 +0800 Subject: can: isotp: check register_netdevice_notifier() error in module init Register the netdevice notifier before can_proto_register() and check the return value. If protocol registration fails, unregister the notifier before returning the error. Align isotp_module_init() with the reordering already done for raw.c (commit c28b3bffe49e ("can: raw: process optimization in raw_init()")) and bcm.c (commit edd1a7e42f1d ("can: bcm: registration process optimization in bcm_module_init()")). Fixes: 8d0caedb7596 ("can: bcm/raw/isotp: use per module netdevice notifier") Signed-off-by: Minhong He Link: https://patch.msgid.link/20260729085656.134523-1-heminhong@kylinos.cn Signed-off-by: Marc Kleine-Budde --- net/can/isotp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 1f11c66b343c..155530aedce2 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -2050,13 +2050,18 @@ static __init int isotp_module_init(void) pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size); + err = register_netdevice_notifier(&canisotp_notifier); + if (err) + return err; + err = can_proto_register(&isotp_can_proto); - if (err < 0) + if (err < 0) { pr_err("can: registration of isotp protocol failed %pe\n", ERR_PTR(err)); - else - register_netdevice_notifier(&canisotp_notifier); + unregister_netdevice_notifier(&canisotp_notifier); + return err; + } - return err; + return 0; } static __exit void isotp_module_exit(void) -- cgit v1.2.3 From a6873910f983096746d1a2e0af94f36b8003e839 Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Thu, 23 Jul 2026 12:59:34 +0300 Subject: can: ctucanfd: unmap BAR0 using base address BAR0 is mapped into bar0_base, while cra_addr points to an offset within that mapping and is used for other purposes. Pass bar0_base to pci_iounmap(), instead of cra_addr, on the probe error path so the address returned by pci_iomap() is used for unmapping. Fixes: 792a5b678e81 ("can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support.") Signed-off-by: Avi Weiss Acked-by: Pavel Pisa Link: https://patch.msgid.link/20260723095934.181042-1-thnkslprpt@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ctucanfd/ctucanfd_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_pci.c b/drivers/net/can/ctucanfd/ctucanfd_pci.c index 625788fa8976..f845951c5d51 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_pci.c +++ b/drivers/net/can/ctucanfd/ctucanfd_pci.c @@ -194,7 +194,7 @@ err_free_board: pci_set_drvdata(pdev, NULL); kfree(bdata); err_pci_iounmap_bar0: - pci_iounmap(pdev, cra_addr); + pci_iounmap(pdev, bar0_base); err_pci_iounmap_bar1: pci_iounmap(pdev, addr); err_release_regions: -- cgit v1.2.3 From 4e735cbe3affe88001428fdd9cae8e685ce92f21 Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Thu, 23 Jul 2026 18:55:43 +0300 Subject: can: ctucanfd: mark error-active controller status valid In the CAN_STATE_ERROR_ACTIVE case, cf->data[1] is set to CAN_ERR_CRTL_ACTIVE, but cf->can_id is not set with CAN_ERR_CRTL in that path. Set CAN_ERR_CRTL so consumers know the controller-status information in cf->data[1] is valid. Fixes: 9bd24927e3ee ("can: ctucanfd: handle skb allocation failure") Signed-off-by: Avi Weiss Link: https://patch.msgid.link/20260723155543.318414-1-thnkslprpt@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ctucanfd/ctucanfd_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c index 0ea1ff28dfce..8f8b1c097ec6 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_base.c +++ b/drivers/net/can/ctucanfd/ctucanfd_base.c @@ -869,7 +869,7 @@ static void ctucan_err_interrupt(struct net_device *ndev, u32 isr) break; case CAN_STATE_ERROR_ACTIVE: if (skb) { - cf->can_id |= CAN_ERR_CNT; + cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT; cf->data[1] = CAN_ERR_CRTL_ACTIVE; cf->data[6] = bec.txerr; cf->data[7] = bec.rxerr; -- cgit v1.2.3 From e74bae899529f49c0f375307983d12e8ecad7d4b Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Thu, 23 Jul 2026 10:44:03 +0300 Subject: can: ctucanfd: handle bus error interrupts Include REG_INT_STAT_BEI in the top-level error interrupt condition. BEI is enabled when CAN_CTRLMODE_BERR_REPORTING is requested and ctucan_err_interrupt() already handles it. Without checking and clearing BEI in the top-level handler, bus error interrupts are not handled or acknowledged. Fixes: 2dcb8e8782d8 ("can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part.") Signed-off-by: Avi Weiss Acked-by: Pavel Pisa Link: https://patch.msgid.link/20260723074403.131575-1-thnkslprpt@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ctucanfd/ctucanfd_base.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c index 8f8b1c097ec6..10ebcc13ea65 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_base.c +++ b/drivers/net/can/ctucanfd/ctucanfd_base.c @@ -1136,8 +1136,12 @@ static irqreturn_t ctucan_interrupt(int irq, void *dev_id) /* Error interrupts */ if (FIELD_GET(REG_INT_STAT_EWLI, isr) || FIELD_GET(REG_INT_STAT_FCSI, isr) || - FIELD_GET(REG_INT_STAT_ALI, isr)) { - icr = isr & (REG_INT_STAT_EWLI | REG_INT_STAT_FCSI | REG_INT_STAT_ALI); + FIELD_GET(REG_INT_STAT_ALI, isr) || + FIELD_GET(REG_INT_STAT_BEI, isr)) { + icr = isr & (REG_INT_STAT_EWLI | + REG_INT_STAT_FCSI | + REG_INT_STAT_ALI | + REG_INT_STAT_BEI); ctucan_netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x\n", icr); ctucan_write32(priv, CTUCANFD_INT_STAT, icr); -- cgit v1.2.3 From c31a435933f18be0f874302161333e9f16e200a0 Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Wed, 22 Jul 2026 22:27:26 +0300 Subject: can: ctucanfd: use self-test mode for PRESUME_ACK Use self-test mode for CAN_CTRLMODE_PRESUME_ACK so transmitted frames can complete without receiving an ACK. ACK forbidden mode prevents the controller from acknowledging received frames and does not implement the presume-ack behavior. Fixes: 2dcb8e8782d8 ("can: ctucanfd: add support for CTU CAN FD open-source IP core - bus independent part.") Signed-off-by: Avi Weiss Acked-by: Pavel Pisa Link: https://patch.msgid.link/20260722192726.230729-1-thnkslprpt@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ctucanfd/ctucanfd_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c index 10ebcc13ea65..07d4aa43c700 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_base.c +++ b/drivers/net/can/ctucanfd/ctucanfd_base.c @@ -340,8 +340,8 @@ static void ctucan_set_mode(struct ctucan_priv *priv, const struct can_ctrlmode (mode_reg & ~REG_MODE_FDE); mode_reg = (mode->flags & CAN_CTRLMODE_PRESUME_ACK) ? - (mode_reg | REG_MODE_ACF) : - (mode_reg & ~REG_MODE_ACF); + (mode_reg | REG_MODE_STM) : + (mode_reg & ~REG_MODE_STM); mode_reg = (mode->flags & CAN_CTRLMODE_FD_NON_ISO) ? (mode_reg | REG_MODE_NISOFD) : -- cgit v1.2.3 From d937bdb244a751fe5967052ea2d64a7b2c476cc0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 23:19:57 +0800 Subject: can: ctucanfd: add missing MODULE_DEVICE_TABLE() The driver has a match table for the pci bus wired into its driver structure, but the table is not exported with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE() entry so module alias information is generated for automatic module loading. 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 driver registration structure, and the missing module alias publication. Signed-off-by: Pengpeng Hou Acked-by: Pavel Pisa Link: https://patch.msgid.link/20260704151957.48194-1-pengpeng@iscas.ac.cn Fixes: 792a5b678e81 ("can: ctucanfd: CTU CAN FD open-source IP core - PCI bus support.") Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ctucanfd/ctucanfd_pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/ctucanfd/ctucanfd_pci.c b/drivers/net/can/ctucanfd/ctucanfd_pci.c index f845951c5d51..4b6db28f7b67 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_pci.c +++ b/drivers/net/can/ctucanfd/ctucanfd_pci.c @@ -266,6 +266,7 @@ static const struct pci_device_id ctucan_pci_tbl[] = { CTUCAN_WITH_CTUCAN_ID)}, {}, }; +MODULE_DEVICE_TABLE(pci, ctucan_pci_tbl); static struct pci_driver ctucan_pci_driver = { .name = KBUILD_MODNAME, -- cgit v1.2.3 From 39132f166ca8ce00ae60d8a9068e06a60943cc4b Mon Sep 17 00:00:00 2001 From: James Gao Date: Wed, 20 May 2026 13:40:03 +0800 Subject: can: peak_usb: add bounds check for USB channel index The channel control index ctrl_idx is derived from rx->len which comes directly from a device USB payload. The mask 0x0f allows values 0-15, but the array size of usb_if->dev[] is only 2. Values 2-15 cause heap out-of-bounds read, eventually causing kernel panic in the IRQ context. Add bounds checking for ctrl_idx before the array access in both pcan_usb_pro_handle_canmsg() and pcan_usb_pro_handle_error(). Fixes: d8a199355f8f ("can: usb: PEAK-System Technik PCAN-USB Pro specific part") Signed-off-by: James Gao Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/TYWPR01MB8559DBAAAA6A7F410400329CF0012@TYWPR01MB8559.jpnprd01.prod.outlook.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) 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 aefcded8e12a..b6be8c19e537 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -534,12 +534,18 @@ static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if, struct pcan_usb_pro_rxmsg *rx) { const unsigned int ctrl_idx = (rx->len >> 4) & 0x0f; - struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; - struct net_device *netdev = dev->netdev; + struct peak_usb_device *dev; + struct net_device *netdev; struct can_frame *can_frame; struct sk_buff *skb; struct skb_shared_hwtstamps *hwts; + if (ctrl_idx >= ARRAY_SIZE(usb_if->dev)) + return -EINVAL; + + dev = usb_if->dev[ctrl_idx]; + netdev = dev->netdev; + skb = alloc_can_skb(netdev, &can_frame); if (!skb) return -ENOMEM; @@ -573,14 +579,20 @@ static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, { const u16 raw_status = le16_to_cpu(er->status); const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f; - struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; - struct net_device *netdev = dev->netdev; + struct peak_usb_device *dev; + struct net_device *netdev; struct can_frame *can_frame; enum can_state new_state = CAN_STATE_ERROR_ACTIVE; u8 err_mask = 0; struct sk_buff *skb; struct skb_shared_hwtstamps *hwts; + if (ctrl_idx >= ARRAY_SIZE(usb_if->dev)) + return -EINVAL; + + dev = usb_if->dev[ctrl_idx]; + netdev = dev->netdev; + /* nothing should be sent while in BUS_OFF state */ if (dev->can.state == CAN_STATE_BUS_OFF) return 0; -- cgit v1.2.3 From 9b3d5a6d952c38bbcf07f903cbeadefdb56b9bc9 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Wed, 17 Jun 2026 02:15:31 +0800 Subject: can: peak_usb: peak_usb_start(): fix double free of transfer buffer on URB submit error In peak_usb_start(), each RX URB transfer buffer is allocated with kmalloc() and the URB is flagged URB_FREE_BUFFER so that the final usb_free_urb() also frees the transfer buffer. If usb_submit_urb() fails, the error path frees the buffer explicitly with kfree(buf) and then calls usb_free_urb(urb). Because URB_FREE_BUFFER is set, usb_free_urb() -> urb_destroy() frees the same buffer a second time, a double free of the transfer buffer. BUG: KASAN: double-free in usb_free_urb.part.0+0x91/0xb0 Free of addr ffff8881069ccb80 by task trigger.sh/285 Call Trace: kfree+0x113/0x3c0 usb_free_urb.part.0+0x91/0xb0 Drop the redundant kfree(buf); usb_free_urb() already releases the transfer buffer. This mirrors commit 03819abbeb11 ("net: usb: lan78xx: Fix double free issue with interrupt buffer allocation"). Fixes: bb4785551f64 ("can: usb: PEAK-System Technik USB adapters driver core") Closes: https://lore.kernel.org/linux-can/178159320216.2154888.16953451793788581739@maoyixie.com/T/#u Cc: stable@vger.kernel.org Signed-off-by: Maoyi Xie Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/178163373110.2507866.216458825145756798@maoyixie.com Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/peak_usb/pcan_usb_core.c | 1 - 1 file changed, 1 deletion(-) 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 c7933d1acc99..55aad01cd8ca 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -470,7 +470,6 @@ static int peak_usb_start(struct peak_usb_device *dev) netif_device_detach(dev->netdev); usb_unanchor_urb(urb); - kfree(buf); usb_free_urb(urb); break; } -- cgit v1.2.3 From 93fcab2c6968446316bbb49548848df604d6346f Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 17:28:36 +0800 Subject: can: peak_usb: validate uCAN receive record lengths pcan_usb_fd_decode_buf() walks uCAN records packed in one USB receive buffer. Require each record to contain the fixed header for its type, and verify CAN payload bytes before copying them into the skb. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260706092836.79754-1-pengpeng@iscas.ac.cn Fixes: 0a25e1f4f185 ("can: peak_usb: add support for PEAK new CANFD USB adapters") Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 40 +++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) 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 ef9fd693e9bd..0d46f4ce5dca 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -566,6 +566,13 @@ static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if, dev->can.ctrlmode); } + if (!(rx_msg_flags & PUCAN_MSG_RTR) && + le16_to_cpu(rx_msg->size) - offsetof(struct pucan_rx_msg, d) < + cfd->len) { + kfree_skb(skb); + return -EBADMSG; + } + cfd->can_id = le32_to_cpu(rm->can_id); if (rx_msg_flags & PUCAN_MSG_EXT_ID) @@ -714,6 +721,24 @@ static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if, peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low)); } +static size_t pcan_usb_fd_rx_msg_min_size(u16 rx_msg_type) +{ + switch (rx_msg_type) { + case PUCAN_MSG_CAN_RX: + return offsetof(struct pucan_rx_msg, d); + case PCAN_UFD_MSG_CALIBRATION: + return sizeof(struct pcan_ufd_ts_msg); + case PUCAN_MSG_ERROR: + return sizeof(struct pucan_error_msg); + case PUCAN_MSG_STATUS: + return sizeof(struct pucan_status_msg); + case PCAN_UFD_MSG_OVERRUN: + return sizeof(struct pcan_ufd_ovr_msg); + default: + return sizeof(struct pucan_msg); + } +} + /* callback for bulk IN urb */ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) { @@ -728,6 +753,12 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) msg_end = urb->transfer_buffer + urb->actual_length; for (; msg_ptr < msg_end;) { u16 rx_msg_type, rx_msg_size; + size_t rx_msg_min_size; + + if (msg_end - msg_ptr < sizeof(*rx_msg)) { + err = -EBADMSG; + break; + } rx_msg = (struct pucan_msg *)msg_ptr; if (!rx_msg->size) { @@ -739,13 +770,20 @@ static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) rx_msg_type = le16_to_cpu(rx_msg->type); /* check if the record goes out of current packet */ - if (msg_ptr + rx_msg_size > msg_end) { + if (rx_msg_size > msg_end - msg_ptr) { netdev_err(netdev, "got frag rec: should inc usb rx buf sze\n"); err = -EBADMSG; break; } + rx_msg_min_size = pcan_usb_fd_rx_msg_min_size(rx_msg_type); + if (rx_msg_size < rx_msg_min_size) { + netdev_err(netdev, "got short rec\n"); + err = -EBADMSG; + break; + } + switch (rx_msg_type) { case PUCAN_MSG_CAN_RX: err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg); -- cgit v1.2.3 From 941eaf9a6d3b33dea49f2c0a1da7546a03b6ff71 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Wed, 22 Jul 2026 16:09:03 +0530 Subject: can: kvaser_usb: kvaser_usb_hydra_get_busparams(): fix memory leak in kvaser_usb_hydra_get_busparams() The memory allocated for cmd is not freed after the call to kvaser_usb_send_cmd() in both the normal and error paths. Fix that by adding a kfree() immediately after the call. Fixes: 39d3df6b0ea8 ("can: kvaser_usb: Compare requested bittiming parameters with actual parameters in do_set_{,data}_bittiming") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Link: https://patch.msgid.link/20260722103906.108571-1-nihaal@cse.iitm.ac.in Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index e09d663e362f..efbb7bed34c9 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -1626,6 +1626,7 @@ static int kvaser_usb_hydra_get_busparams(struct kvaser_usb_net_priv *priv, reinit_completion(&priv->get_busparams_comp); err = kvaser_usb_send_cmd(dev, cmd, cmd_len); + kfree(cmd); if (err) return err; -- cgit v1.2.3 From 0293dd153f9dbc1ddf5dacdccc76b363bce4a8ee Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 22 Jul 2026 12:22:21 +0800 Subject: can: kvaser_usb_leaf: kvaser_usb_leaf_wait_cmd(): validate received command extents The wait and bulk receive paths walk variable-length commands from a USB buffer. A nonzero command shorter than CMD_HEADER_LEN can still be dispatched, and the wait path copies a matching command into a fixed caller-owned struct kvaser_cmd using the device-provided length. Reject nonzero commands that do not contain the fixed header or that extend beyond the current USB buffer item. In the wait path, also reject a matching command that exceeds the destination before copying it. Fixes: 080f40a6fa28 ("can: kvaser_usb: Add support for Kvaser CAN/USB devices") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260722042221.44066-1-pengpeng@iscas.ac.cn Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c index df737cfc5ea0..a876c7819b81 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c @@ -691,13 +691,22 @@ static int kvaser_usb_leaf_wait_cmd(const struct kvaser_usb *dev, u8 id, continue; } - if (pos + tmp->len > actual_len) { + if (tmp->len < CMD_HEADER_LEN || + tmp->len > actual_len - pos) { dev_err_ratelimited(&dev->intf->dev, "Format error\n"); break; } if (tmp->id == id) { + if (tmp->len > sizeof(*cmd)) { + dev_err_ratelimited(&dev->intf->dev, + "Received command %u too large (%u)\n", + tmp->id, tmp->len); + err = -EIO; + goto end; + } + memcpy(cmd, tmp, tmp->len); goto end; } @@ -1737,7 +1746,7 @@ static void kvaser_usb_leaf_read_bulk_callback(struct kvaser_usb *dev, continue; } - if (pos + cmd->len > len) { + if (cmd->len < CMD_HEADER_LEN || cmd->len > len - pos) { dev_err_ratelimited(&dev->intf->dev, "Format error\n"); break; } -- cgit v1.2.3 From bef9004c5b91debfceaea2841855a4ebe81ff2b3 Mon Sep 17 00:00:00 2001 From: Tu Nguyen Date: Thu, 25 Jun 2026 14:51:51 +0100 Subject: can: rcar_canfd: change the initializing flow for clocks and resets Testing CANFD on RZ/G3E shows that many registers do not reset to their initial values with the current flow of deasserting resets first and then enabling clocks. Based on the HW manual, clocks should be supplied first and the resets deasserted afterward. section 7.4.3 Procedure for Activating Modules: RZ/G2L section 4.4.9.3 Procedure for Starting up Units: RZ/G3E So, update the order of the initializing flow for resets and clocks to match the hardware manual, resetting all CANFD registers to their initial values. Also update rcar_canfd_global_deinit() to assert resets before disabling clocks, so the teardown path mirrors the new init ordering. Fixes: 76e9353a80e9 ("can: rcar_canfd: Add support for RZ/G2L family") Signed-off-by: Tu Nguyen Signed-off-by: Biju Das Tested-by: Claudiu Beznea Reviewed-by: Geert Uytterhoeven Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/20260625135216.130450-1-biju.das.jz@bp.renesas.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rcar/rcar_canfd.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index eaf8cac78038..fcc37b73ed43 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -2003,20 +2003,12 @@ static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv) u32 ch, sts; int err; - err = reset_control_reset(gpriv->rstc1); - if (err) - return err; - - err = reset_control_reset(gpriv->rstc2); - if (err) - goto fail_reset1; - /* Enable peripheral clock for register access */ err = clk_prepare_enable(gpriv->clkp); if (err) { dev_err(dev, "failed to enable peripheral clock: %pe\n", ERR_PTR(err)); - goto fail_reset2; + return err; } /* Enable RAM clock */ @@ -2027,10 +2019,18 @@ static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv) goto fail_clk; } + err = reset_control_reset(gpriv->rstc1); + if (err) + goto fail_ram_clk; + + err = reset_control_reset(gpriv->rstc2); + if (err) + goto fail_reset1; + err = rcar_canfd_reset_controller(gpriv); if (err) { dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err)); - goto fail_ram_clk; + goto fail_reset2; } /* Controller in Global reset & Channel reset mode */ @@ -2068,14 +2068,14 @@ static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv) fail_mode: rcar_canfd_disable_global_interrupts(gpriv); -fail_ram_clk: - clk_disable_unprepare(gpriv->clk_ram); -fail_clk: - clk_disable_unprepare(gpriv->clkp); fail_reset2: reset_control_assert(gpriv->rstc2); fail_reset1: reset_control_assert(gpriv->rstc1); +fail_ram_clk: + clk_disable_unprepare(gpriv->clk_ram); +fail_clk: + clk_disable_unprepare(gpriv->clkp); return err; } @@ -2090,10 +2090,10 @@ static void rcar_canfd_global_deinit(struct rcar_canfd_global *gpriv, bool full) rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR); } - clk_disable_unprepare(gpriv->clk_ram); - clk_disable_unprepare(gpriv->clkp); reset_control_assert(gpriv->rstc2); reset_control_assert(gpriv->rstc1); + clk_disable_unprepare(gpriv->clk_ram); + clk_disable_unprepare(gpriv->clkp); } static int rcar_canfd_probe(struct platform_device *pdev) -- cgit v1.2.3 From 856d6cb04e5407523566b075841dcd6423757d1c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 22 Jul 2026 12:43:47 +0800 Subject: can: softing: fw_parse(): validate firmware record spans fw_parse() reads a fixed record header, a firmware-provided payload, and a trailing checksum without knowing the end of the firmware blob. A truncated record can therefore make those reads exceed the blob. The same record also supplies addresses and lengths for writes into DPRAM. The generic loader uses wrap-prone mixed signed arithmetic for its bounds check, while the application loader does not bound the staging copy at all. Pass the firmware end to the parser and validate the full source record. Use a signed wide offset for generic DPRAM records and validate the application staging span against the mapped DPRAM before copying. Fixes: 03fd3cf5a179 ("can: add driver for Softing card") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260722044347.2708-1-pengpeng@iscas.ac.cn Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/softing/softing_fw.c | 46 +++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c index 721df91cdbfb..282570daf3ef 100644 --- a/drivers/net/can/softing/softing_fw.c +++ b/drivers/net/can/softing/softing_fw.c @@ -91,12 +91,12 @@ int softing_bootloader_command(struct softing *card, int16_t cmd, return ret; } -static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr, - uint16_t *plen, const uint8_t **pdat) +static int fw_parse(const u8 **pmem, const u8 *limit, u16 *ptype, + u32 *paddr, u16 *plen, const u8 **pdat) { uint16_t checksum[2]; - const uint8_t *mem; - const uint8_t *end; + const u8 *mem; + const u8 *record_end; /* * firmware records are a binary, unaligned stream composed of: @@ -114,14 +114,21 @@ static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr, * endianness & alignment. */ mem = *pmem; + /* A record needs an 8-byte prefix and a 2-byte checksum. */ + if (mem > limit || limit - mem < 10) + return -EINVAL; + *ptype = le16_to_cpup((void *)&mem[0]); *paddr = le32_to_cpup((void *)&mem[2]); *plen = le16_to_cpup((void *)&mem[6]); + if (*plen > limit - mem - 10) + return -EINVAL; + *pdat = &mem[8]; /* verify checksum */ - end = &mem[8 + *plen]; - checksum[0] = le16_to_cpup((void *)end); - for (checksum[1] = 0; mem < end; ++mem) + record_end = &mem[8 + *plen]; + checksum[0] = le16_to_cpup((void *)record_end); + for (checksum[1] = 0; mem < record_end; ++mem) checksum[1] += *mem; if (checksum[0] != checksum[1]) return -EINVAL; @@ -139,6 +146,7 @@ int softing_load_fw(const char *file, struct softing *card, uint16_t type, len; uint32_t addr; uint8_t *buf = NULL, *new_buf; + s64 dpram_offset; int buflen = 0; int8_t type_end = 0; @@ -153,7 +161,7 @@ int softing_load_fw(const char *file, struct softing *card, mem = fw->data; end = &mem[fw->size]; /* look for header record */ - ret = fw_parse(&mem, &type, &addr, &len, &dat); + ret = fw_parse(&mem, end, &type, &addr, &len, &dat); if (ret < 0) goto failed; if (type != 0xffff) @@ -164,7 +172,7 @@ int softing_load_fw(const char *file, struct softing *card, } /* ok, we had a header */ while (mem < end) { - ret = fw_parse(&mem, &type, &addr, &len, &dat); + ret = fw_parse(&mem, end, &type, &addr, &len, &dat); if (ret < 0) goto failed; if (type == 3) { @@ -179,9 +187,13 @@ int softing_load_fw(const char *file, struct softing *card, goto failed; } - if ((addr + len + offset) > size) + dpram_offset = (s64)addr + offset; + if (dpram_offset < 0 || dpram_offset > size || + len > size - dpram_offset) { + ret = -EINVAL; goto failed; - memcpy_toio(&dpram[addr + offset], dat, len); + } + memcpy_toio(&dpram[dpram_offset], dat, len); /* be sure to flush caches from IO space */ mb(); if (len > buflen) { @@ -195,7 +207,7 @@ int softing_load_fw(const char *file, struct softing *card, buf = new_buf; } /* verify record data */ - memcpy_fromio(buf, &dpram[addr + offset], len); + memcpy_fromio(buf, &dpram[dpram_offset], len); if (memcmp(buf, dat, len)) { /* is not ok */ dev_alert(&card->pdev->dev, "DPRAM readback failed\n"); @@ -237,7 +249,7 @@ int softing_load_app_fw(const char *file, struct softing *card) mem = fw->data; end = &mem[fw->size]; /* look for header record */ - ret = fw_parse(&mem, &type, &addr, &len, &dat); + ret = fw_parse(&mem, end, &type, &addr, &len, &dat); if (ret) goto failed; ret = -EINVAL; @@ -253,7 +265,7 @@ int softing_load_app_fw(const char *file, struct softing *card) } /* ok, we had a header */ while (mem < end) { - ret = fw_parse(&mem, &type, &addr, &len, &dat); + ret = fw_parse(&mem, end, &type, &addr, &len, &dat); if (ret) goto failed; @@ -279,6 +291,12 @@ int softing_load_app_fw(const char *file, struct softing *card) /* work in 16bit (target) */ sum &= 0xffff; + if (card->pdat->app.offs > card->dpram_size || + len > card->dpram_size - card->pdat->app.offs) { + ret = -EINVAL; + goto failed; + } + memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len); iowrite32(card->pdat->app.offs + card->pdat->app.addr, &card->dpram[DPRAM_COMMAND + 2]); -- cgit v1.2.3 From 26504844613fb44c7cab1c5f6fcff77861709baa Mon Sep 17 00:00:00 2001 From: Lucas Martins Alves Date: Tue, 14 Jul 2026 16:48:57 +0000 Subject: can: c_can: c_can_chip_config(): keep controller in init mode until bittiming is configured c_can_chip_config() was programming C_CAN_CTRL_REG without CONTROL_INIT, which may allow the controller to become active before c_can_set_bittiming() finishes. That creates a short timing window where the peripheral can interact with the bus using a different/default bitrate, potentially generating bus errors and corrupting traffic. Set CONTROL_INIT together with the control-mode writes in c_can_chip_config() (normal, loopback and listen-only paths), so the controller stays halted until bit timing is fully programmed. This prevents transient bus disturbance during startup when the configured bitrate differs from the active bus bitrate. Signed-off-by: Lucas Martins Alves Link: https://patch.msgid.link/20260714164839.771123-1-lucas.alves@lumal21.com.br Fixes: 881ff67ad450 ("can: c_can: Added support for Bosch C_CAN controller") Cc: stable@kernel.org [mkl: remove space before close parenthesis] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/c_can/c_can_main.c b/drivers/net/can/c_can/c_can_main.c index 3702cac7fbf0..b3b321d9ce68 100644 --- a/drivers/net/can/c_can/c_can_main.c +++ b/drivers/net/can/c_can/c_can_main.c @@ -597,20 +597,20 @@ static int c_can_chip_config(struct net_device *dev) return err; /* enable automatic retransmission */ - priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR); + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR | CONTROL_INIT); if ((priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) && (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)) { /* loopback + silent mode : useful for hot self-test */ - priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT); priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK | TEST_SILENT); } else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { /* loopback mode : useful for self-test function */ - priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT); priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK); } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { /* silent mode : bus-monitoring mode */ - priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST); + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_TEST | CONTROL_INIT); priv->write_reg(priv, C_CAN_TEST_REG, TEST_SILENT); } -- cgit v1.2.3 From 68c5724ecd159992f76edb7b57dc508a44c8b7da Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 9 Jul 2026 09:54:26 +0200 Subject: can: gs_usb: gs_usb_receive_bulk_callback(): resubmit URB on skb allocation failure If the allocation of the SKB in gs_usb_receive_bulk_callback() fails, the driver returns from the callback without resubmitting the URB in order to receive further USB in URBs. This results in a silent performance degradation which, if it occurs repeatedly, results in starvation of USB in traffic. Instead of returning immediately, try to resend the URB. If this also fails, this is logged as an info message. Fixes: d08e973a77d1 ("can: gs_usb: Added support for the GS_USB CAN devices") Fixes: 26949ac935e3 ("can: gs_usb: add CAN-FD support") Link: https://patch.msgid.link/20260709-gs_usb-resubmit-urb-v1-1-4dd40030cc84@pengutronix.de Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/gs_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index ec9a7cbbbc69..82508a865095 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -674,7 +674,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) if (hf->flags & GS_CAN_FLAG_FD) { skb = alloc_canfd_skb(netdev, &cfd); if (!skb) - return; + goto resubmit_urb; cfd->can_id = le32_to_cpu(hf->can_id); cfd->len = data_length; @@ -687,7 +687,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb) } else { skb = alloc_can_skb(netdev, &cf); if (!skb) - return; + goto resubmit_urb; cf->can_id = le32_to_cpu(hf->can_id); can_frame_set_cc_len(cf, hf->can_dlc, dev->can.ctrlmode); -- cgit v1.2.3 From 7a0cf2b2497c757c3cb1286eddf2986abb0d387b Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 6 Jul 2026 09:46:01 +0800 Subject: can: etas_es58x: es58x_read_bulk_callback(): fix RX buffer leak on URB resubmit failure es58x_read_bulk_callback() resubmits the RX URB after processing a received packet. If the resubmit succeeds, the URB remains anchored and will be handled by the normal RX path or by teardown. However, if usb_submit_urb() fails, the callback unanchors the URB and then returns directly. This skips the existing free_urb path, so the coherent transfer buffer allocated with usb_alloc_coherent() is not released. Reuse the existing free_urb path after a resubmit failure so that the RX coherent buffer is freed before leaving the callback. Fixes: 5eaad4f76826 ("can: usb: etas_es58x: correctly anchor the urb in the read bulk callback") Signed-off-by: Guangshuo Li Reviewed-by: Vincent Mailhol Link: https://patch.msgid.link/20260706014601.415445-1-lgs201920130244@gmail.com Cc: stable@kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/etas_es58x/es58x_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c index b259f6109808..e1724ae79c5a 100644 --- a/drivers/net/can/usb/etas_es58x/es58x_core.c +++ b/drivers/net/can/usb/etas_es58x/es58x_core.c @@ -1476,7 +1476,6 @@ static void es58x_read_bulk_callback(struct urb *urb) dev_err_ratelimited(dev, "Failed resubmitting read bulk urb: %pe\n", ERR_PTR(ret)); - return; free_urb: usb_free_coherent(urb->dev, urb->transfer_buffer_length, -- cgit v1.2.3 From 02925f51377f2a42a6724f00549167499c9302e5 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 17:27:52 +0800 Subject: can: ems_usb: validate CPC message lengths ems_usb_read_bulk_callback() walks CPC messages packed in one USB receive buffer. Check that each declared message fits in the URB payload. Also require the type-specific payload to cover the fields used by the CAN, state, error and overrun handlers. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260706092752.79600-1-pengpeng@iscas.ac.cn Fixes: 702171adeed3 ("ems_usb: Added support for EMS CPC-USB/ARM7 CAN/USB interface") Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/ems_usb.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 9b25dda7c183..24cf8f651f8f 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -409,6 +409,40 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) netif_rx(skb); } +static bool ems_usb_rx_msg_len_valid(struct ems_cpc_msg *msg) +{ + size_t len = msg->length; + size_t can_len; + + switch (msg->type) { + case CPC_MSG_TYPE_CAN_STATE: + return len >= sizeof(msg->msg.can_state); + + case CPC_MSG_TYPE_CAN_FRAME: + case CPC_MSG_TYPE_EXT_CAN_FRAME: + case CPC_MSG_TYPE_RTR_FRAME: + case CPC_MSG_TYPE_EXT_RTR_FRAME: + if (len < CPC_CAN_MSG_MIN_SIZE) + return false; + + if (msg->type == CPC_MSG_TYPE_RTR_FRAME || + msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) + return true; + + can_len = can_cc_dlc2len(msg->msg.can_msg.length & 0xf); + return len >= CPC_CAN_MSG_MIN_SIZE + can_len; + + case CPC_MSG_TYPE_CAN_FRAME_ERROR: + return len >= sizeof(msg->msg.error); + + case CPC_MSG_TYPE_OVERRUN: + return len >= sizeof(msg->msg.overrun); + + default: + return true; + } +} + /* * callback for bulk IN urb */ @@ -451,6 +485,15 @@ static void ems_usb_read_bulk_callback(struct urb *urb) } msg = (struct ems_cpc_msg *)&ibuf[start]; + if (msg->length > + urb->actual_length - start - CPC_MSG_HEADER_LEN) { + netdev_err(netdev, "format error\n"); + break; + } + if (!ems_usb_rx_msg_len_valid(msg)) { + netdev_err(netdev, "format error\n"); + break; + } switch (msg->type) { case CPC_MSG_TYPE_CAN_STATE: -- cgit v1.2.3 From 26cb8ebbfaf713c82e142d08828d4d765057633b Mon Sep 17 00:00:00 2001 From: Chao Shi Date: Mon, 27 Jul 2026 16:12:57 -0400 Subject: block: stop the timeout timer when releasing a never added disk disk_release() undoes blk_mq_init_allocated_queue() for a disk whose probe failed before add_disk(), but it only calls blk_mq_exit_queue(). Nothing there stops q->timeout, and that timer rolls forward: it stays pending until it next expires, not until the last request completes. So if the driver issued any I/O before adding the disk, the request_queue is freed while still linked into a timer wheel bucket. Commit 6f8191fdf41d ("block: simplify disk shutdown") dropped the blk_cleanup_queue() call that used to stop it. __del_gendisk() and blk_mq_destroy_queue() still do; only the probe failure path lost it. nvme gets there because nvme_update_ns_info() submits Report Zones or FDP io-mgmt-recv on ns->queue before the disk is added, so a later failure - a concurrent reset setting NVME_CTRL_FROZEN, or device_add_disk() failing - lands in put_disk() with the timer armed: BUG: KASAN: slab-use-after-free in detach_if_pending+0x30c/0x340 Write of size 8 at addr ffff888004d71310 by task kworker/u8:2/37 __timer_delete_sync+0x156/0x240 kernel/time/timer.c:1621 blk_sync_queue+0x22/0x40 block/blk-core.c:222 nvme_sync_queues+0x100/0x150 drivers/nvme/host/core.c:5362 nvme_reset_work+0x138/0x930 drivers/nvme/host/pci.c:3264 Allocated by task 34: __blk_mq_alloc_disk+0x33/0x100 block/blk-mq.c:4462 nvme_alloc_ns+0x290/0x3870 drivers/nvme/host/core.c:4146 Freed by task 0: blk_free_queue_rcu+0x3a/0x50 block/blk-core.c:254 rcu_core+0xc10/0x1730 kernel/rcu/tree.c:2857 The queue being synced there is ctrl->admin_q, only a victim sharing a timer wheel bucket with the freed queue's dangling entry; other runs tripped in enqueue_timer(), __run_timers() or blk_mq_timeout_work(). Failing nvme_alloc_ns() with a debug patch makes it deterministic: one leaked timer trips KASAN within seconds, while 1987 patched releases produced no splat. Stop the timer and the queue work items before blk_mq_exit_queue(), like blk_mq_destroy_queue() does. Found by FuzzNvme. Fixes: 6f8191fdf41d ("block: simplify disk shutdown") Acked-by: Weidong Zhu Signed-off-by: Chao Shi Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260727201257.211635-1-coshi036@gmail.com Signed-off-by: Jens Axboe --- block/genhd.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index df2c3c69b467..e8ce0cabf392 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1281,14 +1281,18 @@ static void disk_release(struct device *dev) /* * To undo the all initialization from blk_mq_init_allocated_queue in * case of a probe failure where add_disk is never called we have to - * call blk_mq_exit_queue here. We can't do this for the more common - * teardown case (yet) as the tagset can be gone by the time the disk - * is released once it was added. + * call blk_mq_exit_queue here, after stopping the timer and work items + * that I/O issued before add_disk may have left pending. We can't do + * this for the more common teardown case (yet) as the tagset can be + * gone by the time the disk is released once it was added. */ if (queue_is_mq(disk->queue) && test_bit(GD_OWNS_QUEUE, &disk->state) && - !test_bit(GD_ADDED, &disk->state)) + !test_bit(GD_ADDED, &disk->state)) { + blk_sync_queue(disk->queue); + blk_mq_cancel_work_sync(disk->queue); blk_mq_exit_queue(disk->queue); + } blkcg_exit_disk(disk); -- cgit v1.2.3 From e40e20ac089e32f1d910636155dc82e61e61dcf3 Mon Sep 17 00:00:00 2001 From: Alexander Kaplan Date: Wed, 10 Jun 2026 21:38:25 +0200 Subject: drm/dp: Read the PCON max FRL bandwidth only for HDMI DFPs The PCON max FRL bandwidth field lives in byte 2 of the DFP Detailed Capability Info (DPCD 0x82 for the first DFP). The DP standard defines the meaning of descriptor bytes 1-3 strictly per DFP type, and for a DisplayPort type DFP all of them are reserved, with "read all 0s" semantics (DP v2.0, section 2.12.3, Table 2-183). The FRL bandwidth field is an HDMI DFP extension added by the VESA DP-to-HDMI PCON specification. drm_dp_get_pcon_max_frl_bw() however parses the byte without checking the DFP type, the branch presence or DETAILED_CAP_INFO_AVAILABLE. Without the latter the port descriptors are one byte wide and port_cap[2] is not even the right register. All neighbouring helpers parsing the same descriptor are scoped by the DFP type already, see for instance drm_dp_downstream_max_bpc() reading the same byte and returning 0 for a DP type DFP. amdgpu's DC parses the field only for HDMI(/DP++) detailed types as well. This is not theoretical. A Synaptics VMM7100 based USB-C to HDMI adapter with a macOS targeted firmware advertises a DisplayPort type DFP with the type byte replicated across the whole descriptor (08 08 08 08). i915 decodes that as "PCON limited to 18 Gbps FRL" and prunes every mode above ~750 MHz dotclock, including all the 4k@100/120 modes the sink EDID offers, while macOS drives 4k@120 through the same adapter just fine via DP DSC (and amdgpu's type-scoped parser would ignore the bogus field as well). Only parse the field for an HDMI DFP behind a DPCD 1.1+ branch device that reports detailed cap info, matching the type-scoped field layout of the spec and the rest of the helpers. Fixes: ce32a6239de6 ("drm/dp_helper: Add Helpers for FRL Link Training support for DP-HDMI2.1 PCON") Cc: Ankit Nautiyal Cc: Uma Shankar (v2) Cc: Jani Nikula Cc: Maarten Lankhorst Cc: dri-devel@lists.freedesktop.org Cc: # v5.12+ Signed-off-by: Alexander Kaplan Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260610193825.2933-1-alexander.kaplan@sms-medipool.de --- drivers/gpu/drm/display/drm_dp_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 9c31e14cc413..e623ccb4c1d8 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -3686,6 +3686,18 @@ int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE], int bw; u8 buf; + if (!drm_dp_is_branch(dpcd)) + return 0; + + if (dpcd[DP_DPCD_REV] < 0x11) + return 0; + + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) + return 0; + + if ((port_cap[0] & DP_DS_PORT_TYPE_MASK) != DP_DS_PORT_TYPE_HDMI) + return 0; + buf = port_cap[2]; bw = buf & DP_PCON_MAX_FRL_BW; -- cgit v1.2.3 From 5732bd17feb9ddf843591c4d038eb6de1f8daf4c Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Sat, 9 May 2026 22:50:25 +1000 Subject: MAINTAINERS: Demote myself to reviewer I haven't been active enough lately to warrant an M: maintainer entry, demote me to reviewer. Signed-off-by: Michael Ellerman Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/87y0hspx0u.fsf@mpe.ellerman.id.au --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 716acfc3d7c1..07088d5ac788 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14887,7 +14887,7 @@ X: drivers/macintosh/via-macii.c LINUX FOR POWERPC (32-BIT AND 64-BIT) M: Madhavan Srinivasan -M: Michael Ellerman +R: Michael Ellerman R: Nicholas Piggin R: Christophe Leroy (CS GROUP) L: linuxppc-dev@lists.ozlabs.org -- cgit v1.2.3 From 3e191eddbdcb8bf7beb1e9b58209073bd5450719 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 28 Jul 2026 17:31:45 +0200 Subject: drm/mediatek: mtk_dsi: Enable HS clock only at pre-enable Commit 76255024cadb ("drm/mediatek: mtk_dsi: enable hs clock during pre-enable") rightfully moves the HS clock enablement to before atomic_enable(), but it's moving it to mtk_dsi_poweron(), which is not only called in the .atomic_pre_enable() callback for the DRM bridge, but also in the MediaTek DRM's .ddp_start() callback, which happens way before the bridge ones. The HS clock enablement should be done at just the right time, otherwise some bridge chips (or some Display Driver ICs) may not work correctly: this is seen at least with a Parade DSI to eDP bridge (PS8640) on the MT8173 Elm Chromebook. This resolves a regression that was seen on the aforementioned machine, which was happening only after a suspend-resume cycle. Cc: # 7.1.x Fixes: 76255024cadb ("drm/mediatek: mtk_dsi: enable hs clock during pre-enable") Signed-off-by: AngeloGioacchino Del Regno Tested-by: Adam Thiede Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260728153145.578404-1-angelogioacchino.delregno@collabora.com/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_dsi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index eb325e68aa59..49dfc0f825cb 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -743,8 +743,6 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi) mtk_dsi_set_vm_cmd(dsi); mtk_dsi_config_vdo_timing(dsi); mtk_dsi_set_interrupt_enable(dsi); - mtk_dsi_lane_ready(dsi); - mtk_dsi_clk_hs_mode(dsi, 1); return 0; err_disable_engine_clk: @@ -858,6 +856,9 @@ static void mtk_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge, ret = mtk_dsi_poweron(dsi); if (ret < 0) drm_err(drm, "failed to power on dsi\n"); + + mtk_dsi_lane_ready(dsi); + mtk_dsi_clk_hs_mode(dsi, 1); } static void mtk_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge, -- cgit v1.2.3 From 533e3469a57996905cdb95f178e7efe38c21aeb2 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 22 Jul 2026 00:22:42 +0900 Subject: drm/mediatek: ovl_adaptor: balance component registrations The OVL adaptor registers both an aggregate driver for its child devices and a component for the main DRM aggregate. Probe currently ignores an error from registering the child aggregate and leaves that aggregate registered if registering the DRM component fails. The remove callback also leaves the DRM component registered. These imbalances can leave component framework entries referring to a device whose probe failed or whose driver has been detached. The aggregate unbind callback also fails to undo component_bind_all(), leaving its child components marked as bound when the aggregate is removed. Check the aggregate registration result, unwind it when the component registration fails, and unregister the component before the aggregate on remove. Keep runtime PM enabled until both framework registrations have been removed, and unbind all child components from the aggregate unbind callback. Fixes: 453c3364632a ("drm/mediatek: Add ovl_adaptor support for MT8195") Cc: stable@vger.kernel.org # 6.4+ Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260721152242.47138-1-mhun512@gmail.com/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c index c0af3e3b51d5..6a259872b0a9 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c @@ -625,6 +625,7 @@ static void mtk_disp_ovl_adaptor_master_unbind(struct device *dev) struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev); priv->children_bound = false; + component_unbind_all(dev, priv->mmsys_dev); } static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = { @@ -651,12 +652,15 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev) priv->mmsys_dev = pdev->dev.platform_data; - component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match); + ret = component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match); + if (ret) + return dev_err_probe(dev, ret, "Failed to add component master\n"); pm_runtime_enable(dev); ret = component_add(dev, &mtk_disp_ovl_adaptor_comp_ops); if (ret != 0) { + component_master_del(dev, &mtk_disp_ovl_adaptor_master_ops); pm_runtime_disable(dev); return dev_err_probe(dev, ret, "Failed to add component\n"); } @@ -666,6 +670,7 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev) static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev) { + component_del(&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops); component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops); pm_runtime_disable(&pdev->dev); } -- cgit v1.2.3 From 8891e39e89042e285fd82fdde325d1311ec750a1 Mon Sep 17 00:00:00 2001 From: Alexander Kaplan Date: Sat, 18 Jul 2026 12:52:07 +0200 Subject: drm/i915/dp: Ignore the sink's DSC max FRL rate without a PCON DSC encoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_dp_hdmi_sink_max_frl() limits the sink's max FRL rate by its DSC max FRL rate whenever the sink supports DSC 1.2. However, the DSC max FRL rate (HF-VSDB DSC_Max_FRL_Rate) only applies to compressed video transport, which requires a DSC 1.2 encoder in the PCON (configured via intel_dp_pcon_dsc_configure()). Without such an encoder the HDMI link always carries uncompressed video, for which the regular Max_FRL_Rate is the correct limit. Applying the DSC limit unconditionally trains the FRL link at a lower rate than both the PCON and the sink support. E.g. an LG OLED G4 (Max_FRL_Rate 48 Gbps, DSC_Max_FRL_Rate 24 Gbps) behind a Synaptics VMM7100 PCON (PCON max FRL bw 48 Gbps, no DSC encoder): Sink max rate from EDID = 24 Gbps FRL trained with : 24 Gbps while Windows/macOS train the same hardware at 40/48 Gbps. The too low FRL rate needlessly constrains the formats available to the sink. Only apply the sink's DSC max FRL rate if the PCON has a DSC 1.2 encoder, matching the gate in intel_dp_pcon_dsc_configure(). PCONs with a DSC encoder keep the current conservative behavior, since the link is trained once and compressed transport may be used for any subsequent mode. With this the setup above trains at 48 Gbps. Tested on PTL (xe) with the above PCON/sink combo. Fixes: 10fec80b48c5 ("drm/i915/display: Configure PCON for DSC1.1 to DSC1.2 encoding") Cc: Ankit Nautiyal Cc: Ville Syrjälä Reviewed-by: Ankit Nautiyal Signed-off-by: Alexander Kaplan Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260718105207.5565-3-alexander.kaplan@sms-medipool.de (cherry picked from commit 71b57dd92f94569dca4bdf883fbd8ca5d4ed4bae) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_dp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 7ff5712f8b19..5733d2e7ac7f 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4242,7 +4242,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) rate_per_lane = info->hdmi.max_frl_rate_per_lane; max_frl_rate = max_lanes * rate_per_lane; - if (info->hdmi.dsc_cap.v_1p2) { + /* + * The sink's DSC max FRL rate only applies to compressed video + * transport, which requires a DSC 1.2 encoder in the PCON. Without + * one the HDMI link always carries uncompressed video, for which + * the regular max FRL rate is the limit. + */ + if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) && + info->hdmi.dsc_cap.v_1p2) { max_dsc_lanes = info->hdmi.dsc_cap.max_lanes; dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane; if (max_dsc_lanes && dsc_rate_per_lane) -- cgit v1.2.3 From c256bd486855d8948aff29e0c97d50712da4e85e Mon Sep 17 00:00:00 2001 From: Brajesh Gupta Date: Fri, 24 Jul 2026 20:12:12 +0530 Subject: drm/imagination: Update the trace point pvr_job_submit_fw() Trace point pvr_job_submit_fw() is used to trace job submission to the FW. Currently it is recorded when a command is written to the Client circular buffer. Move trace recording after writing command to the Kernel circular buffer to better represent command submission to the FW. Fixes: c1079aebb4de ("drm/imagination: Add support for trace points") Signed-off-by: Brajesh Gupta Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260724-b4-tracepoint-fix-v3-1-8f8e5e8179d3@imgtec.com Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_queue.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 941c017399fc..d13726401d92 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -726,8 +726,6 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) cmd->partial_render_geom_frag_fence.value = job->done_fence->seqno - 1; } - trace_pvr_job_submit_fw(job); - /* Submit job to FW */ pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd, job->id, job->id); @@ -802,6 +800,9 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job) job->hwrt, frag_job->fw_ccb_cmd_type == ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR); + + trace_pvr_job_submit_fw(geom_job); + trace_pvr_job_submit_fw(frag_job); } else { struct pvr_queue *queue = container_of(job->base.sched, struct pvr_queue, scheduler); @@ -809,6 +810,8 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job) pvr_cccb_send_kccb_kick(pvr_dev, &queue->cccb, pvr_context_get_fw_addr(job->ctx) + queue->ctx_offset, job->hwrt); + + trace_pvr_job_submit_fw(job); } return dma_fence_get(job->done_fence); -- cgit v1.2.3 From 537601771adb97a603503bc7837d39eaa44023e1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 Jul 2026 15:58:14 -0300 Subject: ASoC: dt-bindings: realtek,rt5640: Make interrupts optional The RT5640 GPIO1/IRQ pin can be configured either as GPIO1 or as the codec interrupt output. Some boards, such as the Firefly-RK3399, do not connect the codec interrupt output. This causes the following binding validation warning: 'interrupts' is a required property Make the interrupts property optional to support such hardware configurations. Signed-off-by: Fabio Estevam Link: https://patch.msgid.link/20260727185814.2599488-1-festevam@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/realtek,rt5640.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml index 2eb631950963..922fe161f4d7 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml @@ -135,7 +135,6 @@ properties: required: - compatible - reg - - interrupts unevaluatedProperties: false -- cgit v1.2.3 From d94f82d57e7a86def6946f22b34eb53f96628f8a Mon Sep 17 00:00:00 2001 From: Himal Prasad Ghimiray Date: Tue, 28 Jul 2026 11:29:17 +0530 Subject: drm/xe/pt: check no-DMA huge-pte cases before DMA segment test On a non-range clear, curs.size is never set, so the segment test (next - va_curs_start > curs->size) returns false for every level > 0 before the clear_pt short-circuit is reached. The clear then descends to level 0 instead of forming a huge zero-leaf, wasting page tables and risking -ENOMEM on unbind. Move the null-VMA, purged-BO and clear_pt short-circuits above the curs->size test. The bind path always sets curs.size, so it is unaffected. v2 - Also set curs.size on the clear path so the cursor stays meaningful during the walk. clear_pt is only reached with range == NULL, so assert that invariant. (Matthew Brost) Cc: Matthew Brost Fixes: 5b658b7e89c3 ("drm/xe: Clear scratch page on vm_bind") Reported-by: Sashiko Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260728055916.593707-2-himal.prasad.ghimiray@intel.com Signed-off-by: Himal Prasad Ghimiray (cherry picked from commit 04eeeb45cb61b8a3e9d785003457e550c920ba49) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pt.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 39c9c8f0ea2d..d0a1e6c2b55f 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -443,10 +443,6 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, if (!xe_pt_covers(addr, next, level, &xe_walk->base)) return false; - /* Does the DMA segment cover the whole pte? */ - if (next - xe_walk->va_curs_start > xe_walk->curs->size) - return false; - /* null VMA's and purged BO's do not have dma addresses */ if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) return true; @@ -455,6 +451,10 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, if (xe_walk->clear_pt) return true; + /* Does the DMA segment cover the whole pte? */ + if (next - xe_walk->va_curs_start > xe_walk->curs->size) + return false; + /* Is the DMA address huge PTE size aligned? */ size = next - addr; dma = addr - xe_walk->va_curs_start + xe_res_dma(xe_walk->curs); @@ -775,8 +775,11 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, } xe_walk.needs_64K = (vm->flags & XE_VM_FLAG_64K); - if (clear_pt) + if (clear_pt) { + xe_assert(xe, !range); + curs.size = xe_vma_size(vma); goto walk_pt; + } if (vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) { xe_walk.default_vram_pte = xe_atomic_for_vram(vm, vma) ? XE_USM_PPGTT_PTE_AE : 0; -- cgit v1.2.3 From 8cf2f40ceb85047ad8a84dffae3bebc9fed18216 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Wed, 29 Jul 2026 08:27:33 +0900 Subject: fprobe: Fix module reference count leak on error in register_fprobe() In register_fprobe(), get_ips_from_filter() resolves target function addresses and increments module reference counts via try_module_get() for symbols in kernel modules. If get_ips_from_filter() fails on the second pass and returns an error, register_fprobe() returned directly without releasing module references acquired up to that point. Fix this by ensuring the cleanup loop executing module_put() runs even when get_ips_from_filter() returns a negative error. Link: https://lore.kernel.org/all/178528125360.101985.4144133640239273153.stgit@devnote2/ Fixes: d24fa977eec5 ("tracing: fprobe: Fix to lock module while registering fprobe") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) --- kernel/trace/fprobe.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index f215990b9061..f681015413b8 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -961,10 +961,8 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter return -ENOMEM; ret = get_ips_from_filter(filter, notfilter, addrs, mods, num); - if (ret < 0) - return ret; - - ret = register_fprobe_ips(fp, addrs, ret); + if (ret >= 0) + ret = register_fprobe_ips(fp, addrs, ret); for (int i = 0; i < num; i++) { if (mods[i]) -- cgit v1.2.3 From 6e9f539e4f01153651dd77609b5ccadd44b74df8 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 29 Jul 2026 15:41:30 +0200 Subject: ovpn: add missing rtnl_link_ops->get_size callback ovpn_fill_info emits IFLA_OVPN_MODE inside IFLA_INFO_DATA, but ovpn_link_ops does not provide a get_size callback. Consequently, rtnetlink's size estimate for ovpn link messages does not include the nested mode attribute. Available skb tailroom may hide this mismatch. When the remaining space is insufficient, however, ovpn_fill_info returns -EMSGSIZE and message construction fails. Add the callback and account for IFLA_OVPN_MODE. Fixes: c2d950c4672a ("ovpn: add basic interface creation/destruction/management routines") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 9993c1dfe471..9d9a0ff690d6 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -210,6 +210,12 @@ static int ovpn_newlink(struct net_device *dev, return register_netdevice(dev); } +static size_t ovpn_get_size(const struct net_device *dev) +{ + /* IFLA_OVPN_MODE */ + return nla_total_size(sizeof(u8)); +} + static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct ovpn_priv *ovpn = netdev_priv(dev); @@ -228,6 +234,7 @@ static struct rtnl_link_ops ovpn_link_ops = { .policy = ovpn_policy, .maxtype = IFLA_OVPN_MAX, .newlink = ovpn_newlink, + .get_size = ovpn_get_size, .fill_info = ovpn_fill_info, }; -- cgit v1.2.3 From b921b8613790a3f9e78ab64017fa7149ef0b750c Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Mon, 20 Jul 2026 13:49:17 +0200 Subject: drm/panthor: validate firmware interface structure sizes iface_fw_to_cpu_addr() only checks that the firmware-provided MCU virtual address points inside the shared section. The returned pointer is later used as a full firmware interface structure, so accepting an address near the end of the shared section can still lead to out-of-bounds accesses. Pass the expected object size to iface_fw_to_cpu_addr() and reject ranges that do not fit entirely in the shared section. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Steven Price Signed-off-by: Steven Price Link: https://patch.msgid.link/20260720114918.15973-1-osama.abdelkader@gmail.com --- drivers/gpu/drm/panthor/panthor_fw.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 0623d37ac617..e45933e333b5 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -864,18 +864,24 @@ out: * iface_fw_to_cpu_addr() - Turn an MCU address into a CPU address * @ptdev: Device. * @mcu_va: MCU address. + * @size: Size of the object pointed to by @mcu_va. * - * Return: NULL if the address is not part of the shared section, non-NULL otherwise. + * Return: NULL if the object is not part of the shared section, non-NULL otherwise. */ -static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va) +static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va, size_t size) { u64 shared_mem_start = panthor_kernel_bo_gpuva(ptdev->fw->shared_section->mem); - u64 shared_mem_end = shared_mem_start + - panthor_kernel_bo_size(ptdev->fw->shared_section->mem); - if (mcu_va < shared_mem_start || mcu_va >= shared_mem_end) + size_t shared_mem_size = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); + u64 offset; + + if (mcu_va < shared_mem_start) + return NULL; + + offset = mcu_va - shared_mem_start; + if (offset > shared_mem_size || size > shared_mem_size - offset) return NULL; - return ptdev->fw->shared_section->mem->kmap + (mcu_va - shared_mem_start); + return ptdev->fw->shared_section->mem->kmap + offset; } static int panthor_init_cs_iface(struct panthor_device *ptdev, @@ -897,8 +903,10 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev, spin_lock_init(&cs_iface->lock); cs_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset; - cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va); - cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va); + cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va, + sizeof(*cs_iface->input)); + cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va, + sizeof(*cs_iface->output)); if (!cs_iface->input || !cs_iface->output) { drm_err(&ptdev->base, "Invalid stream control interface input/output VA"); @@ -948,8 +956,10 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev, spin_lock_init(&csg_iface->lock); csg_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset; - csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va); - csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va); + csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va, + sizeof(*csg_iface->input)); + csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va, + sizeof(*csg_iface->output)); if (csg_iface->control->stream_num < MIN_CS_PER_CSG || csg_iface->control->stream_num > MAX_CS_PER_CSG) @@ -1006,8 +1016,10 @@ static int panthor_fw_init_ifaces(struct panthor_device *ptdev) return -EINVAL; } - glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va); - glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va); + glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va, + sizeof(*glb_iface->input)); + glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va, + sizeof(*glb_iface->output)); if (!glb_iface->input || !glb_iface->output) { drm_err(&ptdev->base, "Invalid global control interface input/output VA"); return -EINVAL; -- cgit v1.2.3 From a7c28483fd57dd0e1487024af70622315320774b Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Jul 2026 16:50:59 +0100 Subject: arm64: tegra: Add EL2 virtual timer interrupt for Tegra194 Commit d87773de9efe ("clocksource/drivers/arm_arch_timer: Default to EL2 virtual timer when running VHE") updated the ARM arch timer driver to use the virtual timer by default if the CPU is running at EL2 with VHE enabled. If the CPU is running at EL2 with VHE enabled but there is no interrupt provided for the virtual timer, then the following warning is displayed: arch_timer: [Firmware Bug]: VHE-capable CPU without EL2 virtual timer interrupt This warning is observed on Tegra194 platforms. Tegra194 SoC includes NVIDIA Carmel ARM v8.2 CPUs and support an EL2 virtual timer. Fix the above warning by adding the PPI for the EL2 virtual timer interrupt for Tegra194. Fixes: 5425fb15d8ee ("arm64: tegra: Add Tegra194 chip device tree") Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- arch/arm64/boot/dts/nvidia/tegra194.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi index eb7e3c666df6..5e510f77b538 100644 --- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi @@ -3176,6 +3176,8 @@ , , + ; interrupt-parent = <&gic>; always-on; -- cgit v1.2.3 From f27f6976ea269219c1259a7c2f8c6dfe782540a3 Mon Sep 17 00:00:00 2001 From: Hongyan Xu Date: Wed, 29 Jul 2026 18:01:16 +0800 Subject: hwmon: (npcm750-pwm-fan): stop fan timer on device detach When a fan tach channel is present, npcm7xx_pwm_fan_probe() starts fan_timer. The timer callback polls tach state and rearms the timer, but the driver has no remove callback or devm cleanup action to stop it. On device detach, the devm-managed driver data and I/O mappings can be released while the timer is still pending or running. Register a devm cleanup action before starting the timer and shut the timer down synchronously from that action. This issue was found by a static analysis tool. Fixes: f1fd4a4db777 ("hwmon: Add NPCM7xx PWM and Fan driver") Cc: stable@vger.kernel.org Signed-off-by: Hongyan Xu Link: https://lore.kernel.org/r/20260729100116.790-1-getshell@seu.edu.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/npcm750-pwm-fan.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index aea0b8659f5f..6ff2a6bc06c1 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -358,6 +358,11 @@ static void npcm7xx_fan_polling(struct timer_list *t) add_timer(&data->fan_timer); } +static void npcm7xx_fan_cleanup(void *timer) +{ + timer_shutdown_sync(timer); +} + static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data, u8 fan, u8 cmp, u8 fan_id, u8 flag_int, u8 flag_mode, u8 flag_clear) @@ -1020,6 +1025,12 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS); timer_setup(&data->fan_timer, npcm7xx_fan_polling, 0); + ret = devm_add_action_or_reset(dev, + npcm7xx_fan_cleanup, + &data->fan_timer); + if (ret) + return ret; + add_timer(&data->fan_timer); break; } -- cgit v1.2.3 From 779e0eb18c774b81a462a6ee89cfbc9eb2d3cee5 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Wed, 29 Jul 2026 11:43:28 -0600 Subject: riscv: time: Add missing __iomem in get_cycles() and get_cycles_hi() __iomem is missing while calling readl_relaxed() in get_cycles() and get_cycles_hi() and sparse complains. Add __iomem to silence the sparse warnings. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607160619.14G8GHp5-lkp@intel.com/ Signed-off-by: Nam Cao Link: https://patch.msgid.link/20260716053319.2178937-1-namcao@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/timex.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/timex.h b/arch/riscv/include/asm/timex.h index a06697846e69..d41acfb3959d 100644 --- a/arch/riscv/include/asm/timex.h +++ b/arch/riscv/include/asm/timex.h @@ -22,13 +22,13 @@ static inline cycles_t get_cycles(void) #else /* !CONFIG_64BIT */ static inline u32 get_cycles(void) { - return readl_relaxed(((u32 *)clint_time_val)); + return readl_relaxed(((u32 __iomem *)clint_time_val)); } #define get_cycles get_cycles static inline u32 get_cycles_hi(void) { - return readl_relaxed(((u32 *)clint_time_val) + 1); + return readl_relaxed(((u32 __iomem *)clint_time_val) + 1); } #define get_cycles_hi get_cycles_hi #endif /* CONFIG_64BIT */ -- cgit v1.2.3 From 9a22a1542ca03381dcbf000d7a264cbee61e7203 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Wed, 29 Jul 2026 11:43:28 -0600 Subject: riscv/sifive: remove warning in errata The alternative patching of sifive vendor extensions also calls the sifive_errata_patch_func(), but the patch_id of the vendor extension (ext + RISCV_VENDOR_EXT_ALTERNATIVES_BASE) is always larger than ERRATA_SIFIVE_NUMBER. Remove this unnecessary warning. Signed-off-by: Yong-Xuan Wang Link: https://patch.msgid.link/20260503-sifive_errata-v1-1-6f12a81bc267@sifive.com [pjw@kernel.org: drop unnecessary braces] Signed-off-by: Paul Walmsley --- arch/riscv/errata/sifive/errata.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/riscv/errata/sifive/errata.c b/arch/riscv/errata/sifive/errata.c index d0c61f86cba3..df80c9614df1 100644 --- a/arch/riscv/errata/sifive/errata.c +++ b/arch/riscv/errata/sifive/errata.c @@ -93,10 +93,8 @@ void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, for (alt = begin; alt < end; alt++) { if (alt->vendor_id != SIFIVE_VENDOR_ID) continue; - if (alt->patch_id >= ERRATA_SIFIVE_NUMBER) { - WARN(1, "This errata id:%d is not in kernel errata list", alt->patch_id); + if (alt->patch_id >= ERRATA_SIFIVE_NUMBER) continue; - } tmp = (1U << alt->patch_id); if (cpu_req_errata & tmp) { -- cgit v1.2.3 From cfca5a48b03fbd33c8cb84cb73ee2e34467f3a33 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Mon, 27 Jul 2026 01:08:44 -0700 Subject: riscv: mm: fix SWIOTLB initialization for systems with DRAM above 4GB On RISC-V platforms where the entire physical memory (DRAM) resides above the 32-bit address space (i.e., above dma32_phys_limit), the current SWIOTLB initialization logic fails. This patch addresses two interconnected issues on such platforms: 1. Incorrect 32-bit DMA bounce assumption: The existing condition `max_pfn > PFN_DOWN(dma32_phys_limit)` assumes that a 32-bit DMA bounce buffer is required simply because the maximum PFN exceeds the 32-bit limit. However, if all DRAM starts above 4GB, no memory exists below the limit to satisfy this allocation. Fix this by adding a check to ensure `memblock_start_of_DRAM()` is actually below the 32-bit limit before enforcing 32-bit SWIOTLB. 2. kmalloc() bounce buffer allocation failure on non-coherent systems: For non-coherent DMA, kmalloc() buffers whose sizes are not cache-line-aligned still require bouncing, even if 32-bit DMA bouncing is skipped. Without the `SWIOTLB_ANY` flag, swiotlb_init() defaults to allocating from low memory, which fails completely when DRAM only exists in high memory. By appending `SWIOTLB_ANY` to swiotlb_flags, the allocator is permitted to allocate this bounce buffer from high memory. With this patch, systems with non-coherent DMA and DRAM entirely above 4GB can successfully map the software IO TLB in high memory and boot normally. Tested-by: Anirudh Srinivasan Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260727-fix-riscv-swiotlb-v3-1-59479b23736c@linux.dev Reviewed-by: Drew Fustini Signed-off-by: Paul Walmsley --- arch/riscv/mm/init.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 3e450890be07..395ed9ab578e 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -164,7 +164,9 @@ static void print_vm_layout(void) { } void __init arch_mm_preinit(void) { - bool swiotlb = max_pfn > PFN_DOWN(dma32_phys_limit); + bool swiotlb = max_pfn > PFN_DOWN(dma32_phys_limit) && + memblock_start_of_DRAM() < dma32_phys_limit; + unsigned int swiotlb_flags = SWIOTLB_VERBOSE; #ifdef CONFIG_FLATMEM BUG_ON(!mem_map); #endif /* CONFIG_FLATMEM */ @@ -172,17 +174,22 @@ void __init arch_mm_preinit(void) if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && !swiotlb && dma_cache_alignment != 1) { /* - * If no bouncing needed for ZONE_DMA, allocate 1MB swiotlb - * buffer per 1GB of RAM for kmalloc() bouncing on - * non-coherent platforms. + * No 32-bit DMA bouncing needed (either all DRAM is within + * the 32-bit limit, or it all starts above it), but + * kmalloc() buffers whose sizes are not cache-line-aligned + * still require bouncing for non-coherent DMA. Use + * SWIOTLB_ANY so that the buffer can be allocated from high + * memory when DRAM starts above dma32_phys_limit. Allocate + * ~1 MB per 1 GB of RAM. */ unsigned long size = DIV_ROUND_UP(memblock_phys_mem_size(), 1024); swiotlb_adjust_size(min(swiotlb_size_or_default(), size)); swiotlb = true; + swiotlb_flags |= SWIOTLB_ANY; } - swiotlb_init(swiotlb, SWIOTLB_VERBOSE); + swiotlb_init(swiotlb, swiotlb_flags); print_vm_layout(); } -- cgit v1.2.3 From c786d2bdf1f3964deee192ad942dee2a741c1e2c Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 28 Jul 2026 21:49:51 +0900 Subject: tracing/mmiotrace: Reset dropped_count in mmio_reset_data() mmio_reset_data() is called during tracer initialization, reset, and start. While it resets overrun_detected and prev_overruns, it neglects to reset dropped_count. Consequently, dropped event counts from prior tracing sessions persist in dropped_count and corrupt overrun reports in subsequent runs. Fix this by explicitly calling atomic_set(&dropped_count, 0) in mmio_reset_data(). Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_mmiotrace.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index b88b8d9923ad..e064ba3f28cb 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) { overrun_detected = false; prev_overruns = 0; + atomic_set(&dropped_count, 0); tracing_reset_online_cpus(&tr->array_buffer); } -- cgit v1.2.3 From 12b80cdbc54cf615b4717a4e8180063408091ea2 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 28 Jul 2026 21:50:00 +0900 Subject: tracing/mmiotrace: Add NULL check for mmio_trace_array in logging functions mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map(). If these functions are invoked while mmio_trace_array is NULL (e.g. before initialization or after disabled), accessing tr->array_buffer.buffer will result in a NULL pointer dereference crash. Fix this by adding an explicit NULL check for tr at the beginning of __trace_mmiotrace_rw() and __trace_mmiotrace_map(). Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2 Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_mmiotrace.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index e064ba3f28cb..df8692c2dea8 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -294,11 +294,15 @@ device_initcall(init_mmio_trace); static void __trace_mmiotrace_rw(struct trace_array *tr, struct mmiotrace_rw *rw) { - struct trace_buffer *buffer = tr->array_buffer.buffer; + struct trace_buffer *buffer; struct ring_buffer_event *event; struct trace_mmiotrace_rw *entry; unsigned int trace_ctx; + if (!tr) + return; + + buffer = tr->array_buffer.buffer; trace_ctx = tracing_gen_ctx_flags(0); event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW, sizeof(*entry), trace_ctx); @@ -321,11 +325,15 @@ void mmio_trace_rw(struct mmiotrace_rw *rw) static void __trace_mmiotrace_map(struct trace_array *tr, struct mmiotrace_map *map) { - struct trace_buffer *buffer = tr->array_buffer.buffer; + struct trace_buffer *buffer; struct ring_buffer_event *event; struct trace_mmiotrace_map *entry; unsigned int trace_ctx; + if (!tr) + return; + + buffer = tr->array_buffer.buffer; trace_ctx = tracing_gen_ctx_flags(0); event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP, sizeof(*entry), trace_ctx); -- cgit v1.2.3 From ac8719969e6c3c54e939834df812bc41f25453cf Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Wed, 29 Jul 2026 09:27:58 +0900 Subject: tracing: Check return value of __register_event() in trace_module_add_events() trace_module_add_events() ignores the return value of __register_event() and unconditionally calls __add_event_to_tracers() for each event. If __register_event() fails (for example, if event_init() fails), the trace_event_call is not added to ftrace_events list, but __add_event_to_tracers() still creates a trace_event_file pointing to it. If module loading subsequently fails and module memory is freed, tracing state retains a stale trace_event_call pointer in trace_event_file, leading to a use-after-free when tracefs or tracing subsystem operations are later executed. Fix this by checking the return value of __register_event() and only calling __add_event_to_tracers() if event registration succeeded. Fixes: ae63b31e4d0e ("tracing: Separate out trace events from global variables") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178528487878.124250.14170824576025743236.stgit@devnote2 Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 956692856fa8..c01b10b99f67 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -3933,8 +3933,8 @@ static void trace_module_add_events(struct module *mod) end = mod->trace_events + mod->num_trace_events; for_each_event(call, start, end) { - __register_event(*call, mod); - __add_event_to_tracers(*call); + if (!__register_event(*call, mod)) + __add_event_to_tracers(*call); } update_cache_events(mod); -- cgit v1.2.3 From c22c7b735f9810ad276014f788f9aa5c879ec238 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Wed, 29 Jul 2026 09:28:07 +0900 Subject: tracing/filters: Fix false positive match in regex_match_full() regex_match_full() calls strncmp(str, r->pattern, len) where len is the target field buffer size. When len is smaller than r->len (the filter pattern length), strncmp() checks only len bytes of r->pattern against str. If those len bytes match, strncmp() returns 0, resulting in a false-positive match where a shorter string in a fixed-size field matches a longer filter pattern. For example, a 4-byte static string field containing "abcd" matched the filter pattern "abcdefgh" because strncmp("abcd", "abcdefgh", 4) returned 0. In this case, @len does NOT include '\0' because it is fixed-size array. Fix this by returning 0 (no match) early when len < r->len. Fixes: 1889d20922d1 ("tracing/filters: Provide basic regex support") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178528488779.124250.5571741156199253769.stgit@devnote2 Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_events_filter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 6385cd662d8d..2b46ca536045 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1027,6 +1027,9 @@ static int regex_match_full(char *str, struct regex *r, int len) if (!len) return strcmp(str, r->pattern) == 0; + if (len < r->len) + return 0; + return strncmp(str, r->pattern, len) == 0; } -- cgit v1.2.3 From f51fed61eea0daba2f95f1a6074085e4cd513c7b Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Fri, 12 Jun 2026 11:24:43 -0500 Subject: riscv: drop __init from vec_check_unaligned_access_speed_all_cpus This function runs within a kthread and need not necessarily finish before system finishes boot and free_initmem() unmaps the .init.text section. This function makes calls to SBI for probing unaligned access speed, and if this is slow for some reason (say some debug prints were added to SBI), the kthread can still be running at this point and result in an instruction page fault when trying to fetch from the freed region. [ 25.642087] Unable to handle kernel paging request at virtual address ffffffff80a04ef8 [ 25.646694] Current vec_check_unali pgtable: 4K pagesize, 48-bit VAs, pgdp=0x00004000316e9000 [ 25.653170] [ffffffff80a04ef8] pgd=000010004be7e401, p4d=000010004be7e401, pud=000010004be7e001, pmd=000010000c3000e3 [ 25.661244] Oops [#1] [ 25.662997] Modules linked in: [ 25.665357] CPU: 3 UID: 0 PID: 42 Comm: vec_check_unali Not tainted 7.0.0-tt-blackhole-asrinivasan-00007-g30ff73f18211 #570 PREEMPTLAZY [ 25.674669] Hardware name: Tenstorrent Blackhole (DT) [ 25.678545] epc : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.683458] ra : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.688372] epc : ffffffff80a04ef8 ra : ffffffff80a04ef8 sp : ffff8f8000203e20 [ 25.693874] gp : ffffffff814dc168 tp : ffffaf8001ad9900 t0 : 0000000000000000 [ 25.699401] t1 : fffffffffffffff0 t2 : ffffaf8001ad9a10 s0 : ffff8f8000203e30 [ 25.704912] s1 : ffffaf80018dc780 a0 : 0000000000000000 a1 : 0000000000000002 [ 25.710407] a2 : 00000000000001f0 a3 : 0000000000000018 a4 : 0000000000000000 [ 25.715917] a5 : 0000000000000000 a6 : ffffaf8001c03d98 a7 : ffffaf8001c03e30 [ 25.721419] s2 : ffff8f8000023c98 s3 : ffffaf8001aa1240 s4 : ffffffff80a04ee0 [ 25.726937] s5 : 0000000000000000 s6 : 0000000000000000 s7 : 0000000000000000 [ 25.732450] s8 : 0000000000000000 s9 : 0000000000000000 s10: 0000000000000000 [ 25.737944] s11: 0000000000000000 t3 : 0000000000000002 t4 : 0000000000000402 [ 25.743481] t5 : 0000000000000040 t6 : 0000000000000004 ssp : 0000000000000000 [ 25.749024] status: 0000000200000120 badaddr: ffffffff80a04ef8 cause: 000000000000000c [ 25.755060] [] vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.760964] [] kthread+0xd8/0xfc [ 25.764660] [] ret_from_fork_kernel+0x18/0x1c4 [ 25.769220] [] ret_from_fork_kernel_asm+0x16/0x18 [ 25.774018] Code: cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc (cccc) cccc Drop __init from its signature so that this doesn't happen. Fixes: a00e022be531 ("riscv: Annotate unaligned access init functions") Signed-off-by: Anirudh Srinivasan Assisted-by: Claude:claude-opus-4-6 Link: https://patch.msgid.link/20260612-vec_unaligned_drop_init-v1-1-df969210ae34@oss.tenstorrent.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/unaligned_access_speed.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index bb57eb5d19df..5a5aa22124e7 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -289,7 +289,7 @@ free: } /* Measure unaligned access speed on all CPUs present at boot in parallel. */ -static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) { schedule_on_each_cpu(check_vector_unaligned_access); riscv_hwprobe_complete_async_probe(); @@ -297,7 +297,7 @@ static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __alway return 0; } #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ -static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) { return 0; } -- cgit v1.2.3 From 52075128273ace53e6254e37899a47d40d4baf45 Mon Sep 17 00:00:00 2001 From: Pratik Vishwakarma Date: Wed, 29 Jul 2026 05:54:59 +0000 Subject: x86/CPU/AMD: Carve out a Zen5 models range Family 0x1a, model 0xd0..0xd7 belongs to the Zen5 generation. Carve it out from the larger, Zen6 range where former doesn't belong. [ bp: Rewrite commit message, add tags. ] Fixes: b5f53e6d3d32 ("x86/CPU/AMD: Add more Zen6 models") Signed-off-by: Pratik Vishwakarma Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/20260729055459.15904-1-Pratik.Vishwakarma@amd.com --- arch/x86/kernel/cpu/amd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 487ac147e11f..d61df70d6875 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -515,11 +515,13 @@ static void bsp_init_amd(struct cpuinfo_x86 *c) case 0x00 ... 0x2f: case 0x40 ... 0x4f: case 0x60 ... 0x7f: + case 0xd0 ... 0xd7: setup_force_cpu_cap(X86_FEATURE_ZEN5); break; case 0x50 ... 0x5f: case 0x80 ... 0xaf: - case 0xc0 ... 0xef: + case 0xc0 ... 0xcf: + case 0xd8 ... 0xef: setup_force_cpu_cap(X86_FEATURE_ZEN6); break; default: -- cgit v1.2.3 From f865c143629d4094866a811dba5f329250bad486 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Sat, 18 Jul 2026 13:09:22 +0800 Subject: audit: fix potential integer overflow in audit_log_n_string() audit_log_n_string() computes new_len as "slen + 3" (enclosing quotes plus the NUL terminator) and stores it into an int, while slen is a size_t. For a sufficiently large slen the addition can overflow and/or the result be truncated when assigned to the int new_len, so the "new_len > avail" check can be bypassed and the subsequent memcpy(ptr, string, slen) can write past the skb tail. This is the same class of bug that was fixed for the hex sibling in commit 65dfde57d1e2 ("audit: fix potential integer overflow in audit_log_n_hex()"); both helpers are reached through audit_log_n_untrustedstring() with the same length source. Make new_len a size_t and use check_add_overflow() to catch the overflow, mirroring the audit_log_n_hex() fix. No functional change for the in-tree callers, which all pass bounded lengths. Cc: stable@vger.kernel.org Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings") Signed-off-by: Zhan Xusheng Signed-off-by: Paul Moore --- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 562476937fa7..9412af9144bc 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -2120,7 +2120,8 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, void audit_log_n_string(struct audit_buffer *ab, const char *string, size_t slen) { - int avail, new_len; + int avail; + size_t new_len; unsigned char *ptr; struct sk_buff *skb; @@ -2130,7 +2131,13 @@ void audit_log_n_string(struct audit_buffer *ab, const char *string, BUG_ON(!ab->skb); skb = ab->skb; avail = skb_tailroom(skb); - new_len = slen + 3; /* enclosing quotes + null terminator */ + + /* enclosing quotes + null terminator */ + if (check_add_overflow(slen, 3, &new_len)) { + audit_log_format(ab, "?"); + return; + } + if (new_len > avail) { avail = audit_expand(ab, new_len); if (!avail) -- cgit v1.2.3 From 246df90b5f1a8a6e6abbd2f058b029558720adec Mon Sep 17 00:00:00 2001 From: Luxiao Xu Date: Tue, 21 Jul 2026 23:37:41 +0800 Subject: audit: fix potential use-after-free in audit_del_rule() `audit_del_rule()` destroys `e->rule.exe` via `audit_remove_mark_rule()` before unlinking the rule from RCU-visible filter lists and waiting for a grace period. Concurrent readers in `audit_filter()` and `audit_filter_rules()` still dereference `e->rule.exe`, while the fsnotify mark can be freed on an independent lifetime path. This creates a use-after-free window during rule deletion. Fix this by unlinking the rule from the RCU-visible lists and invoking `synchronize_rcu()` before calling `audit_remove_mark_rule()` (and other rule removal helpers). This ensures that all existing RCU readers have exited the critical section before any underlying resources are destroyed. Cc: stable@vger.kernel.org Fixes: 34d99af52ad4 ("audit: implement audit by executable") Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei Signed-off-by: Paul Moore --- kernel/auditfilter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 4401119b5275..7f791afe5791 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1045,6 +1045,10 @@ int audit_del_rule(struct audit_entry *entry) goto out; } + list_del_rcu(&e->list); + list_del(&e->rule.list); + synchronize_rcu(); + if (e->rule.watch) audit_remove_watch_rule(&e->rule); @@ -1062,8 +1066,6 @@ int audit_del_rule(struct audit_entry *entry) audit_signals--; #endif - list_del_rcu(&e->list); - list_del(&e->rule.list); call_rcu(&e->rcu, audit_free_rule_rcu); out: -- cgit v1.2.3 From d6c075f797a672a6e3bd2fd44aee713801698ec2 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Thu, 18 Jun 2026 02:25:20 +0500 Subject: accel/qaic: use sizeof(*trans_hdr) for transaction length check In encode_message() the per-transaction lower-bound check compares trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer, instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr. Every other length check in this file (encode_message() at the loop guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so this is an inconsistency. On 64-bit builds the pointer and the struct are both 8 bytes, so the check is correct by coincidence and there is no behavioural change. On 32-bit builds the pointer is 4 bytes, which weakens the minimum-length check below the 8-byte header size. Use sizeof(*trans_hdr) so the check validates against the actual transaction header size on all builds. Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()") Signed-off-by: Muhammad Bilal Reviewed-by: Jeff Hugo Signed-off-by: Jeff Hugo Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com --- drivers/accel/qaic/qaic_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index bb94d3556904..50bf3340e49c 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -786,7 +786,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg, break; } trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len); - if (trans_hdr->len < sizeof(trans_hdr) || + if (trans_hdr->len < sizeof(*trans_hdr) || size_add(user_len, trans_hdr->len) > user_msg->len) { ret = -EINVAL; break; -- cgit v1.2.3 From a3ba349af8e1bb7e0efdcd2dd53d69282f18478a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Jul 2026 14:58:38 +0200 Subject: arm64: dts: qcom: eliza: Fix DSI1 phy reference clock rate The DSI PHY CXO clock input is the SoC CXO divided by two. DSI0 already uses correct one, but DSI1 got copy-paste from SM8750, which had same problem and copy-pasted code from SM8650. Wrong clock parent will cause incorrect DSI1 PHY PLL frequencies to be used making the DSI panel non-working, although there is no upstream user of DSI1. Fixes: 159d252ed800 ("arm64: dts: qcom: eliza: Add display (MDSS) with Display CC") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260713125837.727632-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/eliza.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi index 977de44b816e..72b786fec195 100644 --- a/arch/arm64/boot/dts/qcom/eliza.dtsi +++ b/arch/arm64/boot/dts/qcom/eliza.dtsi @@ -2559,7 +2559,7 @@ "dsi_pll"; clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&bi_tcxo_div2>; clock-names = "iface", "ref"; -- cgit v1.2.3 From 7d3aae206663c4e006b25a1c7a20a4029e67da76 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 10 Jul 2026 09:20:51 -0700 Subject: KVM: SVM: Update x2APIC MSR intercepts if AVIC is inhibited while L2 is active Always update x2APIC MSR intercepts for L1 when AVIC is deactivated, even if L2 is active and KVM is using a separate MSR bitmap to run L2. If AVIC is fully enabled prior to running L2, and is then inhibited while L2 is active (for a VM-scoped inhibit), then KVM will run L1 with AVIC disabled, but with x2APIC MSR intercepts disabled, i.e. will allow L1 to read most of the host's APIC state, send arbitrary interrupts, change task priority, and ultimately trivially DoS the host. E.g. sending a self-IPI in L1 on HYPERV_REENLIGHTENMENT_VECTOR, 0xee, with CONFIG_HYPERV=n in the host kernel as a "safe" PoC, yields: Spurious interrupt (vector 0xee) on CPU#425. Acked And hacking KVM to abuse kvm_set_posted_intr_wakeup_handler() to register a handler and WARN on POSTED_INTR_WAKEUP_VECTOR yields: ------------[ cut here ]------------ WARNING: arch/x86/kvm/svm/svm.c:5594 at pi_wakeup_handler+0x9/0x10 [kvm_amd], CPU#156: nested_x2apic_t/316940 CPU: 156 UID: 0 PID: 316940 Comm: nested_x2apic_t Tainted: G S U Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER Hardware name: Google Astoria-Turin/astoria, BIOS 0.20260209.0-0 02/09/2026 RIP: 0010:pi_wakeup_handler+0x9/0x10 [kvm_amd] Call Trace: sysvec_kvm_posted_intr_wakeup_ipi+0x64/0x80 asm_sysvec_kvm_posted_intr_wakeup_ipi+0x1a/0x20 RIP: 0010:vcpu_run+0x1430/0x1e40 [kvm] kvm_arch_vcpu_ioctl_run+0x2c1/0x600 [kvm] kvm_vcpu_ioctl+0x580/0x6b0 [kvm] __se_sys_ioctl+0x6d/0xb0 do_syscall_64+0x10a/0x480 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x46ff4b ---[ end trace 0000000000000000 ]--- Fixes: 091abbf578f9 ("KVM: x86: nSVM: optimize svm_set_x2apic_msr_interception") Cc: stable@vger.kernel.org Cc: Yosry Ahmed Signed-off-by: Sean Christopherson Link: https://patch.msgid.link/20260729213558.639074-1-pbonzini@redhat.com/ Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/avic.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index 58e493a80cb0..16e09a8fe85e 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -240,14 +240,6 @@ static void avic_deactivate_vmcb(struct vcpu_svm *svm) if (!is_sev_es_guest(&svm->vcpu)) svm_set_intercept(svm, INTERCEPT_CR8_WRITE); - /* - * If running nested and the guest uses its own MSR bitmap, there - * is no need to update L0's msr bitmap - */ - if (is_guest_mode(&svm->vcpu) && - vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_MSR_PROT)) - return; - /* Enabling MSR intercept for x2APIC registers */ avic_set_x2apic_msr_interception(svm, true); } -- cgit v1.2.3 From a65f5179d3f0c93da31b450aeb416eaa22f1912b Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 29 Jul 2026 15:56:46 +0800 Subject: drm/amd/pm: hide pp_table sysfs on APUs APUs use firmware-owned DPM tables and do not support replacement through pp_table. Generic callbacks can nevertheless expose the sysfs file and accept an upload before resetting the power management stack. Treat pp_table as unsupported on APUs. Use the same platform check in the get and set paths to hide the file and reject uploads. Fixes: 289921b03fe5 ("drm/amd/powerplay: implement sysfs of pp_table for smu11 (v2)") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit 74f28db2db69777cd2f059d50fe34e365ddd5add) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index 6d1ad4d5b8f0..e5e89294958a 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1183,6 +1183,13 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, return ret; } +static bool amdgpu_dpm_is_pp_table_allowed(struct amdgpu_device *adev) +{ + return !amdgpu_sriov_vf(adev) && + !(adev->flags & AMD_IS_APU) && + !adev->scpm_enabled; +} + int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, size_t size) { @@ -1193,7 +1200,8 @@ int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, if ((!table && size) || (table && !size)) return -EINVAL; - if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) + if (!amdgpu_dpm_is_pp_table_allowed(adev) || + !pp_funcs->get_pp_table) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); @@ -1717,7 +1725,8 @@ int amdgpu_dpm_set_pp_table(struct amdgpu_device *adev, if (!buf || !size) return -EINVAL; - if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled) + if (!amdgpu_dpm_is_pp_table_allowed(adev) || + !pp_funcs->set_pp_table) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); -- cgit v1.2.3 From 8419331e64d92a8de5fc4feef0e305f201fb8b33 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Tue, 28 Jul 2026 13:02:47 -0400 Subject: drm/amd/display: Exit idle optimizations before programming [Why] We need to exit PSR/IPS before programming. Before calling DC for programming in amdgpu_dm_commit_planes(), there's a vblank_control_workqueue flush. This waits for IPS and PSR exit. (See drm_vblank_on/off() > amdgpu_dm_crtc_set_vblank() --queue_work()-> amdgpu_dm_crtc_vblank_control_worker()) Prior to the tagged "Fixes:" change, drm_vblank_get() was called before the workqueue flush. This ordering ensures that PSR exit occurred before programming. After the "Fixes:" change, drm_vblank_get() is called after the workqueue flush, leading to programming while idle optimizations are still active. This can lead to incorrect flip_pending detection used by vblank event delivery. [How] Split the vblank_get() component of `dm_arm_vblank_event()` into `dm_arm_vblank_event_pre_programming()`, which is called before programming. Call it before the vblank_control_workqueue flush. Includes a drive-by cleanup of prepare_flip_isr(): the only caller is dm_arm_vblank_event() and it's simple enough to roll-in. v2: Fix checkpatch formatting warning on drm_arm_vblank_event_pre_programming() arg alignment. Fixes: 48ab86360af1 ("drm/amd/display: check GRPH_FLIP status before sending event") Cc: stable@vger.kernel.org Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141#note_3583205 Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/5527 Assisted-by: Codex:gpt-5.6-sol Assisted-by: Claude:opus-5 Suggested-by: David Weber Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit 05984e29520a28c27f5a2388742c957a6a87ee7a) --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 78 +++++++++++++---------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index eaf19ec843e8..1820547b1dde 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -9903,25 +9903,6 @@ static void remove_stream(struct amdgpu_device *adev, acrtc->enabled = false; } -static void prepare_flip_isr(struct amdgpu_crtc *acrtc) -{ - - assert_spin_locked(&acrtc->base.dev->event_lock); - WARN_ON(acrtc->event); - - acrtc->event = acrtc->base.state->event; - - /* Set the flip status */ - acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; - - /* Mark this event as consumed */ - acrtc->base.state->event = NULL; - - drm_dbg_state(acrtc->base.dev, - "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", - acrtc->crtc_id); -} - static void update_freesync_state_on_stream( struct amdgpu_display_manager *dm, struct dm_crtc_state *new_crtc_state, @@ -10274,17 +10255,47 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc, return; if (pflip_update) { - drm_crtc_vblank_get(&acrtc->base); WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE); - /* Arm flip completion handling and event delivery after programming. */ - prepare_flip_isr(acrtc); + WARN_ON(acrtc->event); + + acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; + acrtc->event = acrtc->base.state->event; + acrtc->base.state->event = NULL; + + drm_dbg_state(acrtc->base.dev, + "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", + acrtc->crtc_id); } else if (cursor_update) { - drm_crtc_vblank_get(&acrtc->base); acrtc->event = acrtc->base.state->event; acrtc->base.state->event = NULL; } } +/** + * dm_arm_vblank_event_pre_programming - Prepare for programming + * @acrtc: The amdgpu CRTC to prepare + * @acrtc_state: The new CRTC state + * @pflip_update: Whether a page flip is being programmed + * @cursor_update: Whether a cursor update is being programmed + * + * Grab a reference on the vblank counter if a page flip or cursor update is to + * be programmed. Do this before programming so the HW is not in any + * idle-optimized state (such as PSR). + */ +static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc, + struct dm_crtc_state *acrtc_state, + bool pflip_update, + bool cursor_update) +{ + assert_spin_locked(&acrtc->base.dev->event_lock); + + if (!acrtc->base.state->event || acrtc_state->active_planes == 0) + return; + + if (pflip_update || cursor_update) + drm_crtc_vblank_get(&acrtc->base); +} + static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, struct drm_device *dev, struct amdgpu_display_manager *dm, @@ -10550,16 +10561,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, } } - /* - * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for - * event delivery. Only GRPH_FLIP handler can send pflip events, and it - * only fires if HW latched to the flip. Maintain legacy behavior by - * arming event before programming. - */ - if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { - scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state, + pflip_present, + cursor_update); + /* + * DCE depends on a combination of GRPH_FLIP, VLINE0, and + * VUPDATE for event delivery. Only GRPH_FLIP handler can send + * pflip events, and it only fires if HW latched to the flip. + * Maintain legacy behavior by arming event before programming. + */ + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { dm_arm_vblank_event(acrtc_attach, acrtc_state, - pflip_present, cursor_update); + pflip_present, cursor_update); } } -- cgit v1.2.3 From d0ecbedd6a70f2fe768d46de740265dc8a4424e3 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 28 Jul 2026 17:01:11 +0800 Subject: ASoC: rt722: reset codec to fix abnormal sound The audio output may become abnormal after a warm reboot from Windows. Reset the codec once during hardware initialization to restore it to a known state and prevent the issue. BugLink: https://github.com/thesofproject/linux/issues/5845 Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260728090111.3676617-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca-sdw.c | 4 ++++ sound/soc/codecs/rt722-sdca.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index e68aa0350a5b..7482a9a8d198 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -113,6 +113,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) case 0x2000090 ... 0x2000094: case 0x20000b1: case 0x20000b4: + case 0x3010000: case 0x3110000: case 0x5300000 ... 0x5300300: case 0x5400002: @@ -196,15 +197,18 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) case 0x2000007: case 0x200000d: case 0x2000019: + case 0x200001a: case 0x2000020: case 0x2000030: case 0x2000046: case 0x2000067: case 0x2000084: case 0x2000086: + case 0x3010000: case 0x3110000: case 0x5800003: case 0x5810000: + case 0x6100008: case 0x44011000 ... 0x440115ff: case 0x44012000: case 0x44012021: diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 1b6729f363fc..6b509eb65d77 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1851,6 +1851,17 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) } } +static void rt722_sdca_reset(struct rt722_sdca_priv *rt722) +{ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_LDO1_CTL, RT722_HIDDEN_REG_SW_RESET, + RT722_HIDDEN_REG_SW_RESET); + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_RESET_CTL, 0x1, 0x1); + if (rt722->hw_vid == RT722_VA) + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_LDO1_CTL, 0xb091); +} + int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); @@ -1888,6 +1899,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) rt722->hw_vid = (val & 0x0f00) >> 8; dev_dbg(&slave->dev, "%s hw_vid=0x%x\n", __func__, rt722->hw_vid); + if (!rt722->first_hw_init) + rt722_sdca_reset(rt722); + rt722_sdca_dmic_preset(rt722); rt722_sdca_amp_preset(rt722); rt722_sdca_jack_preset(rt722); -- cgit v1.2.3 From bab4d538f8485e0d48538fcb82b285df3779278e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 25 Jul 2026 14:57:22 -0700 Subject: ARM: dts: BCM5301X: fix PCIe controller 2 second interrupt PCIe controller 2 has interrupts 0-4 mapping to GIC SPI 138-142. The mapping for interrupt 1 was incorrectly set to 138 due to a copy-paste error. Fix it to 139. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Link: https://lore.kernel.org/r/20260725215722.9323-1-rosenp@gmail.com Fixes: 3b3e35b279be ("ARM: dts: BCM5301X: Relicense AXI interrupts code to the GPL 2.0+ / MIT") Signed-off-by: Florian Fainelli --- arch/arm/boot/dts/broadcom/bcm-ns.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi index 392a25713669..bd52de0faa3e 100644 --- a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi @@ -141,7 +141,7 @@ /* PCIe Controller 2 */ <0x00014000 0 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>, - <0x00014000 1 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>, + <0x00014000 1 &gic GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>, <0x00014000 2 &gic GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>, <0x00014000 3 &gic GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>, <0x00014000 4 &gic GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, -- cgit v1.2.3 From 6de6732c4c784ce64e2457630ca800dfe5efb774 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 28 Jun 2026 16:10:49 -0700 Subject: ARM: dts: BCM5301X: EA9200: fix NVRAM size Fixes: [ 0.182121] WARNING: CPU: 0 PID: 1 at drivers/nvmem/brcm_nvram.c:85 brcm_nvram_probe+0x400/0x480 [ 0.182159] Unexpected (big) NVRAM size: 1056112 B Fixes: 90a77291ac09 ("ARM: dts: BCM5301X: R8000 add NVRAM with MAC address for WAN port") Signed-off-by: Rosen Penev Link: https://lore.kernel.org/r/20260628231049.1248899-1-rosenp@gmail.com Signed-off-by: Florian Fainelli --- arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts b/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts index 5bbc2ba0f959..87569408bb69 100644 --- a/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts +++ b/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts @@ -26,7 +26,7 @@ nvram@1c080000 { compatible = "brcm,nvram"; - reg = <0x1c080000 0x180000>; + reg = <0x1c080000 0x100000>; et2macaddr: et2macaddr { #nvmem-cell-cells = <1>; -- cgit v1.2.3 From e5a259d98823a93459643b239b71fed7589ab669 Mon Sep 17 00:00:00 2001 From: Yafang Shao Date: Tue, 28 Jul 2026 10:49:50 +0800 Subject: kbuild: rpm-pkg: Preserve BTF sections in kernel modules during debuginfo stripping After switching to the kernel's default package scripts for our local kernel RPM builds, we noticed that module BTF entries were missing: $ ls /sys/kernel/btf/ vmlinux <<<< only vmlinux, no module BTF Root cause: find-debuginfo.sh (from the debugedit package) prefers eu-strip over strip when elfutils is installed, which is the common case on RHEL 9. eu-strip removes non-allocated ELF sections, including the .BTF section that contains BPF Type Format information for kernel modules. Without .BTF, BPF tools (bpftool, bcc, bpftrace) cannot resolve kernel types at runtime, and /sys/kernel/btf/ entries are not created when modules are loaded. Additionally, since commit 8646db238997 ("libbpf,bpf: Share BTF relocate-related code with kernel"), modules contain a .BTF.base section that maps distilled type IDs to vmlinux types. If .BTF.base is stripped, btf_parse_module() falls back to vmlinux BTF directly, causing type ID mismatches and rejecting the module's BTF entirely. Fix by passing --keep-section .BTF and --keep-section .BTF.base via _find_debuginfo_opts, which adds -K .BTF and -K .BTF.base to the eu-strip/strip command, preserving both sections while allowing normal debuginfo extraction to proceed. After this change, all module BTF files are properly generated: $ ls /sys/kernel/btf/ aesni_intel drm i2c_i801 mfd_core ahci drm_client_lib i2c_mux net_failover backlight drm_kms_helper i2c_smbus pcspkr ccp drm_shmem_helper input_leds qemu_fw_cfg dm_log failover intel_rapl_common sch_fq_codel dm_mirror fat intel_rapl_msr serio_raw dm_mod fuse irqbypass sunrpc dm_region_hash gf128mul iTCO_wdt vfat virtio_balloon virtio_console virtio_dma_buf virtio_gpu virtio_net virtio_rng virtio_blk vmlinux xfs Suggested-by: Nathan Chancellor Signed-off-by: Yafang Shao Cc: Alan Maguire Link: https://patch.msgid.link/20260728024950.44946-1-laoar.shao@gmail.com Signed-off-by: Nathan Chancellor --- scripts/package/kernel.spec | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec index c732415662ef..46e80970f723 100644 --- a/scripts/package/kernel.spec +++ b/scripts/package/kernel.spec @@ -67,7 +67,18 @@ This package provides debug information for the kernel image and modules from th %undefine _unique_debug_srcs %undefine _debugsource_packages %undefine _debuginfo_subpackages + +# Preserve .BTF and .BTF.base sections in kernel modules during debuginfo +# stripping. find-debuginfo.sh uses eu-strip which removes non-allocated ELF +# sections like .BTF by default. .BTF.base is required for BTF distillation +# support; without it, module BTF validation fails. +%global with_keep_section %(%{__find_debuginfo} --help 2>&1 | grep -c keep-section) +%if %{with_keep_section} +%global _find_debuginfo_opts -r --keep-section .BTF --keep-section .BTF.base +%else %global _find_debuginfo_opts -r +%endif + %global _missing_build_ids_terminate_build 1 %global _no_recompute_build_ids 1 %{debug_package} -- cgit v1.2.3 From a0188cc133696627857d16054e43f9ebc7efc821 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Wed, 29 Jul 2026 03:21:32 +0200 Subject: riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove remove_pud_mapping() and remove_p4d_mapping() obtain a child table base with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for addr. RISC-V folds page-table levels at runtime. When a level is folded, its offset helper returns the parent entry itself, but the index can still be nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39 folds both P4D and PUD, so memory hot-remove can descend into unrelated memory and pass an invalid page to __free_pages(). This can trigger: kernel BUG at include/linux/mm.h:1810! VM_BUG_ON_PAGE(page_ref_count(page) == 0) arch_remove_memory+0x1e/0x5c try_remove_memory+0x15e/0x200 remove_memory+0x24/0x3c Only add the index when the corresponding page-table level is enabled, matching p4d_offset() and pud_offset(). Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support") Assisted-by: Claude:claude-fable-5 Signed-off-by: Karl Mehltretter Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/mm/init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 395ed9ab578e..b6d149e989c8 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1625,7 +1625,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un for (; addr < end; addr = next) { next = pud_addr_end(addr, end); - pudp = pud_base + pud_index(addr); + pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base; pud = pudp_get(pudp); if (!pud_present(pud)) continue; @@ -1656,7 +1656,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un for (; addr < end; addr = next) { next = p4d_addr_end(addr, end); - p4dp = p4d_base + p4d_index(addr); + p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base; p4d = p4dp_get(p4dp); if (!p4d_present(p4d)) continue; -- cgit v1.2.3 From 78cd56c2a9d2e8da763cea3b06b636266ca66911 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Wed, 29 Jul 2026 14:36:09 +0100 Subject: ring-buffer: Fix reader page read offset for remote buffers A page swapped in by __rb_get_reader_page_from_remote() retains its stale read offset, causing subsequent reads to skip events or read past valid data. Fix it. Link: https://patch.msgid.link/20260729133609.4022734-1-vdonnefort@google.com Fixes: fbd1743ecba1 ("ring-buffer: Add non-consuming read for ring-buffer remotes") Signed-off-by: Vincent Donnefort Reviewed-by: Keir Fraser Tested-by: Keir Fraser Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 804ccae694d2..78d3875a47a5 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -5783,6 +5783,7 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer) cpu_buffer->head_page = new_head; cpu_buffer->reader_page = new_reader; + cpu_buffer->reader_page->read = 0; cpu_buffer->pages = &new_head->list; cpu_buffer->read_stamp = new_reader->page->time_stamp; cpu_buffer->lost_events = cpu_buffer->meta_page->reader.lost_events; -- cgit v1.2.3 From a10ea943356b9d70c5616a0a06f6fa97cfdaccb1 Mon Sep 17 00:00:00 2001 From: Hidayath Khan Date: Mon, 27 Jul 2026 11:35:30 +0200 Subject: dibs: fix use-after-free of dmb_node in loopback attach/detach/unregister dibs_lo_attach_dmb(), dibs_lo_detach_dmb() and dibs_lo_unregister_dmb() look up the dmb_node under dmb_ht_lock, drop the lock and only then operate on the node's refcount. Nothing keeps the node alive across that window: __dibs_lo_unregister_dmb() removes the node from the hash table under the write lock and immediately frees it. A concurrent final put can therefore free the node between the lookup and the refcount operation: CPU0 (attach) CPU1 (owner unregisters) read_lock_bh(&dmb_ht_lock) find dmb_node (refcnt == 1) read_unlock_bh(&dmb_ht_lock) refcount_dec_and_test() 1 -> 0 write_lock_bh(&dmb_ht_lock) hash_del(&dmb_node->list) write_unlock_bh(&dmb_ht_lock) kfree(dmb_node) refcount_inc_not_zero(&dmb_node->refcnt) <-- use-after-free The same window exists for the refcount_dec_and_test() calls in the detach and unregister paths. Close the race structurally by making hash table membership and the refcount transitions atomic with respect to each other: - Perform the final refcount_dec_and_test() and hash_del() in a single dmb_ht_lock write-side critical section, in both the unregister and the detach path. Freeing the node still happens after the lock is dropped, which is safe because a node whose refcount reached zero has left the hash table and can no longer be found. - This establishes the invariant that any node found in the hash table holds at least one reference, and that the final reference can only be dropped under the write lock. dibs_lo_attach_dmb() can thus take its reference with a plain refcount_inc() while still holding the read lock; refcount_inc_not_zero() is no longer needed. __dibs_lo_unregister_dmb() no longer touches the hash table and is renamed to dibs_lo_free_dmb() accordingly. Note: commit cc21191b584c ("dibs: Move data path to dibs layer") moved the code to its current location; the race was introduced earlier by commit c3a910f2380f ("net/smc: implement DMB-merged operations of loopback-ism"). Tested SMC-D via ISM and dibs loopback. Cc: stable@vger.kernel.org Fixes: c3a910f2380f ("net/smc: implement DMB-merged operations of loopback-ism") Reported-by: Rahul Chandelkar Signed-off-by: Hidayath Khan Reviewed-by: Alexandra Winter Link: https://patch.msgid.link/20260727093530.968834-1-hidayath@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/dibs/dibs_loopback.c | 47 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index 0f2e09311152..fd5caf1e19a8 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -118,14 +118,9 @@ err_bit: return rc; } -static void __dibs_lo_unregister_dmb(struct dibs_lo_dev *ldev, - struct dibs_lo_dmb_node *dmb_node) +static void dibs_lo_free_dmb(struct dibs_lo_dev *ldev, + struct dibs_lo_dmb_node *dmb_node) { - /* remove dmb from hash table */ - write_lock_bh(&ldev->dmb_ht_lock); - hash_del(&dmb_node->list); - write_unlock_bh(&ldev->dmb_ht_lock); - clear_bit(dmb_node->sba_idx, ldev->sba_idx_mask); folio_put(virt_to_folio(dmb_node->cpu_addr)); kfree(dmb_node); @@ -139,27 +134,33 @@ static int dibs_lo_unregister_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb) struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node; struct dibs_lo_dev *ldev; unsigned long flags; + bool last; ldev = dibs->drv_priv; /* find dmb from hash table */ - read_lock_bh(&ldev->dmb_ht_lock); + write_lock_bh(&ldev->dmb_ht_lock); hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) { if (tmp_node->token == dmb->dmb_tok) { dmb_node = tmp_node; break; } } - read_unlock_bh(&ldev->dmb_ht_lock); - if (!dmb_node) + if (!dmb_node) { + write_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; + } + last = refcount_dec_and_test(&dmb_node->refcnt); + if (last) + hash_del(&dmb_node->list); + write_unlock_bh(&ldev->dmb_ht_lock); - if (refcount_dec_and_test(&dmb_node->refcnt)) { + if (last) { spin_lock_irqsave(&dibs->lock, flags); dibs->dmb_clientid_arr[dmb_node->sba_idx] = NO_DIBS_CLIENT; spin_unlock_irqrestore(&dibs->lock, flags); - __dibs_lo_unregister_dmb(ldev, dmb_node); + dibs_lo_free_dmb(ldev, dmb_node); } return 0; } @@ -188,14 +189,9 @@ static int dibs_lo_attach_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb) read_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } + refcount_inc(&dmb_node->refcnt); read_unlock_bh(&ldev->dmb_ht_lock); - if (!refcount_inc_not_zero(&dmb_node->refcnt)) - /* the dmb is being unregistered, but has - * not been removed from the hash table. - */ - return -EINVAL; - /* provide dmb information */ dmb->idx = dmb_node->sba_idx; dmb->dmb_tok = dmb_node->token; @@ -209,11 +205,12 @@ static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token) { struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node; struct dibs_lo_dev *ldev; + bool last; ldev = dibs->drv_priv; /* find dmb_node according to dmb->dmb_tok */ - read_lock_bh(&ldev->dmb_ht_lock); + write_lock_bh(&ldev->dmb_ht_lock); hash_for_each_possible(ldev->dmb_ht, tmp_node, list, token) { if (tmp_node->token == token) { dmb_node = tmp_node; @@ -221,13 +218,17 @@ static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token) } } if (!dmb_node) { - read_unlock_bh(&ldev->dmb_ht_lock); + write_unlock_bh(&ldev->dmb_ht_lock); return -EINVAL; } - read_unlock_bh(&ldev->dmb_ht_lock); + last = refcount_dec_and_test(&dmb_node->refcnt); + if (last) + hash_del(&dmb_node->list); + write_unlock_bh(&ldev->dmb_ht_lock); + + if (last) + dibs_lo_free_dmb(ldev, dmb_node); - if (refcount_dec_and_test(&dmb_node->refcnt)) - __dibs_lo_unregister_dmb(ldev, dmb_node); return 0; } -- cgit v1.2.3 From fc9c7ca5fcbf7fe3bcba87d1ff72f0009071ba86 Mon Sep 17 00:00:00 2001 From: Kiran Kella Date: Mon, 27 Jul 2026 03:16:28 -0700 Subject: psp: fix NULL genl_sock deref race with concurrent netns teardown The race occurs between network namespace removal and PSP device unregistration. When a netns is deleted while a PSP device associated with that netns is concurrently being removed, psp_dev_unregister() triggers psp_nl_notify_dev() to send a device change notification. Concurrently, cleanup_net() running in the netns workqueue calls genl_pernet_exit(), which sets net->genl_sock to NULL. If genl_pernet_exit() wins the race, two sites in psp_nl_multicast_per_ns() then dereference the NULL socket and crash: CPU 0 (netns teardown) CPU 1 (PSP device unregister) ====================== ============================= cleanup_net [workqueue] genl_pernet_exit() psp_dev_unregister() net->genl_sock = NULL psp_nl_notify_dev() psp_nl_multicast_per_ns() build_ntf() -> netlink_has_listeners(NULL) /* crash */ genlmsg_multicast_netns() -> nlmsg_multicast_filtered(NULL) /* crash */ Fix by replacing the bare dev_net() calls with maybe_get_net(). maybe_get_net() returns NULL if the namespace is already dying. Holding the reference ensures genl_sock remains valid across both the build_ntf() and genlmsg_multicast_netns() calls. Fixes: 00c94ca2b99e ("psp: base PSP device support") Fixes: 06c2dce2d0f6 ("psp: add new netlink cmd for dev-assoc and dev-disassoc") Reviewed-by: Ajit Khaparde Reviewed-by: Vikas Gupta Reviewed-by: Bhargava Marreddy Reviewed-by: Akhilesh Samineni Signed-off-by: Kiran Kella Link: https://patch.msgid.link/20260727101628.502042-1-kiran.kella@broadcom.com Signed-off-by: Jakub Kicinski --- net/psp/psp_nl.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c index 9610d8c456ff..43b066353c65 100644 --- a/net/psp/psp_nl.c +++ b/net/psp/psp_nl.c @@ -62,7 +62,14 @@ psp_nl_multicast_per_ns(struct psp_dev *psd, unsigned int group, struct net *main_net; struct sk_buff *ntf; - main_net = dev_net(psd->main_netdev); + /* device may be changing netns in parallel */ + rcu_read_lock(); + main_net = maybe_get_net(dev_net_rcu(psd->main_netdev)); + rcu_read_unlock(); + + if (!main_net) + return; + xa_init(&sent_nets); list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) { @@ -88,10 +95,10 @@ psp_nl_multicast_per_ns(struct psp_dev *psd, unsigned int group, /* Send to main device netns */ ntf = build_ntf(psd, main_net, ctx); - if (!ntf) - return; - genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group, - GFP_KERNEL); + if (ntf) + genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group, + GFP_KERNEL); + put_net(main_net); } static struct sk_buff *psp_nl_clone_ntf(struct psp_dev *psd, struct net *net, -- cgit v1.2.3 From 93cad1f6bd1e27c75c4a5ab000c2a2fc01181ccf Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Mon, 27 Jul 2026 14:53:39 -0400 Subject: ipv6: release fib6_null_entry on subtree failure When adding a source-specific route creates a new subtree, fib6_add() installs fib6_null_entry as the temporary leaf of the new subtree root and takes a fib6_info reference for that holder. If adding the first source leaf fails, the code frees the just allocated subtree root but leaves that hold behind. fib6_null_entry is a per-netns sentinel and is freed directly at netns teardown, so this does not keep the object alive. However, it leaves its visible refcount permanently elevated and can eventually saturate the refcount on repeated failures. Drop the null-entry reference before freeing the unlinked subtree root. Fixes: 5ea715289af6 ("ipv6: broadly use fib6_info_hold() helper") Signed-off-by: Shuangpeng Bai Reviewed-by: Ido Schimmel Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://patch.msgid.link/20260727185339.1545169-1-shuangpeng.kernel@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_fib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index a130cdfaebfb..e9fc692d4f3b 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1494,6 +1494,7 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt, root, and then (in failure) stale node in main tree. */ + fib6_info_release(info->nl_net->ipv6.fib6_null_entry); node_free_immediate(info->nl_net, sfn); err = PTR_ERR(sn); goto failure; -- cgit v1.2.3 From 1c15e75dc21fc61f7bb62f2e8c86d86da01608c5 Mon Sep 17 00:00:00 2001 From: Alvin Šipraga Date: Mon, 27 Jul 2026 22:29:29 +0200 Subject: MAINTAINERS: make Luiz a maintainer and myself reviewer for Realtek DSA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have changed jobs and therefore no longer have access to hardware using Realtek Ethernet switches. Luiz has kindly agreed to take up the role of maintainer, while I will stick around as a reviewer. Also update .mailmap so that mails to my old company email stop bouncing. Use my new work email for Analog Devices Inc. instead. Signed-off-by: Alvin Šipraga Reviewed-by: Linus Walleij Acked-by: Luiz Angelo Daros de Luca Link: https://patch.msgid.link/20260727-realtek-maintainers-v1-1-ab501adc0cdb@analog.com Signed-off-by: Jakub Kicinski --- .mailmap | 1 + MAINTAINERS | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index a0f64c31abcc..b66c317e8f06 100644 --- a/.mailmap +++ b/.mailmap @@ -72,6 +72,7 @@ Alice Mikityanska Alice Mikityanska Alice Mikityanska Aloka Dixit +Alvin Šipraga Al Viro Al Viro Amit Blay diff --git a/MAINTAINERS b/MAINTAINERS index 61126d170e4a..2385c27f14b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22735,7 +22735,8 @@ F: drivers/watchdog/realtek_otto_wdt.c REALTEK RTL83xx SMI DSA ROUTER CHIPS M: Linus Walleij -M: Alvin Šipraga +M: Luiz Angelo Daros de Luca +R: Alvin Šipraga S: Maintained F: Documentation/devicetree/bindings/net/dsa/realtek.yaml F: drivers/net/dsa/realtek/* -- cgit v1.2.3 From 74b21f52c5c5a71a05c0ff70e513f4f04ff28b17 Mon Sep 17 00:00:00 2001 From: Charles Vosburgh Date: Mon, 27 Jul 2026 19:17:30 -0400 Subject: sctp: validate Adaptation Indication parameter length The Adaptation Layer Indication parameter contains a fixed 32-bit Adaptation Code Point after its parameter header. However, sctp_verify_param() accepts a header-only parameter because the generic parameter walker only requires the header to be present. sctp_process_param() then reads adaptation_ind beyond the declared parameter. When the malformed parameter is last in an INIT, the read starts at the receive skb tail, and the value is copied into the state cookie returned in the INIT ACK. This may disclose four receive-buffer tail bytes. Require the declared parameter length to match the fixed structure size and abort the association through the existing invalid parameter length path otherwise. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Charles Vosburgh Acked-by: Xin Long Link: https://patch.msgid.link/20260727-sctp-adaptation-length-v1-1-0ab58b2810a5@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/sm_make_chunk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index a1c0334a1038..0ae30c3c8913 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2171,7 +2171,13 @@ static enum sctp_ierror sctp_verify_param(struct net *net, case SCTP_PARAM_HEARTBEAT_INFO: case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: case SCTP_PARAM_ECN_CAPABLE: + break; case SCTP_PARAM_ADAPTATION_LAYER_IND: + if (ntohs(param.p->length) != sizeof(*param.aind)) { + sctp_process_inv_paramlength(asoc, param.p, + chunk, err_chunk); + retval = SCTP_IERROR_ABORT; + } break; case SCTP_PARAM_SUPPORTED_EXT: -- cgit v1.2.3 From c052927905710de1ab7364bf1925efbd12517ea3 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Thu, 9 Jul 2026 08:49:00 +0200 Subject: riscv: vdso: Only try to install vDSO when present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vdso.so.dbg is only built with CONFIG_MMU. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607090258.iSAUYlO1-lkp@intel.com/ Fixes: f157d411a9eb ("riscv: add missing vdso_install target") Fixes: 3edf39916977 ("vDSO, kbuild: Provide vDSO debug variants at runtime") Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260709-riscv-install-vdso-v1-1-0ba4345419ca@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index ce0cc737f870..1363e5bef35c 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -168,7 +168,7 @@ vdso_prepare: prepare0 endif endif -vdso-install-y += arch/riscv/kernel/vdso/vdso.so.dbg +vdso-install-$(CONFIG_MMU) += arch/riscv/kernel/vdso/vdso.so.dbg vdso-install-$(CONFIG_RISCV_USER_CFI) += arch/riscv/kernel/vdso_cfi/vdso-cfi.so.dbg vdso-install-$(CONFIG_COMPAT) += arch/riscv/kernel/compat_vdso/compat_vdso.so.dbg -- cgit v1.2.3 From f11b48aa674b475f196bede7d69593c050107fc5 Mon Sep 17 00:00:00 2001 From: Simon Schippers Date: Tue, 28 Jul 2026 11:22:37 +0200 Subject: Revert "tun/tap & vhost-net: avoid ptr_ring tail-drop when a qdisc is present" This reverts commit 1d6e569b7d0c0b2736636749e4be0a27f3cefcb3. The commit stops the netdev queue when the ptr_ring is full instead of dropping the packet. My own tests showed no relevant regression, but on Brett Sheffield's librecast testbed an IPv6 multicast testcase got slower. With 8 iperf3 TCP threads sending, the throughput dropped from 13.5 Gbit/s to 9.13 Gbit/s. Reported-by: Brett Sheffield Closes: https://lore.kernel.org/netdev/akVnoOYQOrt8k-Gu@karahi.librecast.net/ Signed-off-by: Simon Schippers Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260728092240.250257-2-simon.schippers@tu-dortmund.de Signed-off-by: Jakub Kicinski --- drivers/net/tun.c | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index ffbe6f13fb1f..ec5573f545af 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1018,7 +1018,6 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) struct netdev_queue *queue; struct tun_file *tfile; int len = skb->len; - int ret; rcu_read_lock(); tfile = rcu_dereference(tun->tfiles[txq]); @@ -1073,33 +1072,13 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) nf_reset_ct(skb); - queue = netdev_get_tx_queue(dev, txq); - - spin_lock(&tfile->tx_ring.producer_lock); - ret = __ptr_ring_produce(&tfile->tx_ring, skb); - if (!qdisc_txq_has_no_queue(queue) && - __ptr_ring_check_produce(&tfile->tx_ring) == -ENOSPC) { - netif_tx_stop_queue(queue); - /* Paired with smp_mb() in __tun_wake_queue() */ - smp_mb__after_atomic(); - if (!__ptr_ring_check_produce(&tfile->tx_ring)) - netif_tx_wake_queue(queue); - } - spin_unlock(&tfile->tx_ring.producer_lock); - - if (ret) { - /* This should be a rare case if a qdisc is present, but - * can happen due to lltx. - * Since skb_tx_timestamp(), skb_orphan(), - * run_ebpf_filter() and pskb_trim() could have tinkered - * with the SKB, returning NETDEV_TX_BUSY is unsafe and - * we must drop instead. - */ + if (ptr_ring_produce(&tfile->tx_ring, skb)) { drop_reason = SKB_DROP_REASON_FULL_RING; goto drop; } /* dev->lltx requires to do our own update of trans_start */ + queue = netdev_get_tx_queue(dev, txq); txq_trans_cond_update(queue); /* Notify and wake up reader process */ -- cgit v1.2.3 From 6bc85579c3bbb2f088cbac849c5dc2a134dda736 Mon Sep 17 00:00:00 2001 From: Simon Schippers Date: Tue, 28 Jul 2026 11:22:38 +0200 Subject: Revert "ptr_ring: move free-space check into separate helper" This reverts commit fba362c17d9d9211fc51f272156bb84fc23bdf98. __ptr_ring_check_produce() has no users left after reverting commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop when a qdisc is present"). Signed-off-by: Simon Schippers Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260728092240.250257-3-simon.schippers@tu-dortmund.de Signed-off-by: Jakub Kicinski --- include/linux/ptr_ring.h | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h index c95e891903f0..d2c3629bbe45 100644 --- a/include/linux/ptr_ring.h +++ b/include/linux/ptr_ring.h @@ -96,20 +96,6 @@ static inline bool ptr_ring_full_bh(struct ptr_ring *r) return ret; } -/* Note: callers invoking this in a loop must use a compiler barrier, - * for example cpu_relax(). Callers must hold producer_lock. - */ -static inline int __ptr_ring_check_produce(struct ptr_ring *r) -{ - if (unlikely(!r->size)) - return -EINVAL; - - if (data_race(r->queue[r->producer])) - return -ENOSPC; - - return 0; -} - /* Note: callers invoking this in a loop must use a compiler barrier, * for example cpu_relax(). Callers must hold producer_lock. * Callers are responsible for making sure pointer that is being queued @@ -117,10 +103,8 @@ static inline int __ptr_ring_check_produce(struct ptr_ring *r) */ static inline int __ptr_ring_produce(struct ptr_ring *r, void *ptr) { - int p = __ptr_ring_check_produce(r); - - if (p) - return p; + if (unlikely(!r->size) || data_race(r->queue[r->producer])) + return -ENOSPC; /* Make sure the pointer we are storing points to a valid data. */ /* Pairs with the dependency ordering in __ptr_ring_consume. */ -- cgit v1.2.3 From 8f83be72d9f5ef16c4a908450d0d993e8ec99d34 Mon Sep 17 00:00:00 2001 From: Simon Schippers Date: Tue, 28 Jul 2026 11:22:39 +0200 Subject: Revert "vhost-net: wake queue of tun/tap after ptr_ring consume" This reverts commit baf808fe4fcd35767ab732b4ab2ea80dabfd97a6. There is no netdev queue left to wake after reverting commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop when a qdisc is present"). Signed-off-by: Simon Schippers Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260728092240.250257-4-simon.schippers@tu-dortmund.de Signed-off-by: Jakub Kicinski --- drivers/net/tun.c | 23 ----------------------- drivers/vhost/net.c | 21 ++++++--------------- include/linux/if_tun.h | 3 --- 3 files changed, 6 insertions(+), 41 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index ec5573f545af..39abc3078097 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -3787,29 +3787,6 @@ struct ptr_ring *tun_get_tx_ring(struct file *file) } EXPORT_SYMBOL_GPL(tun_get_tx_ring); -/* Callers must hold ring.consumer_lock */ -void tun_wake_queue(struct file *file, int consumed) -{ - struct tun_file *tfile; - struct tun_struct *tun; - - if (file->f_op != &tun_fops) - return; - - tfile = file->private_data; - if (!tfile) - return; - - rcu_read_lock(); - - tun = rcu_dereference(tfile->tun); - if (tun) - __tun_wake_queue(tun, tfile, consumed); - - rcu_read_unlock(); -} -EXPORT_SYMBOL_GPL(tun_wake_queue); - module_init(tun_init); module_exit(tun_cleanup); MODULE_DESCRIPTION(DRV_DESCRIPTION); diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 3e72b9c6af0c..6949b704166d 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -176,21 +176,13 @@ static void *vhost_net_buf_consume(struct vhost_net_buf *rxq) return ret; } -static int vhost_net_buf_produce(struct sock *sk, - struct vhost_net_virtqueue *nvq) +static int vhost_net_buf_produce(struct vhost_net_virtqueue *nvq) { - struct file *file = sk->sk_socket->file; struct vhost_net_buf *rxq = &nvq->rxq; rxq->head = 0; - spin_lock(&nvq->rx_ring->consumer_lock); - rxq->tail = __ptr_ring_consume_batched(nvq->rx_ring, rxq->queue, - VHOST_NET_BATCH); - - if (rxq->tail) - tun_wake_queue(file, rxq->tail); - - spin_unlock(&nvq->rx_ring->consumer_lock); + rxq->tail = ptr_ring_consume_batched(nvq->rx_ring, rxq->queue, + VHOST_NET_BATCH); return rxq->tail; } @@ -217,15 +209,14 @@ static int vhost_net_buf_peek_len(void *ptr) return __skb_array_len_with_tag(ptr); } -static int vhost_net_buf_peek(struct sock *sk, - struct vhost_net_virtqueue *nvq) +static int vhost_net_buf_peek(struct vhost_net_virtqueue *nvq) { struct vhost_net_buf *rxq = &nvq->rxq; if (!vhost_net_buf_is_empty(rxq)) goto out; - if (!vhost_net_buf_produce(sk, nvq)) + if (!vhost_net_buf_produce(nvq)) return 0; out: @@ -1013,7 +1004,7 @@ static int peek_head_len(struct vhost_net_virtqueue *rvq, struct sock *sk) unsigned long flags; if (rvq->rx_ring) - return vhost_net_buf_peek(sk, rvq); + return vhost_net_buf_peek(rvq); spin_lock_irqsave(&sk->sk_receive_queue.lock, flags); head = skb_peek(&sk->sk_receive_queue); diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 5f3e206c7a73..80166eb62f41 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -22,7 +22,6 @@ struct tun_msg_ctl { #if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE) struct socket *tun_get_socket(struct file *); struct ptr_ring *tun_get_tx_ring(struct file *file); -void tun_wake_queue(struct file *file, int consumed); static inline bool tun_is_xdp_frame(void *ptr) { @@ -56,8 +55,6 @@ static inline struct ptr_ring *tun_get_tx_ring(struct file *f) return ERR_PTR(-EINVAL); } -static inline void tun_wake_queue(struct file *f, int consumed) {} - static inline bool tun_is_xdp_frame(void *ptr) { return false; -- cgit v1.2.3 From c3da92af07eaba43f49910b2e4fbd016e563fa35 Mon Sep 17 00:00:00 2001 From: Simon Schippers Date: Tue, 28 Jul 2026 11:22:40 +0200 Subject: Revert "tun/tap: add ptr_ring consume helper with netdev queue wakeup" This reverts commit d4c22d70d7253dd727c71484c58d504f6c630343. There is no netdev queue left to wake after reverting commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop when a qdisc is present"). Signed-off-by: Simon Schippers Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260728092240.250257-5-simon.schippers@tu-dortmund.de Signed-off-by: Jakub Kicinski --- drivers/net/tun.c | 61 ++++--------------------------------------------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 39abc3078097..fed9dfdfcc3b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -145,8 +145,6 @@ struct tun_file { struct list_head next; struct tun_struct *detached; struct ptr_ring tx_ring; - /* Protected by tx_ring.consumer_lock */ - int cons_cnt; struct xdp_rxq_info xdp_rxq; }; @@ -590,13 +588,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean) rcu_assign_pointer(tun->tfiles[index], tun->tfiles[tun->numqueues - 1]); ntfile = rtnl_dereference(tun->tfiles[index]); - spin_lock(&ntfile->tx_ring.consumer_lock); ntfile->queue_index = index; ntfile->xdp_rxq.queue_index = index; - ntfile->cons_cnt = 0; - if (__ptr_ring_empty(&ntfile->tx_ring)) - netif_wake_subqueue(tun->dev, index); - spin_unlock(&ntfile->tx_ring.consumer_lock); rcu_assign_pointer(tun->tfiles[tun->numqueues - 1], NULL); @@ -737,9 +730,6 @@ static int tun_attach(struct tun_struct *tun, struct file *file, goto out; } - spin_lock(&tfile->tx_ring.consumer_lock); - tfile->cons_cnt = 0; - spin_unlock(&tfile->tx_ring.consumer_lock); tfile->queue_index = tun->numqueues; tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN; @@ -2126,46 +2116,13 @@ done: return total; } -/* Callers must hold ring.consumer_lock */ -static void __tun_wake_queue(struct tun_struct *tun, - struct tun_file *tfile, int consumed) -{ - struct netdev_queue *txq = netdev_get_tx_queue(tun->dev, - tfile->queue_index); - - /* Paired with smp_mb__after_atomic() in tun_net_xmit() */ - smp_mb(); - if (netif_tx_queue_stopped(txq)) { - tfile->cons_cnt += consumed; - if (tfile->cons_cnt >= tfile->tx_ring.size / 2 || - __ptr_ring_empty(&tfile->tx_ring)) { - netif_tx_wake_queue(txq); - tfile->cons_cnt = 0; - } - } -} - -static void *tun_ring_consume(struct tun_struct *tun, struct tun_file *tfile) -{ - void *ptr; - - spin_lock(&tfile->tx_ring.consumer_lock); - ptr = __ptr_ring_consume(&tfile->tx_ring); - if (ptr) - __tun_wake_queue(tun, tfile, 1); - - spin_unlock(&tfile->tx_ring.consumer_lock); - return ptr; -} - -static void *tun_ring_recv(struct tun_struct *tun, struct tun_file *tfile, - int noblock, int *err) +static void *tun_ring_recv(struct tun_file *tfile, int noblock, int *err) { DECLARE_WAITQUEUE(wait, current); void *ptr = NULL; int error = 0; - ptr = tun_ring_consume(tun, tfile); + ptr = ptr_ring_consume(&tfile->tx_ring); if (ptr) goto out; if (noblock) { @@ -2177,7 +2134,7 @@ static void *tun_ring_recv(struct tun_struct *tun, struct tun_file *tfile, while (1) { set_current_state(TASK_INTERRUPTIBLE); - ptr = tun_ring_consume(tun, tfile); + ptr = ptr_ring_consume(&tfile->tx_ring); if (ptr) break; if (signal_pending(current)) { @@ -2214,7 +2171,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile, if (!ptr) { /* Read frames from ring */ - ptr = tun_ring_recv(tun, tfile, noblock, &err); + ptr = tun_ring_recv(tfile, noblock, &err); if (!ptr) return err; } @@ -3669,16 +3626,6 @@ static int tun_queue_resize(struct tun_struct *tun) dev->tx_queue_len, GFP_KERNEL, tun_ptr_free); - if (!ret) { - for (i = 0; i < tun->numqueues; i++) { - tfile = rtnl_dereference(tun->tfiles[i]); - spin_lock(&tfile->tx_ring.consumer_lock); - netif_wake_subqueue(tun->dev, tfile->queue_index); - tfile->cons_cnt = 0; - spin_unlock(&tfile->tx_ring.consumer_lock); - } - } - kfree(rings); return ret; } -- cgit v1.2.3 From b4ce102b2cd88424c5860fbbb20b9eb343a93bf4 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 28 Jul 2026 05:52:14 +0100 Subject: net: dsa: mt7530: check bus->read() errors in the MDIO regmap backend bus->read() returns a negative errno on failure, but mt7530_regmap_read() assigns it to a u16, truncating e.g. -ETIMEDOUT into 0xff92, and returns success. The garbage word is then consumed as register data, and read-modify-write cycles write it back to the switch. Check both reads and propagate their errors. The same defect existed in mt7530_mii_read() since the driver was introduced and moved into the regmap backend unchanged. Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") Signed-off-by: Daniel Golle Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/3c628e48276c2e5522c8795a6be60d11c7a76a7d.1785213071.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mt7530-mdio.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c index 11ea924a9f35..784dd58a7158 100644 --- a/drivers/net/dsa/mt7530-mdio.c +++ b/drivers/net/dsa/mt7530-mdio.c @@ -55,8 +55,15 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) if (ret < 0) return ret; - lo = bus->read(bus, priv->mdiodev->addr, r); - hi = bus->read(bus, priv->mdiodev->addr, 0x10); + ret = bus->read(bus, priv->mdiodev->addr, r); + if (ret < 0) + return ret; + lo = ret; + + ret = bus->read(bus, priv->mdiodev->addr, 0x10); + if (ret < 0) + return ret; + hi = ret; *val = (hi << 16) | (lo & 0xffff); -- cgit v1.2.3 From ed9adac35b8fac635f40e28461505e2e5b6c8fcc Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 28 Jul 2026 05:52:21 +0100 Subject: net: dsa: mt7530: error out on failed reads in ATC/VTCR command polling mt7530_fdb_cmd() and mt7530_vlan_cmd() poll the command register through a helper which returns 0 when the underlying read fails. A failed bus transaction thus clears ATC_BUSY/VTCR_BUSY and is treated as successful command completion, and the subsequent ATC_INVALID and VTCR_INVALID checks are defeated the same way. Poll using regmap_read_poll_timeout(), which stops on read errors and propagates them, and check the completion status read as well. Take the MDIO bus lock across the sequence as the switch regmap is set up with locking disabled. Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") Fixes: 83163f7dca56 ("net: dsa: mediatek: add VLAN support for MT7530") Signed-off-by: Daniel Golle Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/eea1d8f15c54375b3770c23e09fb3217df487169.1785213071.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mt7530.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 3c2a3029b10c..292cde961f1a 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -248,15 +248,20 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) { u32 val; int ret; - struct mt7530_dummy_poll p; /* Set the command operating upon the MAC address entries */ val = ATC_BUSY | ATC_MAT(0) | cmd; mt7530_write(priv, MT7530_ATC, val); - INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_ATC); - ret = readx_poll_timeout(_mt7530_read, &p, val, - !(val & ATC_BUSY), 20, 20000); + mt7530_mutex_lock(priv); + + ret = regmap_read_poll_timeout(priv->regmap, MT7530_ATC, val, + !(val & ATC_BUSY), 20, 20000); + if (!ret) + ret = regmap_read(priv->regmap, MT7530_ATC, &val); + + mt7530_mutex_unlock(priv); + if (ret < 0) { dev_err(priv->dev, "reset timeout\n"); return ret; @@ -265,7 +270,6 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) /* Additional sanity for read command if the specified * entry is invalid */ - val = mt7530_read(priv, MT7530_ATC); if ((cmd == MT7530_FDB_READ) && (val & ATC_INVALID)) return -EINVAL; @@ -1626,22 +1630,26 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port, static int mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid) { - struct mt7530_dummy_poll p; u32 val; int ret; val = VTCR_BUSY | VTCR_FUNC(cmd) | vid; mt7530_write(priv, MT7530_VTCR, val); - INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR); - ret = readx_poll_timeout(_mt7530_read, &p, val, - !(val & VTCR_BUSY), 20, 20000); + mt7530_mutex_lock(priv); + + ret = regmap_read_poll_timeout(priv->regmap, MT7530_VTCR, val, + !(val & VTCR_BUSY), 20, 20000); + if (!ret) + ret = regmap_read(priv->regmap, MT7530_VTCR, &val); + + mt7530_mutex_unlock(priv); + if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); return ret; } - val = mt7530_read(priv, MT7530_VTCR); if (val & VTCR_INVALID) { dev_err(priv->dev, "read VTCR invalid\n"); return -EINVAL; -- cgit v1.2.3 From 77a9ebe8818cf6dd1699bd6728cb5d66307801d7 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Tue, 28 Jul 2026 05:52:29 +0100 Subject: net: dsa: mt7530: error out on failed reads in MT7531 PHY polling The MT7531 indirect PHY access functions poll MT7531_PHY_IAC through a helper which returns 0 when the underlying read fails, so a failed bus transaction clears MT7531_PHY_ACS_ST and the access carries on, returning garbage PHY register data to phylib. Poll using regmap_read_poll_timeout(), which stops on read errors and propagates them. These functions hold the MDIO bus lock across the whole sequence, so the unlocked regmap accesses remain correct. Remove the now-unused _mt7530_unlocked_read(). Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch") Signed-off-by: Daniel Golle Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/79e85d68d210cc37342978171aa6432aa2954333.1785213071.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mt7530.c | 58 +++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 292cde961f1a..aa33d94e11b5 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -191,12 +191,6 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) mt7530_mutex_unlock(priv); } -static u32 -_mt7530_unlocked_read(struct mt7530_dummy_poll *p) -{ - return mt7530_mii_read(p->priv, p->reg); -} - static u32 _mt7530_read(struct mt7530_dummy_poll *p) { @@ -553,16 +547,13 @@ static int mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, int regnum) { - struct mt7530_dummy_poll p; u32 reg, val; int ret; - INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); - mt7530_mutex_lock(priv); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -572,8 +563,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, MT7531_MDIO_DEV_ADDR(devad) | regnum; mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -583,8 +574,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, MT7531_MDIO_DEV_ADDR(devad); mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -601,16 +592,13 @@ static int mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, int regnum, u16 data) { - struct mt7530_dummy_poll p; u32 val, reg; int ret; - INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); - mt7530_mutex_lock(priv); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -620,8 +608,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, MT7531_MDIO_DEV_ADDR(devad) | regnum; mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -631,8 +619,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, MT7531_MDIO_DEV_ADDR(devad) | data; mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -647,16 +635,13 @@ out: static int mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) { - struct mt7530_dummy_poll p; int ret; u32 val; - INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); - mt7530_mutex_lock(priv); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -667,8 +652,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, - !(val & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, + !(val & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -685,16 +670,13 @@ static int mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, u16 data) { - struct mt7530_dummy_poll p; int ret; u32 reg; - INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); - mt7530_mutex_lock(priv); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, - !(reg & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, + !(reg & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; @@ -705,8 +687,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); - ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, - !(reg & MT7531_PHY_ACS_ST), 20, 100000); + ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, + !(reg & MT7531_PHY_ACS_ST), 20, 100000); if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); goto out; -- cgit v1.2.3 From b041ed62aa6e3b2d7d36127e0e5d7bf2701f8231 Mon Sep 17 00:00:00 2001 From: Nazim Amirul Date: Mon, 27 Jul 2026 23:09:04 -0700 Subject: net: stmmac: Fix E2E delay mechanism For E2E delay mechanism, "received DELAY_REQ without timestamp" error messages show up for dwmac v3.70+ and dwxgmac IPs. This issue affects socfpga platforms, Agilex7 (dwmac 3.70) and Agilex5 (dwxgmac). According to the databook, to enable timestamping for all events, the SNAPTYPSEL bits in the MAC_Timestamp_Control register must be set to 2'b01, and the TSEVNTENA bit must be cleared to 0'b0. Commit 3cb958027cb8 ("net: stmmac: Fix E2E delay mechanism") already addresses this problem for all dwmacs above version v4.10. However, same holds true for v3.70 and above, as well as for dwxgmac. Updates the check accordingly. Fixes: 14f347334bf2 ("net: stmmac: Correctly take timestamp for PTPv2") Fixes: f2fb6b6275eb ("net: stmmac: enable timestamp snapshot for required PTP packets in dwmac v5.10a") Fixes: 3cb958027cb8 ("net: stmmac: Fix E2E delay mechanism") Reviewed-by: Maxime Chevallier Signed-off-by: Rohan G Thomas Signed-off-by: Nazim Amirul Link: https://patch.msgid.link/20260728060904.31993-1-muhammad.nazim.amirul.nazle.asmade@altera.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 151c77713025..3801f9d45278 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -755,7 +755,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev, config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; ptp_v2 = PTP_TCR_TSVER2ENA; snap_type_sel = PTP_TCR_SNAPTYPSEL_1; - if (priv->synopsys_id < DWMAC_CORE_4_10) + if (priv->synopsys_id < DWMAC_CORE_3_70 && + priv->plat->core_type != DWMAC_CORE_XGMAC) ts_event_en = PTP_TCR_TSEVNTENA; ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA; ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA; -- cgit v1.2.3 From e1cf066244dad576221b7123a0e5005967f25a20 Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Mon, 27 Jul 2026 20:18:30 +0200 Subject: net: openvswitch: fix skb leak on flow key update failure during recirculation do_execute_actions() returns right away when execute_recirc() fails on the last action as it assumes this function always takes ownership of the skb when 'last' is true. But when the flow key update fails, the function doesn't free the skb and it ends up leaked. This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless. Reported by Sashiko. Fixes: 971427f353f3 ("openvswitch: Add recirc and hash action.") Cc: stable@vger.kernel.org Signed-off-by: Ilya Maximets Reviewed-by: Aaron Conole Link: https://patch.msgid.link/20260727181851.306076-2-i.maximets@ovn.org Signed-off-by: Jakub Kicinski --- net/openvswitch/actions.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 513fca6a8e8a..0118fe3b35e4 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -1108,6 +1108,10 @@ static int execute_masked_set_action(struct sk_buff *skb, return err; } +/* When 'last' is true, recirc() should always consume the 'skb'. + * Otherwise, recirc() should keep 'skb' intact regardless what + * actions are executed on recirculation. + */ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *a, bool last) @@ -1118,8 +1122,12 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, int err; err = ovs_flow_key_update(skb, key); - if (err) + if (err) { + if (last) + ovs_kfree_skb_reason(skb, + OVS_DROP_ACTION_ERROR); return err; + } } BUG_ON(!is_flow_key_valid(key)); -- cgit v1.2.3 From bc62e843bc48f933da765ce47079fd992e535794 Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Mon, 27 Jul 2026 20:18:31 +0200 Subject: net: openvswitch: fix skb leak on flow key update failure during ct ovs_ct_execute() always steals or frees the skb on failure while ovs_flow_key_update() does not. So, if it fails and we return right away, the skb ends up leaked. Fix that by breaking instead and letting the common error handling code at the bottom of the loop to free the skb properly. This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless. Reported by Sashiko. Fixes: ec0d043d05e6 ("openvswitch: Ensure flow is valid before executing ct") Cc: stable@vger.kernel.org Signed-off-by: Ilya Maximets Reviewed-by: Aaron Conole Link: https://patch.msgid.link/20260727181851.306076-3-i.maximets@ovn.org Signed-off-by: Jakub Kicinski --- net/openvswitch/actions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 0118fe3b35e4..dc5ff859f114 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -1380,7 +1380,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, if (!is_flow_key_valid(key)) { err = ovs_flow_key_update(skb, key); if (err) - return err; + break; } err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key, -- cgit v1.2.3 From e67cc80b50f587cd1d8ffc8989dcec3291720bc3 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 27 Jul 2026 04:37:59 -0700 Subject: net: mana: Return error code from mana_create_rxq() mana_create_rxq() returns a struct mana_rxq pointer and returns NULL on any failure. The caller, mana_add_rx_queues(), cannot tell what went wrong and hardcodes the error as -ENOMEM. As a result the actual failure reported by the lower layers (for example -EPROTO from a failed HW request) is masked and every RX queue creation failure looks like an out-of-memory error. Return an ERR_PTR() encoded error code from mana_create_rxq() on failure instead of NULL. The caller now propagates the returned error code directly instead of substituting -ENOMEM. Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Signed-off-by: Aditya Garg Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260727113759.2881500-1-gargaditya@linux.microsoft.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microsoft/mana/mana_en.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 9d9bfd116dab..92bb55935c1c 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2829,7 +2829,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, rxq = kvzalloc_flex(*rxq, rx_oobs, apc->rx_queue_size); if (!rxq) - return NULL; + return ERR_PTR(-ENOMEM); rxq->ndev = ndev; rxq->num_rx_buf = apc->rx_queue_size; @@ -2930,7 +2930,7 @@ out: mana_destroy_rxq(apc, rxq, false); - return NULL; + return ERR_PTR(err); } static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx) @@ -2964,8 +2964,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc, for (i = 0; i < apc->num_queues; i++) { rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev); - if (!rxq) { - err = -ENOMEM; + if (IS_ERR(rxq)) { + err = PTR_ERR(rxq); netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err); goto out; } -- cgit v1.2.3 From 54ad7ea45d63146a8e3c57375f8a269d4cf7ecea Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Mon, 27 Jul 2026 14:03:48 +0800 Subject: ptp: netc: fix potential interrupt storm caused by incorrect unbind order In netc_timer_remove(), hardware interrupts are disabled by clearing TMR_TEMASK before ptp_clock_unregister() is called. This may cause a race condition during driver unbind that could leave hardware interrupts active. For example, a concurrent PTP_CLK_REQ_EXTTS ioctl can re-enable TMR_TEMASK after it has been cleared, leaving a pending hardware interrupt when the driver unbinds. Since the NETC Timer does not support PCIe FLR, hardware state is not reset during probe. When the driver is rebound and the IRQ is registered, the pending interrupt fires immediately. At that point priv->tmr_emask is still zero, so netc_timer_isr() does not clear the interrupt status and unconditionally returns IRQ_HANDLED, resulting in an uninterruptible infinite interrupt storm. Fix this in several ways. First, request the IRQ with IRQF_NO_AUTOEN so it is not enabled when request_irq() runs, and clear TMR_TEMASK in netc_timer_init() before enabling it. The IRQ is only enabled at the end of probe once the timer has been reprogrammed and the PTP clock has been registered. This ensures a stale pending interrupt from a previous unbind or an unclean shutdown cannot be delivered before the driver is fully initialized. Second, in netc_timer_remove() call disable_irq() before ptp_clock_unregister() and move the TMR_TEMASK/TMR_CTRL clearing after it. disable_irq() masks the line and waits for any in-flight netc_timer_isr() to finish, so no ISR can dereference priv->clock after ptp_clock_unregister() has freed it. Unregistering the PTP clock before clearing the mask also guarantees that no in-flight or concurrent ioctl can re-enable hardware interrupts. Finally, return IRQ_NONE from netc_timer_isr() when the masked event status is zero, so the kernel's spurious interrupt detection can disable a stuck line instead of looping forever. Fixes: 671e266835b8 ("ptp: netc: add periodic pulse output support") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260720012508.23227-1-wei.fang%40oss.nxp.com Signed-off-by: Wei Fang Link: https://patch.msgid.link/20260727060348.1887464-1-wei.fang@oss.nxp.com Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_netc.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c index 5e381c354d74..1c20d7efab92 100644 --- a/drivers/ptp/ptp_netc.c +++ b/drivers/ptp/ptp_netc.c @@ -769,6 +769,7 @@ static void netc_timer_init(struct netc_timer *priv) TMR_CTRL_TE | TMR_CTRL_FS; netc_timer_wr(priv, NETC_TMR_CTRL, tmr_ctrl); netc_timer_wr(priv, NETC_TMR_PRSC, priv->oclk_prsc); + netc_timer_wr(priv, NETC_TMR_TEMASK, 0); /* Disable FIPER by default */ fiper_ctrl = netc_timer_rd(priv, NETC_TMR_FIPER_CTRL); @@ -901,6 +902,11 @@ static irqreturn_t netc_timer_isr(int irq, void *data) /* Clear interrupts status */ netc_timer_wr(priv, NETC_TMR_TEVENT, tmr_event); + if (!tmr_event) { + spin_unlock(&priv->lock); + return IRQ_NONE; + } + if (tmr_event & TMR_TEVENT_ALMEN(0)) netc_timer_alarm_write(priv, NETC_TMR_DEFAULT_ALARM, 0); @@ -936,7 +942,8 @@ static int netc_timer_init_msix_irq(struct netc_timer *priv) } priv->irq = pci_irq_vector(pdev, 0); - err = request_irq(priv->irq, netc_timer_isr, 0, priv->irq_name, priv); + err = request_irq(priv->irq, netc_timer_isr, IRQF_NO_AUTOEN, + priv->irq_name, priv); if (err) { dev_err(&pdev->dev, "request_irq() failed\n"); pci_free_irq_vectors(pdev); @@ -951,7 +958,6 @@ static void netc_timer_free_msix_irq(struct netc_timer *priv) { struct pci_dev *pdev = priv->pdev; - disable_irq(priv->irq); free_irq(priv->irq, priv); pci_free_irq_vectors(pdev); } @@ -1005,6 +1011,8 @@ static int netc_timer_probe(struct pci_dev *pdev, goto free_msix_irq; } + enable_irq(priv->irq); + return 0; free_msix_irq: @@ -1019,9 +1027,10 @@ static void netc_timer_remove(struct pci_dev *pdev) { struct netc_timer *priv = pci_get_drvdata(pdev); + disable_irq(priv->irq); + ptp_clock_unregister(priv->clock); netc_timer_wr(priv, NETC_TMR_TEMASK, 0); netc_timer_wr(priv, NETC_TMR_CTRL, 0); - ptp_clock_unregister(priv->clock); netc_timer_free_msix_irq(priv); netc_timer_pci_remove(pdev); } -- cgit v1.2.3 From 70fd0cf29bc47882c1cb11ad4fb2881ac2c1e640 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Sun, 26 Jul 2026 22:56:08 -0300 Subject: net: dsa: realtek: rtl8365mb: use devm_mutex_init for mib_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_DEBUG_MUTEXES enabled, mutex_destroy() needs to be called before the lock is discarded. Use devm_mutex_init() instead so the cleanup is handled automatically. Fixes: 4af2950c50c86 ("net: dsa: realtek-smi: add rtl8365mb subdriver for RTL8365MB-VC") Reviewed-by: Mieczyslaw Nalewaj Signed-off-by: Luiz Angelo Daros de Luca Reviewed-by: Linus Walleij Reviewed-by: Alvin Šipraga Link: https://patch.msgid.link/20260726-realtek_mutext-v2-1-5d62ba998791@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/dsa/realtek/rtl8365mb_main.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c index 5ac091bf93c9..aa05375b090a 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -1988,16 +1988,19 @@ static void rtl8365mb_get_stats64(struct dsa_switch *ds, int port, spin_unlock(&p->stats_lock); } -static void rtl8365mb_stats_setup(struct realtek_priv *priv) +static int rtl8365mb_stats_setup(struct realtek_priv *priv) { struct rtl8365mb *mb = priv->chip_data; struct dsa_switch *ds = &priv->ds; struct dsa_port *dp; + int ret; /* Per-chip global mutex to protect MIB counter access, since doing * so requires accessing a series of registers in a particular order. */ - mutex_init(&mb->mib_lock); + ret = devm_mutex_init(priv->dev, &mb->mib_lock); + if (ret) + return ret; dsa_switch_for_each_available_port(dp, ds) { struct rtl8365mb_port *p = &mb->ports[dp->index]; @@ -2010,6 +2013,8 @@ static void rtl8365mb_stats_setup(struct realtek_priv *priv) */ INIT_DELAYED_WORK(&p->mib_work, rtl8365mb_stats_poll); } + + return 0; } static void rtl8365mb_stats_teardown(struct realtek_priv *priv) @@ -2567,7 +2572,12 @@ static int rtl8365mb_setup(struct dsa_switch *ds) } /* Start statistics counter polling */ - rtl8365mb_stats_setup(priv); + ret = rtl8365mb_stats_setup(priv); + if (ret) { + dev_err(priv->dev, "failed to setup stats: %pe\n", + ERR_PTR(ret)); + goto out_teardown_irq; + } return 0; -- cgit v1.2.3 From 050e07f8765d84b4e74fae239ff7a9f29eb6869c Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Sun, 26 Jul 2026 22:56:09 -0300 Subject: net: dsa: realtek: use devm_mutex_init for regmap lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_DEBUG_MUTEXES enabled, mutex_destroy() needs to be called before the lock is discarded. Use devm_mutex_init() instead so the cleanup is handled automatically. Fixes: 907e772f6f6de ("net: dsa: realtek: allow subdrivers to externally lock regmap") Reviewed-by: Mieczyslaw Nalewaj Signed-off-by: Luiz Angelo Daros de Luca Reviewed-by: Linus Walleij Reviewed-by: Alvin Šipraga Link: https://patch.msgid.link/20260726-realtek_mutext-v2-2-5d62ba998791@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/dsa/realtek/rtl83xx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c index 71124ecca92f..9402bfe4f85a 100644 --- a/drivers/net/dsa/realtek/rtl83xx.c +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -156,7 +156,10 @@ rtl83xx_probe(struct device *dev, if (!priv) return ERR_PTR(-ENOMEM); - mutex_init(&priv->map_lock); + ret = devm_mutex_init(dev, &priv->map_lock); + if (ret) + return ERR_PTR(ret); + mutex_init(&priv->vlan_lock); mutex_init(&priv->l2_lock); -- cgit v1.2.3 From a95f3e9b8985fc0e21bfcf727e941c1f03476927 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Sun, 26 Jul 2026 22:56:10 -0300 Subject: net: dsa: realtek: use devm_mutex_init for vlan_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_DEBUG_MUTEXES enabled, mutex_destroy() needs to be called before the lock is discarded. Use devm_mutex_init() instead so the cleanup is handled automatically. Fixes: 9da2c8672f771 ("net: dsa: realtek: rtl8365mb: add VLAN support") Reviewed-by: Mieczyslaw Nalewaj Signed-off-by: Luiz Angelo Daros de Luca Reviewed-by: Linus Walleij Reviewed-by: Alvin Šipraga Link: https://patch.msgid.link/20260726-realtek_mutext-v2-3-5d62ba998791@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/dsa/realtek/rtl83xx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c index 9402bfe4f85a..9f40afb19ab2 100644 --- a/drivers/net/dsa/realtek/rtl83xx.c +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -160,7 +160,10 @@ rtl83xx_probe(struct device *dev, if (ret) return ERR_PTR(ret); - mutex_init(&priv->vlan_lock); + ret = devm_mutex_init(dev, &priv->vlan_lock); + if (ret) + return ERR_PTR(ret); + mutex_init(&priv->l2_lock); rc.lock_arg = priv; -- cgit v1.2.3 From 442ecdc83d00d6c2312541c4e0ada47e02805fcb Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Sun, 26 Jul 2026 22:56:11 -0300 Subject: net: dsa: realtek: use devm_mutex_init for l2_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_DEBUG_MUTEXES enabled, mutex_destroy() needs to be called before the lock is discarded. Use devm_mutex_init() instead so the cleanup is handled automatically. Fixes: 336e3e4a1ab37 ("net: dsa: realtek: rtl8365mb: add FDB support") Reviewed-by: Mieczyslaw Nalewaj Signed-off-by: Luiz Angelo Daros de Luca Reviewed-by: Linus Walleij Reviewed-by: Alvin Šipraga Link: https://patch.msgid.link/20260726-realtek_mutext-v2-4-5d62ba998791@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/dsa/realtek/rtl83xx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c index 9f40afb19ab2..9dd50b20c000 100644 --- a/drivers/net/dsa/realtek/rtl83xx.c +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -164,7 +164,9 @@ rtl83xx_probe(struct device *dev, if (ret) return ERR_PTR(ret); - mutex_init(&priv->l2_lock); + ret = devm_mutex_init(dev, &priv->l2_lock); + if (ret) + return ERR_PTR(ret); rc.lock_arg = priv; priv->map = devm_regmap_init(dev, NULL, priv, &rc); -- cgit v1.2.3 From 9cbc63400f7dc000adf898bbbc8208dfd93091d7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 28 Jul 2026 09:15:39 +0300 Subject: thunderbolt: Initialize ->domain_released completion before it is being used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both Woody and Marek reported following crash: BUG: unable to handle page fault for address: fffffffffffffff8 Call Trace: device_release+0x43/0x90 kobject_cleanup+0x3c/0x180 icm_probe+0x19c/0x550 [thunderbolt] nhi_probe+0x1a4/0x370 [thunderbolt] local_pci_probe+0x41/0x90 pci_call_probe+0x5b/0x1a0 ... This only triggers on the error path when icm_probe() fails and the domain structure is released, it tries to complete() uninitialized completion. Fix this by initializing the completion earlier. Reported-by: Marek Marczykowski-Górecki Closes: https://lore.kernel.org/linux-usb/amdezCBiW4fd_DuB@mail-itl/ Reported-by: Woody Suwalski Tested_by: Woody Suwalski Closes: https://lore.kernel.org/linux-usb/62caf7f8-b403-d0dd-15bc-b31b56f71c28@gmail.com/ Fixes: f5cc545f5969 ("thunderbolt: Wait for tb_domain_release() to complete when driver is removed") Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 0f795ea58756..35e3c119d5ee 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1226,6 +1226,8 @@ int nhi_probe(struct tb_nhi *nhi) return dev_err_probe(dev, res, "NHI specific init failed\n"); } + init_completion(&nhi->domain_released); + tb = nhi_select_cm(nhi); if (!tb) return dev_err_probe(dev, -ENODEV, @@ -1233,8 +1235,6 @@ int nhi_probe(struct tb_nhi *nhi) dev_dbg(dev, "NHI initialized, starting thunderbolt\n"); - init_completion(&nhi->domain_released); - res = tb_domain_add(tb, host_reset); if (res) { /* -- cgit v1.2.3 From 438f4896f78f69ec73d5f32d2c024193f1223569 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 20 Jul 2026 11:34:21 +1000 Subject: crypto: ccm - Set rfc4309 maxauthsize from child Set the maxauthsize of rfc4309 using that of the child algorithm. Fixes: 4a49b499dfa0 ("[CRYPTO] ccm: Added CCM mode") Reported-by: Seohyeon Maeng Signed-off-by: Herbert Xu --- crypto/ccm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/ccm.c b/crypto/ccm.c index 2ae929ffdef8..916441e4f2b8 100644 --- a/crypto/ccm.c +++ b/crypto/ccm.c @@ -747,7 +747,7 @@ static int crypto_rfc4309_create(struct crypto_template *tmpl, inst->alg.ivsize = 8; inst->alg.chunksize = crypto_aead_alg_chunksize(alg); - inst->alg.maxauthsize = 16; + inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg); inst->alg.base.cra_ctxsize = sizeof(struct crypto_rfc4309_ctx); -- cgit v1.2.3 From 360f2974fcea49c61f6d6f81554741a9eeee7168 Mon Sep 17 00:00:00 2001 From: Vladislav Dronov Date: Tue, 21 Jul 2026 00:33:31 +0200 Subject: crypto: tegra - fix rctx->cryptlen calculation in tegra_gcm_do_one_req() Perform rctx->cryptlen calculation in tegra_gcm_do_one_req() the same way it is done in tegra_ccm_crypt_init(). The current formulae may lead to a crash if a caller does not call tegra_gcm_setauthsize() and so ctx->authsize remains zero. Then a decrypt operation with incorrect rctx->cryptlen will lead to a write beyound rctx->dst_sg buffer. As a follow-up cleanup delete struct tegra_aead_ctx->authsize field since it appears to be completely unused. Also simplify tegra_ccm_setauthsize() and tegra_gcm_setauthsize() functions respectively. Fixes: 0880bb3b00c8 ("crypto: tegra - Add Tegra Security Engine driver") Signed-off-by: Vladislav Dronov Signed-off-by: Herbert Xu --- drivers/crypto/tegra/tegra-se-aes.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 9094c03e991f..0fd1d7035899 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -45,7 +45,6 @@ struct tegra_aes_reqctx { struct tegra_aead_ctx { struct tegra_se *se; - unsigned int authsize; u32 alg; u32 key_id; u32 keylen; @@ -1290,7 +1289,7 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) if (rctx->encrypt) rctx->cryptlen = req->cryptlen; else - rctx->cryptlen = req->cryptlen - ctx->authsize; + rctx->cryptlen = req->cryptlen - rctx->authsize; memcpy(rctx->iv, req->iv, GCM_AES_IV_SIZE); rctx->iv[3] = (1 << 24); @@ -1394,8 +1393,6 @@ static int tegra_aead_cra_init(struct crypto_aead *tfm) static int tegra_ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { - struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); - switch (authsize) { case 4: case 6: @@ -1404,28 +1401,15 @@ static int tegra_ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsize case 12: case 14: case 16: - break; + return 0; default: return -EINVAL; } - - ctx->authsize = authsize; - - return 0; } static int tegra_gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { - struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); - int ret; - - ret = crypto_gcm_check_authsize(authsize); - if (ret) - return ret; - - ctx->authsize = authsize; - - return 0; + return crypto_gcm_check_authsize(authsize); } static void tegra_aead_cra_exit(struct crypto_aead *tfm) -- cgit v1.2.3 From 1132c38927886259c694f77cd74fc577c2216eeb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 22 Jul 2026 12:27:48 +0200 Subject: rhashtable: fix false-positive lockdep splat on rhltable destruction Blamed commit removed ht->mutex usage during destruction but forgot to switch rhashtable_free_one() to rcu_dereference_raw(), this triggers a lockdep splat when an rhltable gets zapped. Fixes: 09ae540e1d5c ("rhashtable: drop ht->mutex in rhashtable_free_and_destroy()") Signed-off-by: Florian Westphal Reviewed-by: Mikhail Gavrilov Signed-off-by: Herbert Xu --- lib/rhashtable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rhashtable.c b/lib/rhashtable.c index d459bef245f4..8b2c405e7a66 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -1261,7 +1261,7 @@ static void rhashtable_free_one(struct rhashtable *ht, struct rhash_head *obj, list = container_of(obj, struct rhlist_head, rhead); do { obj = &list->rhead; - list = rht_dereference(list->next, ht); + list = rcu_dereference_raw(list->next); free_fn(rht_obj(ht, obj), arg); } while (list); } -- cgit v1.2.3 From 9c75402286409f5e1a75e4a445555c84066f89db Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 24 Jul 2026 10:15:38 +0200 Subject: crypto: qce - fix error path in devm_qce_register_algs If ops->register_algs() fails, the error path repeatedly calls the same ops->unregister_algs() from the failed registration. Use the loop index to unregister the previously registered algorithms instead. Fixes: e80cf84b6087 ("crypto: qce - unregister previously registered algos in error path") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Bartosz Golaszewski Signed-off-by: Herbert Xu --- drivers/crypto/qce/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/qce/core.c b/drivers/crypto/qce/core.c index b966f3365b7d..7f005d1fca40 100644 --- a/drivers/crypto/qce/core.c +++ b/drivers/crypto/qce/core.c @@ -59,7 +59,7 @@ static int devm_qce_register_algs(struct qce_device *qce) ret = ops->register_algs(qce); if (ret) { for (j = i - 1; j >= 0; j--) - ops->unregister_algs(qce); + qce_ops[j]->unregister_algs(qce); return ret; } } -- cgit v1.2.3 From 6b36f13891ab4709b7d60023005176cdd5c368cf Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 25 Jul 2026 11:06:10 +0200 Subject: crypto: starfive - use scatterlist length before DMA mapping Using sg_dma_len() is only valid after mapping a scatterlist with dma_map_sg(). However, starfive_aes_aead_do_one_req() uses it before mapping the scatterlist. Use the original scatterlist length because the DMA length has not been populated yet when CONFIG_NEED_SG_DMA_LENGTH=y. Fixes: 7467147ef9bf ("crypto: starfive - Use dma for aes requests") Cc: stable@vger.kernel.org Signed-off-by: Thorsten Blum Signed-off-by: Herbert Xu --- drivers/crypto/starfive/jh7110-aes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/starfive/jh7110-aes.c b/drivers/crypto/starfive/jh7110-aes.c index a0713aa21250..f59adb2a5651 100644 --- a/drivers/crypto/starfive/jh7110-aes.c +++ b/drivers/crypto/starfive/jh7110-aes.c @@ -677,7 +677,7 @@ static int starfive_aes_aead_do_one_req(struct crypto_engine *engine, void *areq if (cryp->total_in) sg_zero_buffer(rctx->in_sg, sg_nents(rctx->in_sg), - sg_dma_len(rctx->in_sg) - cryp->total_in, + rctx->in_sg->length - cryp->total_in, cryp->total_in); ctx->rctx = rctx; -- cgit v1.2.3 From 285d8204638cf8be0dc304dc40f0290ada701340 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 26 Jul 2026 21:27:16 +0200 Subject: crypto: af_alg - Allow cbc(paes) Commit 7524070f26d8 ("crypto: af_alg - Drop support for off-CPU cryptography") breaks a special use case. The cbc-paes-caam driver implements the algorithm cbc(paes), it offers a way to use AES in CBC mode with key material unknown to userspace. Instead of an AES key a CAAM BLOB is passed to the kernel. So, this crypto operation cannot be implemented in a userspace library and needs always help from the kernel. Explicitly allow this use case. Cc: Demi Marie Obenour Suggested-by: Eric Biggers Fixes: 7524070f26d8 ("crypto: af_alg - Drop support for off-CPU cryptography") Signed-off-by: Richard Weinberger Reviewed-by: Eric Biggers Signed-off-by: Herbert Xu --- crypto/algif_skcipher.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index df20bdfe1f1f..035fed7db81f 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -32,6 +32,7 @@ #include #include #include +#include #include static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, @@ -309,7 +310,12 @@ static struct proto_ops algif_skcipher_ops_nokey = { static void *skcipher_bind(const char *name) { - return crypto_alloc_skcipher(name, 0, AF_ALG_CRYPTOAPI_MASK); + u32 mask = AF_ALG_CRYPTOAPI_MASK; + + if (strcmp(name, "cbc(paes)") == 0) + mask = 0; + + return crypto_alloc_skcipher(name, 0, mask); } static void skcipher_release(void *private) -- cgit v1.2.3 From 935dc3bb20e0fee89b598e10d1f5f1f500ddc79a Mon Sep 17 00:00:00 2001 From: Marco Baffo Date: Wed, 22 Jul 2026 06:47:56 +0200 Subject: ovpn: limit keepalive values to one day Large keepalive values can overflow the delayed-work delay on 32-bit systems, causing the keepalive worker to be repeatedly scheduled. A correct configuration should not require such large keepalive values, and an upper limit of one day is already generous and unnecessary in practice. Limit both the keepalive interval and timeout to 86400 seconds. Signed-off-by: Marco Baffo Signed-off-by: Antonio Quartulli --- Documentation/netlink/specs/ovpn.yaml | 4 ++++ drivers/net/ovpn/netlink-gen.c | 20 ++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml index b0c782e59a32..ac50d1d7c00a 100644 --- a/Documentation/netlink/specs/ovpn.yaml +++ b/Documentation/netlink/specs/ovpn.yaml @@ -118,12 +118,16 @@ attribute-sets: doc: >- The number of seconds after which a keep alive message is sent to the peer + checks: + max: 86400 - name: keepalive-timeout type: u32 doc: >- The number of seconds from the last activity after which the peer is assumed dead + checks: + max: 86400 - name: del-reason type: u32 diff --git a/drivers/net/ovpn/netlink-gen.c b/drivers/net/ovpn/netlink-gen.c index 2147cec7c2c5..92d2fdc17c2e 100644 --- a/drivers/net/ovpn/netlink-gen.c +++ b/drivers/net/ovpn/netlink-gen.c @@ -16,6 +16,14 @@ static const struct netlink_range_validation ovpn_a_peer_id_range = { .max = 16777215ULL, }; +static const struct netlink_range_validation ovpn_a_peer_keepalive_interval_range = { + .max = 86400ULL, +}; + +static const struct netlink_range_validation ovpn_a_peer_keepalive_timeout_range = { + .max = 86400ULL, +}; + static const struct netlink_range_validation ovpn_a_peer_tx_id_range = { .max = 16777215ULL, }; @@ -68,8 +76,8 @@ const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16), [OVPN_A_PEER_LOCAL_PORT] = NLA_POLICY_MIN(NLA_BE16, 1), - [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, }, - [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, }, + [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range), + [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range), [OVPN_A_PEER_DEL_REASON] = NLA_POLICY_MAX(NLA_U32, 4), [OVPN_A_PEER_VPN_RX_BYTES] = { .type = NLA_UINT, }, [OVPN_A_PEER_VPN_TX_BYTES] = { .type = NLA_UINT, }, @@ -97,8 +105,8 @@ const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_VPN_IPV6] = NLA_POLICY_EXACT_LEN(16), [OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16), - [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, }, - [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, }, + [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range), + [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range), [OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range), }; @@ -112,8 +120,8 @@ const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_VPN_IPV6] = NLA_POLICY_EXACT_LEN(16), [OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16), - [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, }, - [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, }, + [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range), + [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range), [OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range), }; -- cgit v1.2.3 From 33ec10567fe14456063daf549fdf1a4f53448e4c Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:47 +0200 Subject: ovpn: skip rehash for peers already removed from by_id ovpn_nl_peer_set_doit() resolves the target peer via ovpn_peer_get_by_id() before taking ovpn->lock. In the window between the lookup (which only takes a refcount) and the subsequent spin_lock_bh(&ovpn->lock), a concurrent OVPN_CMD_PEER_DEL, keepalive expiry, or socket teardown can take ovpn->lock first, run ovpn_peer_remove() to unhash the peer from all four tables (by_id, by_vpn_addr4/6, by_transp_addr) and release the lock. set_doit then acquires ovpn->lock and calls ovpn_peer_hash_vpn_ip(), which re-inserts the now-removed peer back into the rehashing tables. The same race affects the float path: ovpn_peer_endpoints_update() holds only a refcount and acquires ovpn->lock very late (after async AEAD decrypt and a netlink notification), then rehashes the peer in the by_transp_addr table. The resurrected peer becomes reachable again from the RX lookup (ovpn_peer_get_by_transp_addr) and the TX VPN-IP lookup, even though userspace believes it is gone. Once the data-path refcount drops the peer is freed via call_rcu while the hash entries embedded in it remain linked, opening a UAF window. Bail out of the rehash when hash_entry_id is unhashed, mirroring the sentinel already used by ovpn_peer_remove() to detect the already-removed state. The check is safe under ovpn->lock, which serializes every mutation of hash_entry_id, and is a no-op for the add path because ovpn_peer_add_mp() inserts hash_entry_id before calling ovpn_peer_hash_vpn_ip(). Fixes: 1d36a36f6d53 ("ovpn: implement peer add/get/dump/delete via netlink") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 73 +++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a21d02ac715e..68021c0c1783 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -297,40 +297,46 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) /* rehashing is required only in MP mode as P2P has one peer * only and thus there is no hashtable */ - if (peer->ovpn->mode == OVPN_MODE_MP) { - spin_lock_bh(&peer->ovpn->lock); - spin_lock_bh(&peer->lock); - bind = rcu_dereference_protected(peer->bind, - lockdep_is_held(&peer->lock)); - if (unlikely(!bind)) { - spin_unlock_bh(&peer->lock); - spin_unlock_bh(&peer->ovpn->lock); - return; - } + if (peer->ovpn->mode != OVPN_MODE_MP) + return; - /* This function may be invoked concurrently, therefore another - * float may have happened in parallel: perform rehashing - * using the peer->bind->remote directly as key - */ + spin_lock_bh(&peer->ovpn->lock); + spin_lock_bh(&peer->lock); + bind = rcu_dereference_protected(peer->bind, + lockdep_is_held(&peer->lock)); + if (unlikely(!bind)) + goto unlock2; - switch (bind->remote.in4.sin_family) { - case AF_INET: - salen = sizeof(*sa); - break; - case AF_INET6: - salen = sizeof(*sa6); - break; - } + /* peer may have been concurrently removed between the caller's + * initial lookup and our acquisition of ovpn->lock; skip the + * rehash so we don't re-insert a removed peer + */ + if (unlikely(hlist_unhashed(&peer->hash_entry_id))) + goto unlock2; - /* remove old hashing */ - hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); - /* re-add with new transport address */ - nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, - &bind->remote, salen); - hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); - spin_unlock_bh(&peer->lock); - spin_unlock_bh(&peer->ovpn->lock); + /* This function may be invoked concurrently, therefore another + * float may have happened in parallel: perform rehashing + * using the peer->bind->remote directly as key + */ + + switch (bind->remote.in4.sin_family) { + case AF_INET: + salen = sizeof(*sa); + break; + case AF_INET6: + salen = sizeof(*sa6); + break; } + + /* remove old hashing */ + hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); + /* re-add with new transport address */ + nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, + &bind->remote, salen); + hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); +unlock2: + spin_unlock_bh(&peer->lock); + spin_unlock_bh(&peer->ovpn->lock); return; unlock: spin_unlock_bh(&peer->lock); @@ -906,6 +912,13 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer) if (peer->ovpn->mode != OVPN_MODE_MP) return; + /* peer may have been concurrently removed between the caller's + * initial lookup and our acquisition of ovpn->lock; skip the + * rehash so we don't re-insert a removed peer + */ + if (hlist_unhashed(&peer->hash_entry_id)) + return; + if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) { /* remove potential old hashing */ hlist_nulls_del_init_rcu(&peer->hash_entry_addr4); -- cgit v1.2.3 From cc12f7240c8c4dee557749d33237542613992f14 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:48 +0200 Subject: ovpn: rehash peer in by_transp_addr table on CMD_PEER_SET When userspace updates a peer's remote endpoint via OVPN_CMD_PEER_SET, ovpn_nl_peer_modify() installs a new ovpn_bind through ovpn_peer_reset_sockaddr(), but ovpn_nl_peer_set_doit() only calls ovpn_peer_hash_vpn_ip() to refresh the VPN-IP hashtables. The peer is left in the bucket of peers->by_transp_addr corresponding to its old remote address. As a consequence, datagrams arriving at the UDP RX path from the newly configured remote hash to a different slot and the lockless lookup in ovpn_peer_get_by_transp_addr() (called from ovpn_udp_encap_recv()) does not find the peer, until either a float event or a peer re-add fixes the bucket. Introduce ovpn_peer_hash_transp_addr() (modeled after ovpn_peer_hash_vpn_ip()) and invoke it from ovpn_nl_peer_set_doit() whenever the request carried a new remote address. The helper bails out in P2P mode and on peers without a bind (TCP), and relies on hlist_nulls_del_init_rcu()'s pprev==NULL short-circuit to handle the case of an entry not currently linked in the table. Fixes: 1d36a36f6d53 ("ovpn: implement peer add/get/dump/delete via netlink") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/netlink.c | 6 +++ drivers/net/ovpn/peer.c | 105 +++++++++++++++++++++++++++++++-------------- drivers/net/ovpn/peer.h | 1 + 3 files changed, 79 insertions(+), 33 deletions(-) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 4c66c1ec497e..4dad85294198 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -534,6 +534,12 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info) */ if (ret > 0) ovpn_peer_hash_vpn_ip(peer); + /* if the remote endpoint was updated, the by_transp_addr hash bucket + * also needs to be refreshed, otherwise incoming packets from the new + * remote address would fail the lockless lookup + */ + if (attrs[OVPN_A_PEER_REMOTE_IPV4] || attrs[OVPN_A_PEER_REMOTE_IPV6]) + ovpn_peer_hash_transp_addr(peer); spin_unlock_bh(&ovpn->lock); ovpn_peer_put(peer); diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 68021c0c1783..a330892e82bf 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -189,6 +189,9 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, &(*__tbl1)[ovpn_get_hash_slot(*__tbl1, _key, _key_len)];\ }) +static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, + const struct ovpn_bind *bind); + /** * ovpn_peer_endpoints_update - update remote or local endpoint for peer * @peer: peer to update the remote endpoint for @@ -196,7 +199,6 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, */ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) { - struct hlist_nulls_head *nhead; struct sockaddr_storage ss; struct sockaddr_in6 *sa6; bool reset_cache = false; @@ -295,46 +297,23 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) ovpn_nl_peer_float_notify(peer, &ss); /* rehashing is required only in MP mode as P2P has one peer - * only and thus there is no hashtable + * only and thus there is no hashtable. + * + * This function may be invoked concurrently, so re-read peer->bind + * under the proper locks and rehash against its current value. */ if (peer->ovpn->mode != OVPN_MODE_MP) return; + /* This function may be invoked concurrently, therefore another + * float may have happened in parallel: re-acquire the locks and + * rehash using the peer->bind->remote directly as key + */ spin_lock_bh(&peer->ovpn->lock); spin_lock_bh(&peer->lock); bind = rcu_dereference_protected(peer->bind, lockdep_is_held(&peer->lock)); - if (unlikely(!bind)) - goto unlock2; - - /* peer may have been concurrently removed between the caller's - * initial lookup and our acquisition of ovpn->lock; skip the - * rehash so we don't re-insert a removed peer - */ - if (unlikely(hlist_unhashed(&peer->hash_entry_id))) - goto unlock2; - - /* This function may be invoked concurrently, therefore another - * float may have happened in parallel: perform rehashing - * using the peer->bind->remote directly as key - */ - - switch (bind->remote.in4.sin_family) { - case AF_INET: - salen = sizeof(*sa); - break; - case AF_INET6: - salen = sizeof(*sa6); - break; - } - - /* remove old hashing */ - hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); - /* re-add with new transport address */ - nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, - &bind->remote, salen); - hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); -unlock2: + __ovpn_peer_hash_transp_addr(peer, bind); spin_unlock_bh(&peer->lock); spin_unlock_bh(&peer->ovpn->lock); return; @@ -902,6 +881,66 @@ bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb, return match; } +/* Move @peer to the by_transp_addr bucket matching its current bind. + * + * Caller must hold both peer->ovpn->lock and peer->lock, and must have + * already dereferenced a valid (non-NULL) peer->bind, passed in as @bind. + */ +static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, + const struct ovpn_bind *bind) +{ + struct hlist_nulls_head *nhead; + size_t salen; + + lockdep_assert_held(&peer->ovpn->lock); + lockdep_assert_held(&peer->lock); + + if (WARN_ON_ONCE(!bind)) + return; + + /* peer may have been concurrently removed between the caller's + * initial lookup and our acquisition of ovpn->lock; skip the + * rehash so we don't re-insert a removed peer + */ + if (unlikely(hlist_unhashed(&peer->hash_entry_id))) + return; + + switch (bind->remote.in4.sin_family) { + case AF_INET: + salen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + salen = sizeof(struct sockaddr_in6); + break; + default: + return; + } + + /* remove old hashing (no-op if entry is not currently linked) */ + hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); + /* re-add with current transport address */ + nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, + &bind->remote, salen); + hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); +} + +void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer) +{ + struct ovpn_bind *bind; + + lockdep_assert_held(&peer->ovpn->lock); + + /* rehashing makes sense only in multipeer mode */ + if (peer->ovpn->mode != OVPN_MODE_MP) + return; + + spin_lock_bh(&peer->lock); + bind = rcu_dereference_protected(peer->bind, + lockdep_is_held(&peer->lock)); + __ovpn_peer_hash_transp_addr(peer, bind); + spin_unlock_bh(&peer->lock); +} + void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer) { struct hlist_nulls_head *nhead; diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index 86c8cffada6d..dfa5c0037e02 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -150,6 +150,7 @@ struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_priv *ovpn, u32 peer_id); struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn, struct sk_buff *skb); void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer); +void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer); bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb, struct ovpn_peer *peer); -- cgit v1.2.3 From 59aed1eb60d70678a53acccb0cb337a26ce6680e Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:50 +0200 Subject: ovpn: ensure socket is owned by ovpn before deref sk_user_data Some subsystems, like BPF SOCKMAP, set sk_user_data without actually setting the encap_type. For this reason, we must make sure that the type is the one ovpn expects before dereferencing sk_user_data. Failing to do so may lead to out-of-bounds reads. Fixes: f6226ae7a0cd ("ovpn: introduce the ovpn_socket object") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/socket.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c index 517caa64a4fe..6cbeb2caaeec 100644 --- a/drivers/net/ovpn/socket.c +++ b/drivers/net/ovpn/socket.c @@ -162,6 +162,15 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) rcu_read_lock(); ovpn_sock = rcu_dereference_sk_user_data(sk); if (ovpn_sock) { + /* something else filled the sk_user_data without + * setting the encap_type. Reject the socket. + */ + if (!type) { + ovpn_sock = ERR_PTR(-EBUSY); + rcu_read_unlock(); + goto sock_release; + } + /* socket owned by another ovpn instance, we can't use it */ if (ovpn_sock->ovpn != peer->ovpn) { ovpn_sock = ERR_PTR(-EBUSY); -- cgit v1.2.3 From 3f012bdbabe211ccbc0c50ea5a1dbc60f8af1532 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:51 +0200 Subject: ovpn: zero-initialize sockaddr before learning a floated endpoint ovpn_peer_endpoints_update() builds the new remote endpoint in an on-stack struct sockaddr_storage that is left uninitialized. For IPv4 only sin_family/sin_addr/sin_port are written, leaving the 8-byte sin_zero padding as stack garbage (for IPv6, sin6_flowinfo is left uninitialized likewise). ovpn_peer_reset_sockaddr() -> ovpn_bind_from_sockaddr() then memcpy()s sizeof(struct sockaddr_in)/sizeof(struct sockaddr_in6) bytes - padding included - into bind->remote. That buffer is later hashed with jhash() over the same length to place the peer in the by_transp_addr table, so the garbage padding lands the floated peer in an essentially random bucket. Lockless lookups in ovpn_peer_get_by_transp_addr() build their key from a zero-initialized sockaddr_storage, compute a different bucket and fail to find the peer. This is also a plain use of uninitialized stack memory in jhash(). Build the floated endpoint with a designated initializer so the padding (sin_zero for IPv4, sin6_flowinfo for IPv6) is zeroed as part of the assignment. This keeps the padding out of the by_transp_addr hash key without memset-ing the whole sockaddr_storage on every received packet. Fixes: f0281c1d3732 ("ovpn: add support for updating local or remote UDP endpoint") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a330892e82bf..33fb0a75e600 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -222,9 +222,16 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) */ local_ip = &ip_hdr(skb)->daddr; sa = (struct sockaddr_in *)&ss; - sa->sin_family = AF_INET; - sa->sin_addr.s_addr = ip_hdr(skb)->saddr; - sa->sin_port = udp_hdr(skb)->source; + /* use a designated initializer so the sin_zero padding + * is zeroed (it ends up in the by_transp_addr hash key) + * without memset-ing the whole sockaddr_storage on the + * RX fast path + */ + *sa = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_addr.s_addr = ip_hdr(skb)->saddr, + .sin_port = udp_hdr(skb)->source, + }; salen = sizeof(*sa); reset_cache = true; break; @@ -250,11 +257,19 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) */ local_ip = &ipv6_hdr(skb)->daddr; sa6 = (struct sockaddr_in6 *)&ss; - sa6->sin6_family = AF_INET6; - sa6->sin6_addr = ipv6_hdr(skb)->saddr; - sa6->sin6_port = udp_hdr(skb)->source; - sa6->sin6_scope_id = ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr, - skb->skb_iif); + /* use a designated initializer so the sin6_flowinfo + * padding is zeroed (it ends up in the by_transp_addr + * hash key) without memset-ing the whole + * sockaddr_storage on the RX fast path + */ + *sa6 = (struct sockaddr_in6) { + .sin6_family = AF_INET6, + .sin6_addr = ipv6_hdr(skb)->saddr, + .sin6_port = udp_hdr(skb)->source, + .sin6_scope_id = + ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr, + skb->skb_iif), + }; salen = sizeof(*sa6); reset_cache = true; break; -- cgit v1.2.3 From b47a52dcd598a50207a33df304acdf45348a690f Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:52 +0200 Subject: ovpn: hash floated peer by transport identity only The by_transp_addr table is keyed on the peer's remote transport address, but the float rehash hashed bind->remote directly, while the two other sites that touch the table build a clean key first: ovpn_peer_add_mp() and the lookup in ovpn_peer_get_by_transp_addr() both hash a sockaddr holding only family/address/port. For a link-local IPv6 peer, bind->remote carries sin6_scope_id (set from ipv6_iface_scope_id() when the endpoint is learned), and that field is folded into the jhash() over sizeof(struct sockaddr_in6). The lookup never sets sin6_scope_id, so after such a peer floats it is rehashed into a scope_id-dependent bucket that lookups (scope_id 0) never visit, making the peer unreachable through the by_transp_addr fallback. ovpn_peer_transp_match() only compares address and port, so the hash was keying on a field the match ignores. sin6_scope_id must stay in bind->remote because the TX path uses it as flowi6_oif, so it cannot just be cleared there. Instead build the hash key from family/address/port only, exactly like ovpn_peer_add_mp() and the lookup, so all three sites agree on the bucket. Fixes: f0281c1d3732 ("ovpn: add support for updating local or remote UDP endpoint") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 33fb0a75e600..eada414a9d92 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -904,7 +904,10 @@ bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb, static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, const struct ovpn_bind *bind) { + struct sockaddr_storage sa = {}; struct hlist_nulls_head *nhead; + struct sockaddr_in6 *sa6; + struct sockaddr_in *sa4; size_t salen; lockdep_assert_held(&peer->ovpn->lock); @@ -920,12 +923,26 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, if (unlikely(hlist_unhashed(&peer->hash_entry_id))) return; + /* Build the hash key from the transport identity only + * (family/address/port), matching ovpn_peer_add_mp() and the lookup + * in ovpn_peer_get_by_transp_addr(). Hashing bind->remote directly + * would fold in sin6_scope_id (set on the float path but never by the + * lookup), scattering the peer into a bucket lookups cannot reach. + */ switch (bind->remote.in4.sin_family) { case AF_INET: - salen = sizeof(struct sockaddr_in); + sa4 = (struct sockaddr_in *)&sa; + sa4->sin_family = AF_INET; + sa4->sin_addr.s_addr = bind->remote.in4.sin_addr.s_addr; + sa4->sin_port = bind->remote.in4.sin_port; + salen = sizeof(*sa4); break; case AF_INET6: - salen = sizeof(struct sockaddr_in6); + sa6 = (struct sockaddr_in6 *)&sa; + sa6->sin6_family = AF_INET6; + sa6->sin6_addr = bind->remote.in6.sin6_addr; + sa6->sin6_port = bind->remote.in6.sin6_port; + salen = sizeof(*sa6); break; default: return; @@ -934,8 +951,8 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, /* remove old hashing (no-op if entry is not currently linked) */ hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); /* re-add with current transport address */ - nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, - &bind->remote, salen); + nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, &sa, + salen); hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); } -- cgit v1.2.3 From 0301aa324941698bec3dd455df1c5abc7afb10db Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:53 +0200 Subject: ovpn: disable IPv4 redirects on MP interfaces ovpn_mp_alloc() tried to disable SEND_REDIRECTS on a multipeer interface, but it runs from ovpn_net_init() (->ndo_init), which register_netdevice() invokes before the NETDEV_REGISTER notifier chain. The IPv4 in_device is only created when that notifier reaches inetdev_event() -> inetdev_init(), so __in_dev_get_rtnl() always returned NULL at ndo_init time and the whole redirect-disabling block (both the per-device and the per-netns IPV4_DEVCONF_ALL write) was dead. MP interfaces therefore kept emitting ICMP redirects. Disabling redirects only once is not enough either: the IPv4 in_device is destroyed and recreated when the interface is moved to a different network namespace (NETDEV_UNREGISTER/NETDEV_REGISTER), and the newly created in_device inherits the destination namespace defaults, silently re-enabling SEND_REDIRECTS. Disable redirects from ovpn_net_open() (->ndo_open) instead: it runs every time the interface is brought up, including after the in_device has been recreated, so the setting is always re-applied. This mirrors what wireguard does in wg_open(). RTNL is held on the ndo_open() path, so __in_dev_get_rtnl() is safe. Fixes: 05003b408c20 ("ovpn: implement multi-peer support") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 50 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 9d9a0ff690d6..3a04757d5c31 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -35,25 +35,11 @@ static void ovpn_priv_free(struct net_device *net) static int ovpn_mp_alloc(struct ovpn_priv *ovpn) { - struct in_device *dev_v4; int i; if (ovpn->mode != OVPN_MODE_MP) return 0; - dev_v4 = __in_dev_get_rtnl(ovpn->dev); - if (dev_v4) { - /* disable redirects as Linux gets confused by ovpn - * handling same-LAN routing. - * This happens because a multipeer interface is used as - * relay point between hosts in the same subnet, while - * in a classic LAN this would not be needed because the - * two hosts would be able to talk directly. - */ - IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false); - IPV4_DEVCONF_ALL(dev_net(ovpn->dev), SEND_REDIRECTS) = false; - } - /* the peer container is fairly large, therefore we allocate it only in * MP mode */ @@ -97,9 +83,38 @@ static void ovpn_net_uninit(struct net_device *dev) gro_cells_destroy(&ovpn->gro_cells); } +static int ovpn_net_open(struct net_device *dev) +{ + struct ovpn_priv *ovpn = netdev_priv(dev); + struct in_device *dev_v4; + + /* the IPv4 in_device (and thus its config) is recreated whenever the + * interface is moved to a new netns, so redirects must be disabled on + * every bring-up rather than once at creation time, otherwise the + * setting is silently lost after such a move + */ + if (ovpn->mode == OVPN_MODE_MP) { + dev_v4 = __in_dev_get_rtnl(dev); + if (dev_v4) { + /* disable redirects as Linux gets confused by ovpn + * handling same-LAN routing. + * This happens because a multipeer interface is used as + * relay point between hosts in the same subnet, while + * in a classic LAN this would not be needed because the + * two hosts would be able to talk directly. + */ + IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false); + IPV4_DEVCONF_ALL(dev_net(dev), SEND_REDIRECTS) = false; + } + } + + return 0; +} + static const struct net_device_ops ovpn_netdev_ops = { .ndo_init = ovpn_net_init, .ndo_uninit = ovpn_net_uninit, + .ndo_open = ovpn_net_open, .ndo_start_xmit = ovpn_net_xmit, }; @@ -183,6 +198,7 @@ static int ovpn_newlink(struct net_device *dev, struct ovpn_priv *ovpn = netdev_priv(dev); struct nlattr **data = params->data; enum ovpn_mode mode = OVPN_MODE_P2P; + int ret; if (data && data[IFLA_OVPN_MODE]) { mode = nla_get_u8(data[IFLA_OVPN_MODE]); @@ -207,7 +223,11 @@ static int ovpn_newlink(struct net_device *dev, else netif_carrier_off(dev); - return register_netdevice(dev); + ret = register_netdevice(dev); + if (ret < 0) + return ret; + + return 0; } static size_t ovpn_get_size(const struct net_device *dev) -- cgit v1.2.3 From 4680c0ebd958fc18e53c8b91d80436b236a8fc09 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:54 +0200 Subject: ovpn: ensure TCP vars are initialized first Netlink calls may access TCP global vars (i.e. when attaching a TCP socket), therefore we need to make sure the latters are initialized beforehand. For this reason move the global TCP initialization at the top of the module init function. Fixes: 11851cbd60ea ("ovpn: implement TCP transport") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 3a04757d5c31..168cfe9b59a9 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -260,8 +260,11 @@ static struct rtnl_link_ops ovpn_link_ops = { static int __init ovpn_init(void) { - int err = rtnl_link_register(&ovpn_link_ops); + int err; + ovpn_tcp_init(); + + err = rtnl_link_register(&ovpn_link_ops); if (err) { pr_err("ovpn: can't register rtnl link ops: %d\n", err); return err; @@ -273,8 +276,6 @@ static int __init ovpn_init(void) goto unreg_rtnl; } - ovpn_tcp_init(); - return 0; unreg_rtnl: -- cgit v1.2.3 From 26ba17d845193dac4921ae1ab280d28d1938052e Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Fri, 24 Jul 2026 09:49:26 +0800 Subject: ovpn: fix incorrect use of rcu_access_pointer() rcu_access_pointer() should only be used to test the value of a pointer, not to dereference it. As it's in a spin_lock_bh() critical section, use rcu_dereference_bh() instead, avoiding an extra rcu_read_lock(). Fixes: f6226ae7a0cd ("ovpn: introduce the ovpn_socket object") Signed-off-by: Qingfang Deng Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index eada414a9d92..b0519f9840d8 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -1249,7 +1249,7 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk, } if (sk) { - ovpn_sock = rcu_access_pointer(peer->sock); + ovpn_sock = rcu_dereference_bh(peer->sock); if (!ovpn_sock || ovpn_sock->sk != sk) { spin_unlock_bh(&ovpn->lock); return; -- cgit v1.2.3 From 2b8f13d3c7e26c46c20d9e367904cf01729c88e6 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Fri, 24 Jul 2026 19:26:20 +0200 Subject: drm/panthor: skip zero-sized firmware sections panthor_fw_load_section_entry() skips BO creation when the firmware section VA range is empty. If such a section is added to the firmware section list, section->mem is left as NULL. Later reload and unplug paths iterate over all firmware sections and dereference section->mem, which can lead to a NULL pointer dereference. Zero-sized firmware sections are valid, so accept them as no-op entries but skip adding them to the section list. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Liviu Dudau Reviewed-by: Steven Price Link: https://patch.msgid.link/20260724172621.63046-1-osama.abdelkader@gmail.com Signed-off-by: Steven Price --- drivers/gpu/drm/panthor/panthor_fw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index e45933e333b5..fa88b2f9da76 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -603,6 +603,9 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, return -EINVAL; } + if (!section_size) + return 0; + name_len = iter->size - iter->offset; section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL); -- cgit v1.2.3 From 732ed8f75ce583d115716f668dc80d730f3ad610 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Fri, 24 Jul 2026 15:46:57 +0800 Subject: net: libwx: fix FDIR ATR queue mismatch for software VLAN packets When TX VLAN hardware offload is disabled, VLAN tags are embedded in the packet payload (software VLAN). Previously, the driver failed to set the WX_TX_FLAGS_SW_VLAN flag for these packets during transmission. This missing flag caused the txgbe FDIR ATR logic to fall through to the default hash calculation path. This resulted in asymmetric hash values for Tx and Rx flows, preventing return packets from being steered to the same queue as the transmit packets. Fix this by detecting software VLANs via eth_type_vlan(skb->protocol) and setting WX_TX_FLAGS_SW_VLAN. This ensures the ATR feature selects the correct hashing algorithm to maintain Tx/Rx queue symmetry. Fixes: b501d261a5b3 ("net: txgbe: add FDIR ATR support") Signed-off-by: Jiawen Wu Reviewed-by: Simon Horman Link: https://patch.msgid.link/0879DA38A8E32701+20260724074657.10773-1-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 814d88d2aee4..5d99e870de5e 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1606,6 +1606,8 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb, if (skb_vlan_tag_present(skb)) { tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT; tx_flags |= WX_TX_FLAGS_HW_VLAN; + } else if (eth_type_vlan(skb->protocol)) { + tx_flags |= WX_TX_FLAGS_SW_VLAN; } if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && -- cgit v1.2.3 From 16809472409d998afcda402e32b8229b389337c4 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Fri, 24 Jul 2026 12:58:31 +0530 Subject: octeontx2-pf: Set correct sequence for carrier off and tx queue stop During link down event, we were doing netif_tx_stop_all_queues() first and then netif_carrier_off(). This can cause a potential race since carrier is still on during down event. This patch reverse the calling order to fix the issue. Fixes: 50fe6c02e5ad ("octeontx2-pf: Register and handle link notifications") Signed-off-by: Suman Ghosh Signed-off-by: Ratheesh Kannoth Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260724072831.2415281-1-rkannoth@marvell.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 2e33b33ec993..c995f2900859 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -889,8 +889,8 @@ static void otx2_handle_link_event(struct otx2_nic *pf) netif_carrier_on(netdev); netif_tx_start_all_queues(netdev); } else { - netif_tx_stop_all_queues(netdev); netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); } } -- cgit v1.2.3 From 1842bf97af109f5ebf830175c9725bf81ebb78b1 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Fri, 22 May 2026 14:58:33 +0200 Subject: sched/deadline: Use revised wakeup rule only for running dl_server Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") applies the revised wakeup rule to any server, as a result servers that are not running (dl_defer_running == 0) and start with a deadline overflow get enqueued and can boost tasks as if they were running, invalidating the defer rule and the documented state model. Apply the revised wakeup rule only for deferrable servers that are marked as running. Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") Signed-off-by: Gabriele Monaco Signed-off-by: Peter Zijlstra (Intel) Acked-by: Juri Lelli Tested-by: Andrea Righi Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com --- kernel/sched/deadline.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 0f858b98c9aa..200300043fa5 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1017,7 +1017,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se) if (dl_time_before(dl_se->deadline, rq_clock(rq)) || dl_entity_overflow(dl_se, rq_clock(rq))) { - if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) && + if (unlikely((!dl_is_implicit(dl_se) || + (dl_se->dl_defer && dl_se->dl_defer_running)) && !dl_time_before(dl_se->deadline, rq_clock(rq)) && !is_dl_boosted(dl_se))) { update_dl_revised_wakeup(dl_se, rq); -- cgit v1.2.3 From a58a2b0ce354df531ebc71fc870058c2feb59f6b Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Mon, 27 Jul 2026 14:10:21 +0200 Subject: net: openvswitch: fix potential UAF on meter attach failure While attaching a newly created meter attach_meter() function makes the new meter visible to other CPUs but can still fail afterwards. On failure, it detaches the meter back and returns an error. However, this is an unexpected behavior for the ovs_meter_cmd_set() that uses a plain kfree(meter) on attach failure without waiting for RCU readers to stop using it, assuming it was never visible. This is never a problem for ovs-vswitchd as it always creates meters before creating any flows that use them. But the UAF can be triggered with a custom application using uAPI: BUG: KASAN: slab-use-after-free in ovs_meter_execute (net/openvswitch/meter.c:653) Read of size 8 at addr ffff88810d152650 by task meter/2508 Call Trace: ovs_meter_execute (net/openvswitch/meter.c:653) do_execute_actions (net/openvswitch/actions.c:1407) ovs_execute_actions (net/openvswitch/actions.c:1584) ovs_packet_cmd_execute (net/openvswitch/datapath.c:703) ... netlink_sendmsg (af_netlink.c:1900) Allocated by task 2519: __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415) ovs_meter_cmd_set (net/openvswitch/meter.c:422) ... netlink_sendmsg (af_netlink.c:1900) Freed by task 2519: kfree (mm/slub.c:2705 mm/slub.c:6405 mm/slub.c:6720) ovs_meter_cmd_set (net/openvswitch/meter.c:479) ... netlink_sendmsg (af_netlink.c:1900) Fix that by making sure attach_meter() doesn't make the meter visible until all the checks are done and the function can't fail anymore. This also makes sure the "hash" value is calculated after the potential re-sizing of the table. Reported by Trend Micro's Zero Day Initiative as ZDI-CAN-31642. Fixes: c7c4c44c9a95 ("net: openvswitch: expand the meters supported number") Cc: stable@vger.kernel.org Signed-off-by: Ilya Maximets Reviewed-by: Eelco Chaudron Link: https://patch.msgid.link/20260727121022.198461-1-i.maximets@ovn.org Signed-off-by: Paolo Abeni --- net/openvswitch/meter.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/net/openvswitch/meter.c b/net/openvswitch/meter.c index a02c47277337..4aaeeae3af5b 100644 --- a/net/openvswitch/meter.c +++ b/net/openvswitch/meter.c @@ -133,18 +133,10 @@ static void dp_meter_instance_remove(struct dp_meter_instance *ti, static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) { - struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti); - u32 hash = meter_hash(ti, meter->id); + struct dp_meter_instance *ti; + u32 hash; int err; - /* In generally, slots selected should be empty, because - * OvS uses id-pool to fetch a available id. - */ - if (unlikely(rcu_dereference_ovsl(ti->dp_meters[hash]))) - return -EBUSY; - - dp_meter_instance_insert(ti, meter); - /* That function is thread-safe. */ tbl->count++; if (tbl->count >= tbl->max_meters_allowed) { @@ -152,16 +144,29 @@ static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter) goto attach_err; } - if (tbl->count >= ti->n_meters && - dp_meter_instance_realloc(tbl, ti->n_meters * 2)) { - err = -ENOMEM; + ti = rcu_dereference_ovsl(tbl->ti); + if (tbl->count >= ti->n_meters) { + err = dp_meter_instance_realloc(tbl, ti->n_meters * 2); + if (err) + goto attach_err; + + ti = rcu_dereference_ovsl(tbl->ti); + } + + hash = meter_hash(ti, meter->id); + + /* In general, selected slots should be empty, because + * OvS uses id-pool to fetch available ids. + */ + if (unlikely(rcu_dereference_ovsl(ti->dp_meters[hash]))) { + err = -EBUSY; goto attach_err; } + dp_meter_instance_insert(ti, meter); return 0; attach_err: - dp_meter_instance_remove(ti, meter); tbl->count--; return err; } -- cgit v1.2.3 From 9c19d60fea9f46ed0c3394653ef4941f1a459968 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 28 Jul 2026 18:18:08 +0800 Subject: spi: spi-nxp-fspi: add per-SoC SDR/DTR clock rate limits for all supported SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection") introduced a global 166MHz cap for DTR mode (RXCLKSRC=3), based on the i.MX8MN datasheet timing specification (Section 3.9.9, page 65). After reviewing the FlexSPI timing parameters in the datasheets for all supported SoCs, the following corrections and additions are needed: 1. SDR mode (RXCLKSRC=0) limits vary per SoC: - i.MX8MN/MM/MP/95: 66MHz (IMX8MNCEC §3.9.9, IMX8MMCEC §3.9.10, IMX8MPCEC, IMX95CEC Rev.8 §4.11.7) - i.MX8QXP/QM/DXL/ULP: 60MHz (IMX8QXPCEC, IMX8QMCEC, IMX8DXLCEC, IMX8ULPCEC §7.3.1 ND mode) - LX2160A: 100MHz (LX2160ACEC FlexSPI timing parameters) 2. DTR mode (RXCLKSRC=3) limits vary per SoC: - i.MX8MN/MM/MP/ULP: 166MHz - i.MX8QXP/QM/DXL: 200MHz (same FlexSPI IP across this family) - i.MX95: 200MHz (IMX95CEC §4.11.7.3.2.3 Table 106) - LX2160A: DTR disabled (FSPI_QUIRK_DISABLE_DTR) Update related platform data with correct speed limation according to datasheet. Fixes: f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection") Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20260728-fspi-clock-v2-1-dbe786a4a6eb@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 83 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 1e36ae084dd8..39c1eaaf9e0a 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -340,6 +340,18 @@ struct nxp_fspi_devtype_data { unsigned int quirks; unsigned int lut_num; bool little_endian; + /* + * The max clock rate (Hz) that FlexSPI can output to the device + * in SDR mode (RXCLKSRC=0). Defaults to 66MHz if zero. + * Some SoCs (e.g. LX2160A) support up to 100MHz in SDR mode. + */ + unsigned long max_sdr_rate; + /* + * The max clock rate (Hz) that FlexSPI can output to the device + * in DTR mode (RXCLKSRC=3). Defaults to 166MHz if zero. + * Some SoCs (e.g. i.MX95, i.MX8QM, i.MX8DXL) support up to 200MHz. + */ + unsigned long max_dtr_rate; }; static struct nxp_fspi_devtype_data lx2160a_data = { @@ -349,6 +361,10 @@ static struct nxp_fspi_devtype_data lx2160a_data = { .quirks = FSPI_QUIRK_DISABLE_DTR, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * LX2160ACEC: SDR RXCLKSRC=0 max 100MHz, DTR disabled via quirk. + */ + .max_sdr_rate = 100000000, }; static struct nxp_fspi_devtype_data imx8mm_data = { @@ -358,6 +374,21 @@ static struct nxp_fspi_devtype_data imx8mm_data = { .quirks = 0, .lut_num = 32, .little_endian = true, /* little-endian */ + /* IMX8MMCEC §3.9.10: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 166000000, +}; + +static struct nxp_fspi_devtype_data imx8mp_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ + /* IMX8MPCEC: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 166000000, }; static struct nxp_fspi_devtype_data imx8qxp_data = { @@ -367,6 +398,12 @@ static struct nxp_fspi_devtype_data imx8qxp_data = { .quirks = 0, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * IMX8QXPCEC: SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 200MHz. + * i.MX8QM and i.MX8DXL share the same FlexSPI IP and limits. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 200000000, }; static struct nxp_fspi_devtype_data imx8dxl_data = { @@ -376,6 +413,12 @@ static struct nxp_fspi_devtype_data imx8dxl_data = { .quirks = FSPI_QUIRK_USE_IP_ONLY, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * IMX8DXLCEC (i.MX 8XLite): SDR RXCLKSRC=0 max 60MHz, + * DDR RXCLKSRC=3 max 200MHz. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 200000000, }; static struct nxp_fspi_devtype_data imx8ulp_data = { @@ -385,6 +428,29 @@ static struct nxp_fspi_devtype_data imx8ulp_data = { .quirks = 0, .lut_num = 16, .little_endian = true, /* little-endian */ + /* + * IMX8ULPCEC §7.3.1, Normal Drive (ND, 1.0V) mode: + * SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 166MHz. + * Note: Overdrive (OD, 1.05V) allows up to 180MHz DTR + * but is not the default use case. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 166000000, +}; + +static struct nxp_fspi_devtype_data imx95_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ + /* + * IMX95CEC Rev.8 §4.11.7: SDR RXCLKSRC=0 max 66MHz, + * DDR RXCLKSRC=3 max 200MHz (Nominal/Overdrive mode). + */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 200000000, }; struct nxp_fspi { @@ -691,10 +757,20 @@ static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f, reg = fspi_readl(f, f->iobase + FSPI_MCR0); if (op_is_dtr) { reg |= FSPI_MCR0_RXCLKSRC(3); - f->max_rate = 166000000; + /* + * Use the SoC-specific DTR max rate if provided, otherwise + * fall back to 166MHz (limit from IMX8MN datasheet §3.9.9). + */ + f->max_rate = f->devtype_data->max_dtr_rate ? + f->devtype_data->max_dtr_rate : 166000000; } else { /*select mode 0 */ reg &= ~FSPI_MCR0_RXCLKSRC(3); - f->max_rate = 66000000; + /* + * Use the SoC-specific SDR max rate if provided, otherwise + * fall back to 66MHz (limit from IMX8MN datasheet §3.9.9). + */ + f->max_rate = f->devtype_data->max_sdr_rate ? + f->devtype_data->max_sdr_rate : 66000000; } fspi_writel(f, reg, f->iobase + FSPI_MCR0); } @@ -1444,10 +1520,11 @@ static const struct dev_pm_ops nxp_fspi_pm_ops = { static const struct of_device_id nxp_fspi_dt_ids[] = { { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, }, { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, }, - { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, }, + { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mp_data, }, { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, }, { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, }, { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, }, + { .compatible = "nxp,imx95-fspi", .data = (void *)&imx95_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids); -- cgit v1.2.3 From b4bde5cfff8e43e948219f0a598e4bf057ecfba4 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 28 Jul 2026 18:18:09 +0800 Subject: spi: spi-nxp-fspi: enter stop mode before reconfiguring MCR0 and DLL In nxp_fspi_select_mem() the RX sample clock source (MCR0[RXCLKSRC]) and the DLL control registers (DLLxCR) are reconfigured while the FlexSPI module is still enabled. According to the FlexSPI reference manual initialization sequence, MCR0 and the DLL control registers should be programmed while the module is in stop mode, i.e. with MCR0[MDIS] set to 1, and the module re-enabled (MCR0[MDIS] = 0) afterwards. Wrap the RX sample clock source selection and the DLL calibration/ override reconfiguration in a stop-mode window to align with the RM and avoid reconfiguring timing-critical registers while the module is active. Signed-off-by: Haibo Chen Reviewed-by: Frank Li Link: https://patch.msgid.link/20260728-fspi-clock-v2-2-dbe786a4a6eb@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 39c1eaaf9e0a..54355295f919 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -867,6 +867,7 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, unsigned long rate = op->max_freq; int ret; uint64_t size_kb; + u32 reg; /* * Return when following condition all meet, @@ -896,6 +897,15 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, dev_dbg(f->dev, "Target device [CS:%x] selected\n", spi_get_chipselect(spi, 0)); + /* + * Per the FlexSPI reference manual (initialization sequence), MCR0 and + * the DLL control registers should be configured while the module is in + * stop mode (MCR0[MDIS] = 1). Enter stop mode before reconfiguring the + * RX sample clock source and the DLL, then exit stop mode afterwards. + */ + reg = fspi_readl(f, f->iobase + FSPI_MCR0); + fspi_writel(f, reg | FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); + nxp_fspi_select_rx_sample_clk_source(f, op_is_dtr); rate = min(f->max_rate, op->max_freq); @@ -928,6 +938,10 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, else nxp_fspi_dll_override(f); + /* Exit stop mode now that MCR0 and the DLL have been reconfigured. */ + reg = fspi_readl(f, f->iobase + FSPI_MCR0); + fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); + f->pre_op_rate = op->max_freq; f->selected = spi_get_chipselect(spi, 0); -- cgit v1.2.3 From b5902b9779796d515b7d65eb9205994b7a8d00cb Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 28 Jul 2026 18:18:10 +0800 Subject: spi: spi-nxp-fspi: propagate clock reconfig failures in nxp_fspi_select_mem() nxp_fspi_select_mem() disables the FlexSPI clocks before calling clk_set_rate() and re-enabling them. If clk_set_rate() or the clock re-enable fails, the function returned early (as void) leaving both the serial root clock and the register interface clock disabled. As the function returned void, nxp_fspi_exec_op() had no way to know about the failure and continued to access FlexSPI registers (LUT setup, data transfer, AHB buffer invalidation). Accessing the controller registers while its clock is gated off results in a synchronous external abort. Make nxp_fspi_select_mem() return an error code and have nxp_fspi_exec_op() bail out on failure before any further register access, including nxp_fspi_invalid(). Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20260728-fspi-clock-v2-3-dbe786a4a6eb@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 54355295f919..4425132432ec 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -859,8 +859,8 @@ static void nxp_fspi_dll_override(struct nxp_fspi *f) * Value for rest of the CS FLSHxxCR0 register would be zero. * */ -static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, - const struct spi_mem_op *op) +static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, + const struct spi_mem_op *op) { /* flexspi only support one DTR mode: 8D-8D-8D */ bool op_is_dtr = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr; @@ -881,7 +881,7 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, if ((f->selected == spi_get_chipselect(spi, 0)) && (!!(f->flags & FSPI_DTR_MODE) == op_is_dtr) && (f->pre_op_rate == op->max_freq)) - return; + return 0; /* Reset FLSHxxCR0 registers */ fspi_writel(f, 0, f->iobase + FSPI_FLSHA1CR0); @@ -922,12 +922,19 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, nxp_fspi_clk_disable_unprep(f); ret = clk_set_rate(f->clk, rate); - if (ret) - return; + if (ret) { + /* + * clk_set_rate() failed with the clocks already disabled. + * Re-enable them so the enable count matches what the caller's + * pm_runtime_put() (runtime_suspend) will drop. + */ + nxp_fspi_clk_prep_enable(f); + return ret; + } ret = nxp_fspi_clk_prep_enable(f); if (ret) - return; + return ret; /* * If clock rate > 100MHz, then switch from DLL override mode to @@ -945,6 +952,8 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, f->pre_op_rate = op->max_freq; f->selected = spi_get_chipselect(spi, 0); + + return 0; } static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op) @@ -1132,7 +1141,16 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true); WARN_ON(err); - nxp_fspi_select_mem(f, mem->spi, op); + err = nxp_fspi_select_mem(f, mem->spi, op); + if (err) { + /* + * On failure the FlexSPI clock may be left disabled, so avoid + * any further register access (which would trigger a synchronous + * external abort) and bail out. + */ + pm_runtime_put_autosuspend(f->dev); + return err; + } nxp_fspi_prepare_lut(f, op); /* -- cgit v1.2.3 From 451c9075d6c53f2438d110addbeeeea6fac18567 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Sun, 26 Jul 2026 12:43:11 +0200 Subject: qede: sync udp_tunnel ports outside qede_lock in the recovery path A TX timeout on a qede NIC that has VXLAN/GENEVE tunnel ports configured wedges the rtnetlink control plane of the whole machine: NETDEV WATCHDOG: ens6f1 (qede): transmit queue 2 timed out 10226 ms [qede_tx_timeout:586(ens6f1)]TX timeout on queue 2! [qede_recovery_handler:2665(ens6f0)]Starting a recovery process The recovery path deadlocks on the driver's own mutex: qede_sp_task rtnl_lock() mutex_lock(&edev->qede_lock) <- taken qede_recovery_handler qede_load udp_tunnel_nic_reset_ntf __udp_tunnel_nic_device_sync info->sync_table == qede_udp_tunnel_sync mutex_lock(&edev->qede_lock) <- same task: deadlock The mutex is not recursive, so the kworker blocks on itself with rtnl_lock held, and neither lock is ever released. Every task that calls rtnl_lock() afterwards (ip, ovs-vswitchd, lldpad, IPv6 addrconf, sshd) blocks forever while the node still answers ping. In a vmcore from an affected production node rtnl_mutex.owner decodes to the very kworker blocked at the innermost mutex_lock() above. Re-sync the tunnel ports from qede_sp_task() after the internal lock is dropped, still under rtnl_lock as the udp_tunnel API requires. This mirrors qede_open(), which calls udp_tunnel_nic_reset_ntf() under rtnl without the internal lock. qede_recovery_handler() now returns whether it has successfully reloaded an open device, and the caller re-syncs the ports only in that case. This keeps the old gating exactly: a device that was down or a failed recovery returns false, as those paths never reached the udp_tunnel_nic_reset_ntf() call before either. This was the only user of the qede_lock()/qede_unlock() helpers, so remove them. Fixes: 8cd160a29415 ("qede: convert to new udp_tunnel_nic infra") Signed-off-by: Denis V. Lunev CC: Andrew Lunn CC: "David S. Miller" CC: Eric Dumazet CC: Jakub Kicinski CC: Paolo Abeni Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260726104311.1782900-1-den@openvz.org Signed-off-by: Paolo Abeni --- drivers/net/ethernet/qlogic/qede/qede_main.c | 44 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index cb0ae0650905..7ed17faced54 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -107,7 +107,7 @@ static void qede_remove(struct pci_dev *pdev); static void qede_shutdown(struct pci_dev *pdev); static void qede_link_update(void *dev, struct qed_link_output *link); static void qede_schedule_recovery_handler(void *dev); -static void qede_recovery_handler(struct qede_dev *edev); +static bool qede_recovery_handler(struct qede_dev *edev); static void qede_schedule_hw_err_handler(void *dev, enum qed_hw_err_type err_type); static void qede_get_eth_tlv_data(void *edev, void *data); @@ -1043,21 +1043,6 @@ void __qede_unlock(struct qede_dev *edev) mutex_unlock(&edev->qede_lock); } -/* This version of the lock should be used when acquiring the RTNL lock is also - * needed in addition to the internal qede lock. - */ -static void qede_lock(struct qede_dev *edev) -{ - rtnl_lock(); - __qede_lock(edev); -} - -static void qede_unlock(struct qede_dev *edev) -{ - __qede_unlock(edev); - rtnl_unlock(); -} - static void qede_periodic_task(struct work_struct *work) { struct qede_dev *edev = container_of(work, struct qede_dev, @@ -1094,6 +1079,8 @@ static void qede_sp_task(struct work_struct *work) */ if (test_and_clear_bit(QEDE_SP_RECOVERY, &edev->sp_flags)) { + bool reloaded; + cancel_delayed_work_sync(&edev->periodic_task); #ifdef CONFIG_QED_SRIOV /* SRIOV must be disabled outside the lock to avoid a deadlock. @@ -1102,9 +1089,17 @@ static void qede_sp_task(struct work_struct *work) if (pci_num_vf(edev->pdev)) qede_sriov_configure(edev->pdev, 0); #endif - qede_lock(edev); - qede_recovery_handler(edev); - qede_unlock(edev); + rtnl_lock(); + __qede_lock(edev); + reloaded = qede_recovery_handler(edev); + __qede_unlock(edev); + + /* The udp_tunnel core synchronously calls back into + * qede_udp_tunnel_sync(), which takes the qede lock. + */ + if (reloaded) + udp_tunnel_nic_reset_ntf(edev->ndev); + rtnl_unlock(); } __qede_lock(edev); @@ -2645,9 +2640,13 @@ static void qede_recovery_failed(struct qede_dev *edev) edev->ops->common->set_power_state(edev->cdev, PCI_D3hot); } -static void qede_recovery_handler(struct qede_dev *edev) +/* Returns true if an open device was successfully reloaded and its + * udp_tunnel ports need to be re-synced by the caller. + */ +static bool qede_recovery_handler(struct qede_dev *edev) { u32 curr_state = edev->state; + bool reloaded = false; int rc; DP_NOTICE(edev, "Starting a recovery process\n"); @@ -2677,17 +2676,18 @@ static void qede_recovery_handler(struct qede_dev *edev) goto err; qede_config_rx_mode(edev->ndev); - udp_tunnel_nic_reset_ntf(edev->ndev); + reloaded = true; } edev->state = curr_state; DP_NOTICE(edev, "Recovery handling is done\n"); - return; + return reloaded; err: qede_recovery_failed(edev); + return false; } static void qede_atomic_hw_err_handler(struct qede_dev *edev) -- cgit v1.2.3 From 246ac114f485c2affb454240f3ea4fabfce22456 Mon Sep 17 00:00:00 2001 From: Luca Fresi Date: Wed, 22 Jul 2026 00:24:04 +0200 Subject: serial: sc16is7xx: enable THRI before filling TX FIFO sc16is7xx_handle_tx() currently requests the THRI enable only after it has filled the TX FIFO. The request is asynchronous because the IER update is performed later by reg_work. The SC16IS7xx generates a THRI interrupt when the TX FIFO crosses its trigger level. If the FIFO drains past that level before reg_work enables THRI, the chip does not generate a new interrupt. Characters remain queued indefinitely even though the hardware FIFO is empty. This was observed on an SC16IS752 while both UART channels were active. During the stall the software TX buffer remained non-empty while TXLVL reported 64 bytes free, LSR reported THR and transmitter empty, IER had THRI enabled, and IIR reported no interrupt pending. Enable THRI synchronously before filling the FIFO so the threshold crossing cannot be missed. Fixes: cc4c1d05eb10 ("sc16is7xx: Properly resume TX after stop") Cc: stable Signed-off-by: Luca Fresi Link: https://patch.msgid.link/20260721222404.204746-1-luca.fresi@bithiatec.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 6c97953d593b..9b152ead050f 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -827,6 +827,9 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) msleep(port->rs485.delay_rts_before_send); guard(mutex)(&one->lock); + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_IER_THRI_BIT, + SC16IS7XX_IER_THRI_BIT); sc16is7xx_handle_tx(port); } -- cgit v1.2.3 From e2fe6a0efecbef00e3ecc2db64dd5afa8c212b41 Mon Sep 17 00:00:00 2001 From: Cunhao Lu <1579567540@qq.com> Date: Mon, 27 Jul 2026 14:25:22 +0800 Subject: serial: 8250_dma: Clear stale RX state on shutdown serial8250_release_dma() terminates RX DMA and releases the channel, but leaves rx_running set. If the port is closed while an RX transfer is active, the stale state remains while rxchan is NULL until the channel is requested again on the next open. The DesignWare BUSY workaround added by commit a7b9ce39fbe4 ("serial: 8250_dw: Ensure BUSY is deasserted") calls serial8250_rx_dma_flush() from the LCR write path during startup. This happens before serial8250_request_dma() obtains a new RX channel. On reopen, the stale rx_running state therefore makes the flush path pass a NULL channel to dmaengine_pause(), causing a kernel Oops. Clear rx_running after terminating RX DMA, matching the TX cleanup. Also make the flush helper return if the DMA object or RX channel is not available so startup and teardown paths cannot pass a NULL channel to the DMAengine API. Fixes: 0fcb7901f9d6 ("tty: serial: 8250_dma: keep own book keeping about RX transfers") Cc: stable Signed-off-by: Cunhao Lu <1579567540@qq.com> Link: https://patch.msgid.link/tencent_9EE2945F4C933B4D810C73C2D7485E000F06@qq.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 3b6452e759d5..5a83e5269b41 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -211,11 +211,12 @@ void serial8250_rx_dma_flush(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; - if (dma->rx_running) { - dmaengine_pause(dma->rxchan); - __dma_rx_complete(p); - dmaengine_terminate_async(dma->rxchan); - } + if (!dma || !dma->rxchan || !dma->rx_running) + return; + + dmaengine_pause(dma->rxchan); + __dma_rx_complete(p); + dmaengine_terminate_async(dma->rxchan); } EXPORT_SYMBOL_GPL(serial8250_rx_dma_flush); @@ -324,6 +325,7 @@ void serial8250_release_dma(struct uart_8250_port *p) /* Release RX resources */ dmaengine_terminate_sync(dma->rxchan); + dma->rx_running = 0; dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, dma->rx_addr); dma_release_channel(dma->rxchan); -- cgit v1.2.3 From e3c04834ae1ab5e9cfbe8ac54ec734aa4774249d Mon Sep 17 00:00:00 2001 From: Jan Sebastian Götte Date: Wed, 29 Jul 2026 19:41:05 +0200 Subject: serial: qcom-geni: fix TX DMA buffer flush MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When transmit flushing a qcom-geni UART during an ongoing TX DMA, the UART gets stuck infinitely repeating corrupted TX DMA frames. The DMA-mode uart_ops does not provide a flush_buffer callback, so an in-flight transfer can complete after serial core has reset the transmit kfifo, underflowing its length and resubmitting page-sized transfers indefinitely. Add one that stops the transfer and clears tx_remaining and tx_queued. The stop path was also broken: it unmapped the buffer while the serial engine could still read it, and never reset the TX DMA state machine. Cancel the main sequencer command first, then reset the state machine and wait for it before unmapping. Drop the early return so a pending mapping is also cleaned up when the main command is inactive. The bug can be triggered from userspace with a large write immediately followed by TCOFLUSH. A following tcdrain will hang forever. The bug was reproduced and this fix was validated on Arduino Uno Q (QRB2210) using /dev/ttyHS1. Assisted-by: Claude:claude-5-opus Codex:gpt-5 Signed-off-by: Jan Sebastian Götte Fixes: 2aaa43c70778 ("tty: serial: qcom-geni-serial: add support for serial engine DMA") Cc: stable Reviewed-by: Praveen Talari Link: https://patch.msgid.link/20260729174105.21838-2-git@jaseg.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 43 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 7ead87b4eb65..1e39122ec09f 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -158,6 +158,7 @@ static const struct uart_ops qcom_geni_uart_pops; static struct uart_driver qcom_geni_console_driver; static struct uart_driver qcom_geni_uart_driver; +static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport); static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); static int qcom_geni_serial_port_setup(struct uart_port *uport); @@ -636,35 +637,34 @@ static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport) return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS); } +static void qcom_geni_serial_flush_buffer_dma(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + + qcom_geni_serial_stop_tx_dma(uport); + port->tx_remaining = 0; + port->tx_queued = 0; +} + static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); - bool done; - if (!qcom_geni_serial_main_active(uport)) - return; + if (qcom_geni_serial_main_active(uport)) + __qcom_geni_serial_cancel_tx_cmd(uport); if (port->tx_dma_addr) { + writel(1, uport->membase + SE_DMA_TX_FSM_RST); + if (!qcom_geni_serial_poll_bit(uport, SE_DMA_TX_IRQ_STAT, + TX_RESET_DONE, true)) + dev_err_ratelimited(uport->dev, "TX DMA reset failed"); + writel(TX_RESET_DONE | TX_DMA_DONE, + uport->membase + SE_DMA_TX_IRQ_CLR); + geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr = 0; - port->tx_remaining = 0; } - - geni_se_cancel_m_cmd(&port->se); - - done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_CANCEL_EN, true); - if (!done) { - geni_se_abort_m_cmd(&port->se); - done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); - if (!done) - dev_err_ratelimited(uport->dev, "M_CMD_ABORT_EN not set"); - writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); - } - - writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) @@ -1180,7 +1180,7 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) uart_port_unlock_irq(uport); } -static void qcom_geni_serial_flush_buffer(struct uart_port *uport) +static void qcom_geni_serial_flush_buffer_fifo(struct uart_port *uport) { qcom_geni_serial_cancel_tx_cmd(uport); } @@ -1769,7 +1769,7 @@ static const struct uart_ops qcom_geni_console_pops = { .request_port = qcom_geni_serial_request_port, .config_port = qcom_geni_serial_config_port, .shutdown = qcom_geni_serial_shutdown, - .flush_buffer = qcom_geni_serial_flush_buffer, + .flush_buffer = qcom_geni_serial_flush_buffer_fifo, .type = qcom_geni_serial_get_type, .set_mctrl = qcom_geni_serial_set_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl, @@ -1792,6 +1792,7 @@ static const struct uart_ops qcom_geni_uart_pops = { .request_port = qcom_geni_serial_request_port, .config_port = qcom_geni_serial_config_port, .shutdown = qcom_geni_serial_shutdown, + .flush_buffer = qcom_geni_serial_flush_buffer_dma, .type = qcom_geni_serial_get_type, .set_mctrl = qcom_geni_serial_set_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl, -- cgit v1.2.3 From c77ffbc980efb337fd750c337d8157d532ea14e5 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 26 Jul 2026 08:12:38 -0600 Subject: io_uring/net: initialize mshot_len for send Commit: 6a8afb9fff64 ("io_uring/net: allow multishot receive per-invocation cap") changed how io_mshot_prep_retry() set sr->len, and added the same initialization in io_mshot_prep_retry(). But it neglected to touch the send path, which may also uses the mshot retry path. Ensure that sr->mshot_len always gets initialized correctly. Fixes: 6a8afb9fff64 ("io_uring/net: allow multishot receive per-invocation cap") Cc: stable@vger.kernel.org Reported-by: Sung Keum Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Jens Axboe --- io_uring/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/io_uring/net.c b/io_uring/net.c index 00a7df803b99..a74d15f7b7d2 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -445,6 +445,7 @@ int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) req->flags |= REQ_F_NOWAIT; if (req->flags & REQ_F_BUFFER_SELECT) sr->buf_group = req->buf_index; + sr->mshot_total_len = sr->mshot_len = 0; if (sr->flags & IORING_RECVSEND_BUNDLE) { if (req->opcode == IORING_OP_SENDMSG) return -EINVAL; -- cgit v1.2.3 From 0b88f470589960591f1cb3f238124939d0a7331d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 26 Jul 2026 08:13:09 -0600 Subject: io_uring/kbuf: cap buffer selection length at MAX_RW_COUNT io_ring_buffers_peek() builds an iovec array from provided buffers, and that in turn can be handed off to a lower level provider. Be prudent and cap the total size to MAX_RW_COUNT, which is the Linux default for how much IO do to in a single call. No bugs here, but it's a good preventative measure to avoid truncation issues. Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Jens Axboe --- io_uring/kbuf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index de0129bceaba..1cf5be62bb65 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -266,6 +266,9 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, if (unlikely(!nr_avail)) return -ENOBUFS; + /* MAX_RW_COUNT is the universal Linux per-call IO maximum */ + arg->max_len = min_t(size_t, arg->max_len, MAX_RW_COUNT); + buf = io_ring_head_to_buf(br, head, bl->mask); if (arg->max_len) { u32 len = READ_ONCE(buf->len); @@ -295,7 +298,7 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, /* set it to max, if not set, so we can use it unconditionally */ if (!arg->max_len) - arg->max_len = INT_MAX; + arg->max_len = MAX_RW_COUNT; req->buf_index = READ_ONCE(buf->bid); do { -- cgit v1.2.3 From 9db03d2116ab9e19051641d75c8270c84a9311ea Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 30 Jul 2026 10:33:14 -0600 Subject: Revert "io_uring/epoll: disallow adding an epoll file to an epoll context" Turns out that my naive assumption that surely nobody is using io_uring to manage nested epoll instances was wrong - libuv is in fact doing that very thing. Since we have users in the wild using that feature, we cannot remove it from upstream. Revert the commit that introduced that limitation. This reverts commit cfa1539b24aff18ecb71c6334e7270f810d145bb. Link: https://lore.kernel.org/io-uring/605939add4dfb674bd7ab39ef8dbd3fd22e754e4.camel@xry111.site/ Reported-by: Xi Ruoyao Signed-off-by: Jens Axboe --- io_uring/epoll.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/io_uring/epoll.c b/io_uring/epoll.c index eecd748cad01..b9db8bde27ec 100644 --- a/io_uring/epoll.c +++ b/io_uring/epoll.c @@ -62,9 +62,6 @@ int io_epoll_ctl(struct io_kiocb *req, unsigned int issue_flags) CLASS(fd, tf)(ie->fd); if (fd_empty(tf)) return -EBADF; - /* disallow adding an epoll context to another epoll context */ - if (ie->op == EPOLL_CTL_ADD && is_file_epoll(fd_file(tf))) - return -EINVAL; key.file = fd_file(tf); key.fd = ie->fd; -- cgit v1.2.3 From 0680cbbf39ca61c70be16141b5259f822e7cdb3b Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Mon, 27 Jul 2026 15:23:30 -0700 Subject: btrfs: trigger cow fixup via dirty_folio() The problem scenario: If we have a folio mmapped shared and then somebody does a dio read with that folio as the read destination, then it is possible that the dio will see a dirty destination page when it starts (and thus skip dirtying and just GUP pin it) but then while it is doing the read, btrfs finishes writing it back and by the endio, the folio is clean. In that case, the dio read must re-dirty the folio with aops->dirty_folio(): btrfs_check_read_bio() |- __iomap_dio_bio_end_io() from btrfs_bio_end_io() |- bio_check_pages_dirty() |- bio_dirty_fn() |- bio_release_pages(bio, true) |- __bio_release_pages(bio, mark_dirty == true) |- folio_lock() |- folio_mark_dirty() |- aops->dirty_folio() |- folio_unlock() A data block normally moves through writeback as follows: TASK folio_lock write clean -> dirty bit + delalloc folio_unlock WRITEBACK for-each-dirty-folio: folio_lock run_delalloc delalloc consumed -> dirty bit + OE submission dirty bit consumed -> writeback bit + OE folio_unlock ENDIO endio OE bytes accounted OE finish writeback -> clean; destroy OE Three critical invariants that this path maintains are: I1. Any dirty block is covered by delalloc xor an ordered extent I2. Any dirty block covered by an OE will be submitted into that OE I3. Any dirty block already submitted into an OE will not be submitted again into the same OE. These ensure that the block will be written exactly once. It is clear that not reserving delalloc for the re-dirty case violates I1. This situation, even without bs < folio_size, has long required btrfs to fixup such dirty pages during writeback with an asynchronous worker that is allowed to do this expensive work and writeback does not proceed for a folio while it is doing this work. Commit 247e743cbe6e ("Btrfs: Use async helpers to deal with pages that have been improperly dirtied") introduced the COW fixup to catch exactly this class at writeback, way back in 2008. Since then, there have been many advances to prevent most of the causes of such re-dirtying and we thought we could get away with removing the annoying cow-fixup in the hope of simplifying writeback for large folio support. Commit b2a9f217ad3f ("btrfs: remove the COW fixup mechanism") Commit 4927b141877c ("btrfs: remove folio ordered flag and subpage bitmap") Since it turns out this assumption was incorrect, as evidenced by the report and attendant reproducers, we must reintroduce the fixup concept. This is of course critically further complicated by bs < folio_size. In that case, rather than just a folio dirty bit, we have a bitmap for the dirty blocks in the folio. And the (also broken) invariant is: I4. folio dirty IFF at least one block bitmap dirty. The original report of a stall on a misinterpreted empty bitmap is exactly evidence of a violation of I4. It is exactly because of bs < folio_size we don't want to simply revert the removal patches. The original fixup was not properly bs < folio_size aware, which motivated removal in the first place. So we wish to build a bs < folio_size aware fixup. One other important detail from the old design, any normal write that happens after a re-dirty but before a fixup is racing with the cow fixup to do the delalloc reservation, therefore it must cancel the fixup state. If it arrives after the reservation exists, it will be a normal dirty overwrite. This critically informs the design in a pretty clear way. fixup requiring re-dirty has folio granularity, while cancellation has delalloc (block) granularity so while we only ever produce fixup in chunks of folios, we must be able to clear it in blocks. Therefore we must track the blocks needing fixup at block granularity. The obvious way to do this is with a new bitmap in btrfs_folio_state, but it is desirable to avoid that if possible. Unfortunately, I don't think it is possible and the reason is subtle and leans on a sort of extreme reproducer, but I think can be explained relatively succinctly. Consider a folio whose two halves will land in different ordered extents (can be accomplished with tricks using nodatasum) and a dio read is running with it as the shared mmap destination. 1. The front half: a. folio comes clean on a normal write b. dio read completes into the folio marking it fixup. c. a write comes for the previous folio for a range extending into this folio, this is a cancellation of the fixup which reserves space. d. writeback runs on the range *not* overlapping the folio. This half remains dirty but is now covered by an OE and is awaiting writeback running on its range to be submitted and finish the OE. 2. The back half: a. the folio is part of an OE that gets far enough along to clear writeback. b. dio read completes into the folio marking it fixup. After this, the folio's front half is dirty in the "normal" sense, it needs to be submitted to the OE waiting for it. It's a cancelled fixup. Meanwhile, the second half is a true fresh fixup. So at this point if we run writeback on this folio, we genuinely can't know what to do without block level information. If we submit it, we submit unreserved dirty from the back half. If we don't, we will never finish the OE waiting for it. So it's either a corruption or a deadlock. Thus, the full high level design picture: - btrfs_data_dirty_folio(): For out of band non-reserving dirties, mark still-clean blocks inside EOF dirty and set their fixup bits (the event carries no range, so every clean block is suspect). Already-dirty blocks are covered or pending and are left alone. - Writeback: skip fixup blocks and enqueue work for them - writepage_fixup(): for each fixup block do the fixup reservation in a worker, after which the blocks can be written back normally. - Typical reserving write paths cancel fixup state for the ranges they cover with btrfs_folio_cancel_fixup() Link: https://lore.kernel.org/linux-btrfs/20260721191152.101118-1-borntraeger@linux.ibm.com/ Assisted-by: LLM Reviewed-by: Qu Wenruo Signed-off-by: Boris Burkov Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 1 + fs/btrfs/disk-io.c | 7 +- fs/btrfs/extent_io.c | 113 ++++++++++++++++++++++ fs/btrfs/fs.h | 12 +++ fs/btrfs/inode.c | 200 ++++++++++++++++++++++++++++++++++++++- fs/btrfs/subpage.c | 216 ++++++++++++++++++++++++++++++++++++++++++- fs/btrfs/subpage.h | 41 +++++++- include/trace/events/btrfs.h | 35 +++++++ 8 files changed, 613 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 7fdc6c3fd066..1082fa92c145 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -600,6 +600,7 @@ int btrfs_prealloc_file_range_trans(struct inode *inode, loff_t actual_len, u64 *alloc_hint); int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_folio, u64 start, u64 end, struct writeback_control *wbc); +void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio); int btrfs_encoded_io_compression_from_extent(struct btrfs_fs_info *fs_info, int compress_type); int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode, diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 36332df9a0f1..6bb70c43a63f 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1760,6 +1760,8 @@ static int read_backup_root(struct btrfs_fs_info *fs_info, u8 priority) /* helper to cleanup workers */ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info) { + if (fs_info->fixup_workers) + destroy_workqueue(fs_info->fixup_workers); btrfs_destroy_workqueue(fs_info->delalloc_workers); btrfs_destroy_workqueue(fs_info->workers); if (fs_info->endio_workers) @@ -1967,6 +1969,9 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info) fs_info->caching_workers = btrfs_alloc_workqueue(fs_info, "cache", flags, max_active, 0); + fs_info->fixup_workers = + alloc_ordered_workqueue("btrfs-fixup", ordered_flags); + fs_info->endio_workers = alloc_workqueue("btrfs-endio", flags, max_active); fs_info->endio_meta_workers = @@ -1992,7 +1997,7 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info) fs_info->endio_workers && fs_info->endio_meta_workers && fs_info->endio_write_workers && fs_info->endio_freespace_worker && fs_info->rmw_workers && - fs_info->caching_workers && + fs_info->caching_workers && fs_info->fixup_workers && fs_info->delayed_workers && fs_info->qgroup_rescan_workers && fs_info->discard_ctl.discard_workers)) { return -ENOMEM; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index de5785117a47..f032f0858f40 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1440,6 +1440,115 @@ static bool find_next_delalloc_bitmap(struct folio *folio, return true; } +/* + * Debug checks for fixup selection logic to help ensure the invariants + * we expect for fixup marking hold in practice. + * + * - A dirty block without a fixup bit is covered by delalloc or a running + * ordered extent (it was dirtied by a reserving write path). + * - A block with a fixup bit is never covered by delalloc: every delalloc + * setter holds the folio lock and cancels the fixup state of the blocks + * it covers (btrfs_folio_set_dirty()) before releasing it. + */ +static void debug_check_writepage_fixup(struct btrfs_inode *inode, u64 start, + u32 len, bool needs_fixup) +{ + struct btrfs_ordered_extent *ordered; + bool delalloc; + + if (!IS_ENABLED(CONFIG_BTRFS_DEBUG)) + return; + + delalloc = btrfs_test_range_bit_exists(&inode->io_tree, start, + start + len - 1, EXTENT_DELALLOC); + if (needs_fixup) { + if (unlikely(delalloc)) + DEBUG_WARN("writeback: delalloc and fixup conflict. ino %llu start %llu", + btrfs_ino(inode), start); + } else { + if (delalloc) + return; + + ordered = btrfs_lookup_ordered_range(inode, start, len); + if (unlikely(!ordered)) + DEBUG_WARN("dirty block, no delalloc, fixup, ordered. ino %llu start %llu", + btrfs_ino(inode), start); + else + btrfs_put_ordered_extent(ordered); + } +} + +/* + * Handle folios dirtied without a delalloc reservation, e.g. + * O_DIRECT read into a MAP_SHARED mapping dirtying via set_page_dirty_lock(). + * + * btrfs_data_dirty_folio() records the affected blocks in the fixup bitmap + * and the folio fixup flag and we check them here in writeback. + * + * Don't submit such blocks and queue work for the fixup worker to reserve + * space for them so that they can be submitted properly by writeback. + * + * Return 1 if the folio needed fixup, 0 if not, and a negative error code + * on error. + */ +static noinline_for_stack int writepage_fixup(struct btrfs_inode *inode, + struct folio *folio, + struct btrfs_bio_ctrl *bio_ctrl) +{ + struct btrfs_fs_info *fs_info = inode_to_fs_info(&inode->vfs_inode); + const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio); + const u32 sectorsize = fs_info->sectorsize; + const u64 page_start = folio_pos(folio); + bool found_fixup = false; + unsigned int bit; + + /* + * A folio was dirtied without calling aops->dirty_folio() which we + * explicitly assert is not allowed. + */ + if (unlikely(bitmap_empty(bio_ctrl->submit_bitmap, blocks_per_folio))) { + DEBUG_WARN(); + btrfs_err_rl(fs_info, + "root %lld ino %llu folio %llu is dirty with an empty dirty bitmap", + btrfs_root_id(inode->root), btrfs_ino(inode), + folio_pos(folio)); + return -EUCLEAN; + } + + /* Cheap check on the folio flag. Set iff the fixup bitmap is non-empty. */ + if (likely(!folio_test_fixup_pending(folio))) + return 0; + + for_each_set_bit(bit, bio_ctrl->submit_bitmap, blocks_per_folio) { + const u64 start = page_start + (bit << fs_info->sectorsize_bits); + const bool needs_fixup = btrfs_folio_test_fixup(fs_info, folio, + start, sectorsize); + + debug_check_writepage_fixup(inode, start, sectorsize, needs_fixup); + if (needs_fixup) { + bitmap_clear(bio_ctrl->submit_bitmap, bit, 1); + found_fixup = true; + } + } + if (likely(found_fixup)) { + btrfs_queue_writepage_fixup(inode, folio); + folio_redirty_for_writepage(bio_ctrl->wbc, folio); + if (bitmap_empty(bio_ctrl->submit_bitmap, blocks_per_folio)) { + folio_unlock(folio); + return 1; + } + return 0; + } + /* We should always find fixup if the folio fixup flag was set. */ + DEBUG_WARN(); + btrfs_err_rl(fs_info, + "root %lld ino %llu folio %llu is fixup with an empty fixup bitmap", + btrfs_root_id(inode->root), btrfs_ino(inode), + folio_pos(folio)); + + return -EUCLEAN; +} + /* * Do all of the delayed allocation setup. * @@ -1492,6 +1601,10 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode, /* Save the dirty bitmap as our submission bitmap will be a subset of it. */ btrfs_copy_subpage_dirty_bitmap(fs_info, folio, bio_ctrl->submit_bitmap); + ret = writepage_fixup(inode, folio, bio_ctrl); + if (ret) + return ret; + for_each_set_bitrange(start_bit, end_bit, bio_ctrl->submit_bitmap, blocks_per_folio) { u64 start = page_start + (start_bit << fs_info->sectorsize_bits); diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 7ee9ec2b0efb..f7f343fbe732 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -713,6 +713,8 @@ struct btrfs_fs_info { struct btrfs_workqueue *endio_write_workers; struct btrfs_workqueue *endio_freespace_worker; struct btrfs_workqueue *caching_workers; + + struct workqueue_struct *fixup_workers; struct btrfs_workqueue *delayed_workers; struct task_struct *transaction_kthread; @@ -1200,6 +1202,16 @@ static inline void btrfs_wake_unfinished_drop(struct btrfs_fs_info *fs_info) clear_and_wake_up_bit(BTRFS_FS_UNFINISHED_DROPS, &fs_info->flags); } +/* + * We use the folio owner_2 flag to indicate the folio has blocks that were + * dirtied without a space reservation and need the writepage fixup before + * writeback. For bs < folio_size the fixup bitmap tracks the affected + * blocks. + */ +#define folio_test_fixup_pending(folio) folio_test_owner_2(folio) +#define folio_set_fixup_pending(folio) folio_set_owner_2(folio) +#define folio_clear_fixup_pending(folio) folio_clear_owner_2(folio) + #define BTRFS_FS_ERROR(fs_info) (READ_ONCE((fs_info)->fs_error)) #define BTRFS_FS_LOG_CLEANUP_ERROR(fs_info) \ diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 04ea10b61bbb..98b31a090626 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2812,6 +2812,163 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, EXTENT_DELALLOC | extra_bits, cached_state); } +struct btrfs_writepage_fixup { + struct folio *folio; + struct btrfs_inode *inode; + struct work_struct work; +}; + +/* + * Do the real fixup work of reserving space for the blocks a folio's fixup + * state records. Queued by writepage_fixup() when writeback found the bits set. + * + * Since the fixup can be cancelled by a task dirtying with a reservation, we must + * re-check the state of fixup under the folio lock. + */ +static void btrfs_writepage_fixup_worker(struct work_struct *work) +{ + struct btrfs_writepage_fixup *fixup = + container_of(work, struct btrfs_writepage_fixup, work); + struct extent_state *cached_state = NULL; + struct extent_changeset *data_reserved = NULL; + unsigned long delalloc_bitmap[BITS_TO_LONGS(BTRFS_MAX_BLOCKS_PER_FOLIO)] = { 0 }; + struct folio *folio = fixup->folio; + struct btrfs_inode *inode = fixup->inode; + struct btrfs_fs_info *fs_info = inode->root->fs_info; + const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio); + const u32 sectorsize = fs_info->sectorsize; + const u64 page_start = folio_pos(folio); + const u64 page_end = folio_next_pos(folio) - 1; + unsigned int start_bit; + unsigned int end_bit; + unsigned int bit; + bool reserved; + int ret; + + /* + * We would prefer to reserve under the folio lock when we know exactly + * which blocks need a reservation. Unfortunately, since the reservation + * can go into flushers which can go into writeback, which takes folio + * locks, that is not possible. Therefore, we have to reserve for the + * whole folio here, then release what we didn't end up needing once we + * figure it out. + * + * Also note the slightly strange error checking. If fixup is actually + * not set, we don't need to mark an error on the mapping. So hang on to + * ret until after we lock and find out if we actually care. + */ + ret = btrfs_delalloc_reserve_space(inode, &data_reserved, page_start, + folio_size(folio)); + reserved = (ret == 0); +again: + folio_lock(folio); + + if (!folio->mapping || !folio_test_fixup_pending(folio)) { + ret = 0; + goto out; + } + if (ret) + goto out; + + btrfs_lock_extent(&inode->io_tree, page_start, page_end, &cached_state); + + for (bit = 0; bit < blocks_per_folio; bit++) { + struct btrfs_ordered_extent *ordered; + const u64 start = page_start + (bit << fs_info->sectorsize_bits); + + if (test_bit(bit, delalloc_bitmap)) + continue; + if (!btrfs_folio_test_fixup(fs_info, folio, start, sectorsize)) + continue; + /* + * Any task that sets EXTENT_DELALLOC clears the fixup bits + * under the folio lock, so it should be impossible to observe + * both under the lock. Setting delalloc twice would wrongly + * double account the space. + */ + if (IS_ENABLED(CONFIG_BTRFS_DEBUG) && + unlikely(btrfs_test_range_bit_exists(&inode->io_tree, start, + start + sectorsize - 1, + EXTENT_DELALLOC))) { + DEBUG_WARN("fixup worker: delalloc and fixup conflict. ino %llu start %llu", + btrfs_ino(inode), start); + btrfs_folio_clear_fixup(fs_info, folio, start, sectorsize); + continue; + } + ordered = btrfs_lookup_ordered_range(inode, start, sectorsize); + if (ordered) { + trace_btrfs_writepage_fixup_defer(inode, ordered); + btrfs_unlock_extent(&inode->io_tree, page_start, + page_end, &cached_state); + folio_unlock(folio); + btrfs_start_ordered_extent(ordered); + btrfs_put_ordered_extent(ordered); + goto again; + } + ret = btrfs_set_extent_delalloc(inode, start, + start + sectorsize - 1, 0, + &cached_state); + if (ret) + break; + trace_btrfs_writepage_fixup_reserve(inode, start, sectorsize); + btrfs_folio_clear_fixup(fs_info, folio, start, sectorsize); + set_bit(bit, delalloc_bitmap); + } + + btrfs_unlock_extent(&inode->io_tree, page_start, page_end, &cached_state); +out: + if (ret < 0) { + /* Failure here is analogous to failure in writeback. */ + mapping_set_error(folio->mapping, ret); + btrfs_folio_clear_fixup_dirty(fs_info, folio, page_start, + folio_size(folio)); + } + if (reserved) { + btrfs_delalloc_release_extents(inode, folio_size(folio)); + for_each_clear_bitrange(start_bit, end_bit, delalloc_bitmap, + blocks_per_folio) + btrfs_delalloc_release_space(inode, data_reserved, + page_start + (start_bit << fs_info->sectorsize_bits), + (end_bit - start_bit) << fs_info->sectorsize_bits, + true); + } + folio_unlock(folio); + folio_put(folio); + kfree(fixup); + extent_changeset_free(data_reserved); + btrfs_add_delayed_iput(inode); +} + +/* + * Queue space reservation fixup work for blocks dirtied without a space reservation. + * + * Should be used by writeback while holding the folio locked. + * + * If we fail to queue fixup, then the folio state is unchanged and a future + * writeback pass will still see it. + */ +void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio) +{ + struct btrfs_fs_info *fs_info = inode->root->fs_info; + struct btrfs_writepage_fixup *fixup; + + fixup = kzalloc_obj(*fixup, GFP_NOFS); + if (!fixup) + return; + + /* + * This is called from within extent_write_cache_pages() which + * has successfully done an igrab(). But that will be released at the + * end of the writeback pass. We need to extend it for the worker as well. + */ + ihold(&inode->vfs_inode); + folio_get(folio); + INIT_WORK(&fixup->work, btrfs_writepage_fixup_worker); + fixup->folio = folio; + fixup->inode = inode; + queue_work(fs_info->fixup_workers, &fixup->work); +} + /* * Clear the old accounting flags and set EXTENT_DELALLOC for the range. * @@ -7507,6 +7664,12 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset, folio_wait_writeback(folio); wait_subpage_spinlock(folio); + /* + * The invalidated blocks are going away; drop any fixup blocks among + * them, data included, as they have no space reservation. + */ + btrfs_folio_clear_fixup_dirty(fs_info, folio, page_start + offset, length); + /* * For subpage case, we have call sites like * btrfs_punch_hole_lock_range() which passes range not aligned to @@ -10548,6 +10711,41 @@ static const struct file_operations btrfs_dir_file_operations = { .setlease = generic_setlease, }; +/* + * The folio is going dirty without a btrfs delalloc space reservation. + * This requires a fixup before writeback which we might sleep so cannot + * run in this context, so we merely set state on the folio indicating it + * needs fixup before writeback. + * + * Note that there is no range in the input, so the whole folio is marked + * dirty and fixup. + * + * We believe that all callers of dirty_folio either: + * - take the folio lock (e.g. pinned folio release notification). + * - take the pte lock but must be running on a dirty pte which means + * page_mkwrite() ran on it and reserved the space. zap_pte_range() cannot + * race with writeback cleaning the folio because writeback runs + * folio_mkclean() which also uses the pte lock and revokes outstanding + * writable mappings. + * Therefore, an additional folio private lock (a la bfs->lock for all cases, + * not just subpage) is not necessary. + */ +static bool btrfs_data_dirty_folio(struct address_space *mapping, + struct folio *folio) +{ + struct btrfs_inode *inode = BTRFS_I(mapping->host); + struct btrfs_fs_info *fs_info = inode->root->fs_info; + const u64 page_start = folio_pos(folio); + const u64 range_end = min_t(u64, folio_next_pos(folio), + round_up(i_size_read(&inode->vfs_inode), + fs_info->sectorsize)); + + if (range_end > page_start) + btrfs_folio_set_fixup_dirty(fs_info, folio, page_start, + range_end - page_start); + return filemap_dirty_folio(mapping, folio); +} + /* * btrfs doesn't support the bmap operation because swapfiles * use bmap to make a mapping of extents in the file. They assume @@ -10568,7 +10766,7 @@ static const struct address_space_operations btrfs_aops = { .launder_folio = btrfs_launder_folio, .release_folio = btrfs_release_folio, .migrate_folio = btrfs_migrate_folio, - .dirty_folio = filemap_dirty_folio, + .dirty_folio = btrfs_data_dirty_folio, .error_remove_folio = generic_error_remove_folio, .swap_activate = btrfs_swap_activate, .swap_deactivate = btrfs_swap_deactivate, diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c index 2a9397be8116..27dd677ca687 100644 --- a/fs/btrfs/subpage.c +++ b/fs/btrfs/subpage.c @@ -345,18 +345,57 @@ void btrfs_subpage_clear_uptodate(const struct btrfs_fs_info *fs_info, spin_unlock_irqrestore(&bfs->lock, flags); } +/* + * folio_mark_dirty() for a folio we are dirtying with a space reservation. + * + * Dirtiers without a reservation use btrfs_data_dirty_folio(). + */ +static void btrfs_folio_mark_dirty(struct folio *folio) +{ + struct address_space *mapping = folio_mapping(folio); + + if (!mapping || !mapping->host || !is_data_inode(BTRFS_I(mapping->host))) { + folio_mark_dirty(folio); + return; + } + if (folio_test_reclaim(folio)) + folio_clear_reclaim(folio); + filemap_dirty_folio(mapping, folio); +} + +/* + * The set helper of the dirty ops, so it only runs for folios without a + * fixup bitmap: for those the folio flag is the whole fixup state, and this + * reserving write covers the block, so retire it. Metadata never has the + * flag set and only pays the test. + */ +static void btrfs_folio_mark_dirty_reserved(struct folio *folio) +{ + if (folio_test_fixup_pending(folio)) + folio_clear_fixup_pending(folio); + btrfs_folio_mark_dirty(folio); +} + void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info, struct folio *folio, u64 start, u32 len) { struct btrfs_folio_state *bfs = folio_get_private(folio); - unsigned int start_bit = subpage_calc_start_bit(fs_info, folio, + unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio, dirty, start, len); + unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio, + fixup, start, len); + const unsigned int nbits = len >> fs_info->sectorsize_bits; unsigned long flags; spin_lock_irqsave(&bfs->lock, flags); - bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits); + bitmap_set(bfs->bitmaps, dirty_bit, nbits); + /* Proper dirtying obviates the need for fixup. */ + bitmap_clear(bfs->bitmaps, fixup_bit, nbits); + if (folio_test_fixup_pending(folio) && + subpage_test_bitmap_all_zero(fs_info, folio, fixup)) + folio_clear_fixup_pending(folio); spin_unlock_irqrestore(&bfs->lock, flags); - folio_mark_dirty(folio); + btrfs_folio_mark_dirty(folio); } static void folio_clear_tags(struct folio *folio) @@ -457,6 +496,172 @@ void btrfs_subpage_clear_writeback(const struct btrfs_fs_info *fs_info, spin_unlock_irqrestore(&bfs->lock, flags); } +void btrfs_subpage_clear_fixup(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len) +{ + struct btrfs_folio_state *bfs = folio_get_private(folio); + unsigned int start_bit = subpage_calc_start_bit(fs_info, folio, + fixup, start, len); + unsigned long flags; + + spin_lock_irqsave(&bfs->lock, flags); + bitmap_clear(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits); + if (subpage_test_bitmap_all_zero(fs_info, folio, fixup)) + folio_clear_fixup_pending(folio); + spin_unlock_irqrestore(&bfs->lock, flags); +} + +/* + * In one pass under bfs->lock, mark every block with a clear dirty bit in the + * range both dirty and needing fixup. + * + * Only called from the dirty_folio callback, which owns the folio-level + * dirty flag; calling folio_mark_dirty() here would recurse. + * + * The folio fixup flag and bits are both set under bfs->lock so that a + * writeback pass observing the new bits also observes the flag. + */ +static void btrfs_subpage_set_fixup_dirty(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len) +{ + struct btrfs_folio_state *bfs = folio_get_private(folio); + unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio, + dirty, start, len); + unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio, + fixup, start, len); + const unsigned int nbits = len >> fs_info->sectorsize_bits; + unsigned long flags; + bool marked = false; + + spin_lock_irqsave(&bfs->lock, flags); + for (unsigned int i = 0; i < nbits; i++) { + if (test_bit(dirty_bit + i, bfs->bitmaps)) + continue; + set_bit(dirty_bit + i, bfs->bitmaps); + set_bit(fixup_bit + i, bfs->bitmaps); + marked = true; + } + if (marked) + folio_set_fixup_pending(folio); + spin_unlock_irqrestore(&bfs->lock, flags); +} + +/* + * Mark the still-clean blocks of a folio dirty and needing fixup, for + * btrfs_data_dirty_folio(). + * + * A subpage block size folio that is not uptodate is left alone: its clean + * blocks may hold content that was never read in, which must not be marked + * dirty. + */ +void btrfs_folio_set_fixup_dirty(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len) +{ + if (!btrfs_is_subpage(fs_info, folio)) { + if (!folio_test_dirty(folio)) + folio_set_fixup_pending(folio); + return; + } + if (!folio_test_uptodate(folio)) + return; + btrfs_subpage_set_fixup_dirty(fs_info, folio, start, len); +} + +/* + * Drop the fixup blocks inside the range: clear both their fixup and dirty + * bits. + * + * Fixup blocks carry no space reservation, so their fixup and dirty bits + * must be dropped together. Clearing only the fixup bit would leave a + * dirty block without a reservation which is not a valid state. + * + * Returns true if the folio has no dirty blocks left. + */ +static bool btrfs_subpage_clear_fixup_dirty(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len) +{ + struct btrfs_folio_state *bfs = folio_get_private(folio); + unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio, + dirty, start, len); + unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio, + fixup, start, len); + const unsigned int nbits = len >> fs_info->sectorsize_bits; + unsigned long flags; + bool last; + + spin_lock_irqsave(&bfs->lock, flags); + for (unsigned int i = 0; i < nbits; i++) { + if (!test_bit(fixup_bit + i, bfs->bitmaps)) + continue; + clear_bit(fixup_bit + i, bfs->bitmaps); + clear_bit(dirty_bit + i, bfs->bitmaps); + } + if (subpage_test_bitmap_all_zero(fs_info, folio, fixup)) + folio_clear_fixup_pending(folio); + last = subpage_test_bitmap_all_zero(fs_info, folio, dirty); + spin_unlock_irqrestore(&bfs->lock, flags); + return last; +} + +/* + * Drop the fixup blocks inside the range, for callers discarding their data: + * btrfs_invalidate_folio() and the writepage fixup worker's error path. + * + * Callers that have just reserved space for a block want + * btrfs_folio_clear_fixup() instead - there the block stays dirty and gets + * written. + * + * The range can be byte-granular (an unaligned truncate through + * btrfs_invalidate_folio()); only blocks fully inside it are dropped, as a + * partially covered block still holds live data outside the range. For + * single-block folios the folio flag is the fixup state, so it is dropped + * only when the range covers the whole folio. + */ +void btrfs_folio_clear_fixup_dirty(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len) +{ + u64 aligned_start; + u64 aligned_end; + + /* The folio flag is set whenever any fixup bitmap bit is. */ + if (!folio_test_fixup_pending(folio)) + return; + if (!btrfs_is_subpage(fs_info, folio)) { + if (start <= folio_pos(folio) && + start + len >= folio_next_pos(folio)) { + folio_clear_fixup_pending(folio); + folio_clear_dirty_for_io(folio); + } + return; + } + btrfs_subpage_clamp_range(folio, &start, &len); + aligned_start = round_up(start, fs_info->sectorsize); + aligned_end = round_down(start + len, fs_info->sectorsize); + if (aligned_end <= aligned_start) + return; + if (btrfs_subpage_clear_fixup_dirty(fs_info, folio, aligned_start, + aligned_end - aligned_start)) + folio_clear_dirty_for_io(folio); +} + +bool btrfs_folio_test_fixup(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len) +{ + if (!btrfs_is_subpage(fs_info, folio)) + return folio_test_fixup_pending(folio); + return btrfs_subpage_test_fixup(fs_info, folio, start, len); +} + +void btrfs_folio_clear_fixup(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len) +{ + if (!btrfs_is_subpage(fs_info, folio)) { + folio_clear_fixup_pending(folio); + return; + } + btrfs_subpage_clear_fixup(fs_info, folio, start, len); +} + /* * Unlike set/clear which is dependent on each page status, for test all bits * are tested in the same way. @@ -480,6 +685,7 @@ bool btrfs_subpage_test_##name(const struct btrfs_fs_info *fs_info, \ IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(uptodate); IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(dirty); IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(writeback); +IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(fixup); /* * Note that, in selftests (extent-io-tests), we can have empty fs_info passed @@ -571,8 +777,8 @@ bool btrfs_meta_folio_test_##name(struct folio *folio, const struct extent_buffe } IMPLEMENT_BTRFS_PAGE_OPS(uptodate, folio_mark_uptodate, folio_clear_uptodate, folio_test_uptodate); -IMPLEMENT_BTRFS_PAGE_OPS(dirty, folio_mark_dirty, folio_clear_dirty_for_io, - folio_test_dirty); +IMPLEMENT_BTRFS_PAGE_OPS(dirty, btrfs_folio_mark_dirty_reserved, + folio_clear_dirty_for_io, folio_test_dirty); IMPLEMENT_BTRFS_PAGE_OPS(writeback, folio_start_writeback, folio_end_writeback, folio_test_writeback); diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h index c6d7394e6418..9aceba93c818 100644 --- a/fs/btrfs/subpage.h +++ b/fs/btrfs/subpage.h @@ -14,15 +14,15 @@ struct folio; /* * Extra info for subpage bitmap. * - * For subpage we pack all uptodate/dirty/writeback bitmaps into + * For subpage we pack all uptodate/dirty/writeback/fixup bitmaps into * one larger bitmap. * * This structure records how they are organized in the bitmap: * - * /- uptodate /- dirty /- writeback - * | | | - * v v v - * |u|u|u|u|........|u|u|d|d|.......|d|d|w|w|.......|w|w| + * /- uptodate /- dirty /- writeback /- fixup + * | | | | + * v v v v + * |u|u|u|u|........|u|u|d|d|.......|d|d|w|w|.....|w|w|f|f|.....|f|f| * |< sectors_per_page >| * * Unlike regular macro-like enums, here we do not go upper-case names, as @@ -40,6 +40,14 @@ enum { */ btrfs_bitmap_nr_writeback, + /* + * Blocks dirtied by the dirty_folio callback instead of a reserving + * write path (e.g. set_page_dirty_lock() on a GUP pin). They have + * no space reservation and need the writepage fixup before they can + * be submitted. + */ + btrfs_bitmap_nr_fixup, + btrfs_bitmap_nr_max }; @@ -165,6 +173,29 @@ DECLARE_BTRFS_SUBPAGE_OPS(uptodate); DECLARE_BTRFS_SUBPAGE_OPS(dirty); DECLARE_BTRFS_SUBPAGE_OPS(writeback); +/* + * Fixup bit helpers. + * + * The fixup bit is data-only and has no plain set helper (setting happens + * together with dirtying in btrfs_subpage_set_fixup_dirty()), so it does not + * go through DECLARE_BTRFS_SUBPAGE_OPS(). For single-block folios the + * folio_*_fixup_pending() flag takes the place of the bitmap. + */ +void btrfs_subpage_clear_fixup(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len); +bool btrfs_subpage_test_fixup(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len); +bool btrfs_folio_test_fixup(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len); +void btrfs_folio_set_fixup_dirty(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len); +/* For a block that just got its space reserved; it stays dirty. */ +void btrfs_folio_clear_fixup(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len); +/* For callers discarding the data; clears the dirty bits too. */ +void btrfs_folio_clear_fixup_dirty(const struct btrfs_fs_info *fs_info, + struct folio *folio, u64 start, u32 len); + /* * Helper for error cleanup, where a folio will have its dirty flag cleared, * with writeback started and finished. diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 4c5c47c5edb7..6c1438f6a4d3 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -689,6 +689,41 @@ DEFINE_EVENT(btrfs__ordered_extent, btrfs_ordered_extent_lookup_first, TP_ARGS(inode, ordered) ); +/* + * The writepage fixup worker deferred a block because this still-running + * ordered extent covers it. + */ +DEFINE_EVENT(btrfs__ordered_extent, btrfs_writepage_fixup_defer, + + TP_PROTO(const struct btrfs_inode *inode, + const struct btrfs_ordered_extent *ordered), + + TP_ARGS(inode, ordered) +); + +/* The writepage fixup worker reserved space for a block and set delalloc. */ +TRACE_EVENT(btrfs_writepage_fixup_reserve, + + TP_PROTO(const struct btrfs_inode *inode, u64 start, u32 len), + + TP_ARGS(inode, start, len), + + TP_STRUCT__entry_btrfs( + __field( u64, ino ) + __field( u64, start ) + __field( u32, len ) + ), + + TP_fast_assign_btrfs(inode->root->fs_info, + __entry->ino = btrfs_ino(inode); + __entry->start = start; + __entry->len = len; + ), + + TP_printk_btrfs("ino=%llu start=%llu len=%u", + __entry->ino, __entry->start, __entry->len) +); + DEFINE_EVENT(btrfs__ordered_extent, btrfs_ordered_extent_split, TP_PROTO(const struct btrfs_inode *inode, -- cgit v1.2.3 From 74186c2968f8f756ac3226b545b598457c910c75 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:13 +0200 Subject: s390/vfio_ccw: Free all memory if cp_init() fails The routine cp_free() is called to unpin/free any memory once an I/O is completed successfully, or if cp_prefetch() fails. But if cp_init() fails, and cp->initialized is not enabled, the same routine cannot be used to free all the memory. An attempt to address this exists in ccwchain_handle_ccw(), where a single call to ccwchain_free() is made for the currently-processed CCW segment. But this will leak other segments (created as a result of a Transfer in Channel) that had been allocated as part of the same channel program. Address this by performing the cleanup outside of the recursive ccwchain_handle_ccw()/ccwchain_loop_tic() logic. Fixes: 8b515be512a2 ("vfio-ccw: Fix memory leak and don't call cp_free in cp_init") Cc: stable@vger.kernel.org Reviewed-by: Farhan Ali Reviewed-by: Matthew Rosato Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_cp.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 7561aa7d3e01..086d1b54bdb0 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -455,9 +455,6 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp) /* Loop for tics on this new chain. */ ret = ccwchain_loop_tic(chain, cp); - if (ret) - ccwchain_free(chain); - return ret; } @@ -486,6 +483,23 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp) return 0; } +static int ccwchain_build_ccws(dma32_t cda, struct channel_program *cp) +{ + struct ccwchain *chain, *temp; + int ret; + + ret = ccwchain_handle_ccw(cda, cp); + + if (ret) { + /* Cleanup if an error occurred */ + list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) { + ccwchain_free(chain); + } + } + + return ret; +} + static int ccwchain_fetch_tic(struct ccw1 *ccw, struct channel_program *cp) { @@ -735,7 +749,7 @@ int cp_init(struct channel_program *cp, union orb *orb) memcpy(&cp->orb, orb, sizeof(*orb)); /* Build a ccwchain for the first CCW segment */ - ret = ccwchain_handle_ccw(orb->cmd.cpa, cp); + ret = ccwchain_build_ccws(orb->cmd.cpa, cp); if (!ret) cp->initialized = true; -- cgit v1.2.3 From 5405c90d6a47b3014e74ee0618a162449abbbc93 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:14 +0200 Subject: s390/vfio_ccw: Limit the number of channel program segments The processing of channel programs, and the CCWs within them, is done recursively. As such, there is an arbitrary (but not architectural) limit to the number of CCWs that can exist in a single channel program. The vfio-ccw logic breaks these channel programs into segments whenever it encounters a Transfer-In-Channel (TIC) CCW, and the combined number of segments count towards the global limit. Impose an equivalent limit to the number of segments until such logic can be made non-recursive. Fixes: 0a19e61e6d4c ("vfio: ccw: introduce channel program interfaces") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_cp.c | 6 ++++++ drivers/s390/cio/vfio_ccw_cp.h | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 086d1b54bdb0..1c2890d139c6 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -332,6 +332,7 @@ static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len) goto out_err; list_add_tail(&chain->next, &cp->ccwchain_list); + cp->ccwchain_count++; return chain; @@ -441,6 +442,10 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp) if (len < 0) return len; + /* Limit number of chains in a single channel program */ + if (cp->ccwchain_count >= CCWCHAIN_COUNT_MAX) + return -EINVAL; + /* Need alloc a new chain for this one. */ chain = ccwchain_alloc(cp, len); if (!chain) @@ -745,6 +750,7 @@ int cp_init(struct channel_program *cp, union orb *orb) vdev->dev, "Prefetching channel program even though prefetch not specified in ORB"); + cp->ccwchain_count = 0; INIT_LIST_HEAD(&cp->ccwchain_list); memcpy(&cp->orb, orb, sizeof(*orb)); diff --git a/drivers/s390/cio/vfio_ccw_cp.h b/drivers/s390/cio/vfio_ccw_cp.h index fc31eb699807..a9b1d8dbc6f6 100644 --- a/drivers/s390/cio/vfio_ccw_cp.h +++ b/drivers/s390/cio/vfio_ccw_cp.h @@ -23,11 +23,18 @@ */ #define CCWCHAIN_LEN_MAX 256 +/* + * Maximum number of chains + */ +#define CCWCHAIN_COUNT_MAX 16 + /** * struct channel_program - manage information for channel program * @ccwchain_list: list head of ccwchains * @orb: orb for the currently processed ssch request * @initialized: whether this instance is actually initialized + * @guest_cp: copy of guest channel program + * @ccwchain_count: number of channel program segments (linked by TIC) * * @ccwchain_list is the head of a ccwchain list, that contents the * translated result of the guest channel program that pointed out by @@ -38,6 +45,7 @@ struct channel_program { union orb orb; bool initialized; struct ccw1 *guest_cp; + unsigned int ccwchain_count; }; int cp_init(struct channel_program *cp, union orb *orb); -- cgit v1.2.3 From a005b7f1a491ffda61bff0fd0f6548f8986fb977 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:15 +0200 Subject: s390/vfio_ccw: Fix out of bounds check on CCW array The routine ccwchain_calc_length() counts the number of channel command words (CCWs) that are chained together in a single channel program, and rejects anything larger than CCWCHAIN_LEN_MAX (256) CCWs. The loop itself is "do..while (count < 257)", and while the logic in is_cpa_within_range() correctly adjusts between the 0-index array of CCWs and the count of CCWs starting at 1, this means it would look at a possible 257th CCW before ending the loop and (correctly) returning an error. Fix this by restructuring the loop to break as soon as 256 CCWs (thus indexes 0-255) are examined, without looking at memory outside the range. Fixes: 0a19e61e6d4c ("vfio: ccw: introduce channel program interfaces") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_cp.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 1c2890d139c6..af632f9d5453 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -377,11 +377,9 @@ static void ccwchain_cda_free(struct ccwchain *chain, int idx) static int ccwchain_calc_length(u64 iova, struct channel_program *cp) { struct ccw1 *ccw = cp->guest_cp; - int cnt = 0; - - do { - cnt++; + int cnt; + for (cnt = 1; cnt <= CCWCHAIN_LEN_MAX; cnt++, ccw++) { /* * We want to keep counting if the current CCW has the * command-chaining flag enabled, or if it is a TIC CCW @@ -391,15 +389,10 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp) * after the TIC, depending on the results of its operation. */ if (!ccw_is_chain(ccw) && !is_tic_within_range(ccw, iova, cnt)) - break; - - ccw++; - } while (cnt < CCWCHAIN_LEN_MAX + 1); - - if (cnt == CCWCHAIN_LEN_MAX + 1) - cnt = -EINVAL; + return cnt; + } - return cnt; + return -EINVAL; } static int tic_target_chain_exists(struct ccw1 *tic, struct channel_program *cp) -- cgit v1.2.3 From 565bef268d75bf7df665bce6923a88cd0eb74592 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:16 +0200 Subject: s390/vfio_ccw: Ensure first IDAW remains constant The first IDAW in a list does not need to be on a 2K/4K boundary like all others, and so is read separately to accurately calculate the size of the buffer needed to read the full IDAL. Verify that the address found in the first IDAW is unchanged between reads, to ensure a consistent set of IDAWs being worked with. Fixes: 01aa26c672c0 ("s390/cio: Combine direct and indirect CCW paths") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_cp.c | 16 ++++++++++++++++ drivers/s390/cio/vfio_ccw_cp.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index af632f9d5453..6275794751cb 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -523,6 +523,7 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int &container_of(cp, struct vfio_ccw_private, cp)->vdev; dma64_t *idaws; dma32_t *idaws_f1; + u64 first_idaw; int idal_len = idaw_nr * sizeof(*idaws); int idaw_size = idal_is_2k(cp) ? PAGE_SIZE / 2 : PAGE_SIZE; int idaw_mask = ~(idaw_size - 1); @@ -539,6 +540,18 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int kfree(idaws); return ERR_PTR(ret); } + + idaws_f1 = (dma32_t *)idaws; + if (cp->orb.cmd.c64) + first_idaw = dma64_to_u64(idaws[0]); + else + first_idaw = dma32_to_u32(idaws_f1[0]); + + /* Unexpected mismatch from earlier read */ + if (first_idaw != cp->guest_iova) { + kfree(idaws); + return ERR_PTR(-EINVAL); + } } else { /* Fabricate an IDAL based off CCW data address */ if (cp->orb.cmd.c64) { @@ -604,6 +617,9 @@ static int ccw_count_idaws(struct ccw1 *ccw, iova = dma32_to_u32(ccw->cda); } + /* Save the read address for later */ + cp->guest_iova = iova; + /* Format-1 IDAWs operate on 2K each */ if (!cp->orb.cmd.c64) return idal_2k_nr_words((void *)iova, bytes); diff --git a/drivers/s390/cio/vfio_ccw_cp.h b/drivers/s390/cio/vfio_ccw_cp.h index a9b1d8dbc6f6..9af98ff12d67 100644 --- a/drivers/s390/cio/vfio_ccw_cp.h +++ b/drivers/s390/cio/vfio_ccw_cp.h @@ -35,6 +35,7 @@ * @initialized: whether this instance is actually initialized * @guest_cp: copy of guest channel program * @ccwchain_count: number of channel program segments (linked by TIC) + * @guest_iova: first data address of a guest channel program * * @ccwchain_list is the head of a ccwchain list, that contents the * translated result of the guest channel program that pointed out by @@ -46,6 +47,7 @@ struct channel_program { bool initialized; struct ccw1 *guest_cp; unsigned int ccwchain_count; + u64 guest_iova; }; int cp_init(struct channel_program *cp, union orb *orb); -- cgit v1.2.3 From 4f6fdc6e1a7fbfa36b945af33c65a417948feac0 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:17 +0200 Subject: s390/vfio_ccw: Calculate idal length based on idaw type Sashiko pointed out that get_guest_idal() unconditionally calculates the length of the IDAL presuming everything is a Format-2 IDAW. The output of vfio-ccw is always Format-2, but the input can be either Format-1 (31-bit addresses) or Format-2 (64-bit addresses). As a result, the size of the guest IDAL may be incorrect and should be trimmed down. Reported-by: sashiko-bot Link: https://lore.kernel.org/r/20260720203400.7328E1F000E9@smtp.kernel.org/ Fixes: 1b676fe3d9d3 ("vfio/ccw: handle a guest Format-1 IDAL") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_cp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 6275794751cb..5ef082b8289a 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -233,6 +233,7 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len) } #define idal_is_2k(_cp) (!(_cp)->orb.cmd.c64 || (_cp)->orb.cmd.i2k) +#define get_idaw_size(_cp) ((_cp)->orb.cmd.c64 ? sizeof(u64) : sizeof(u32)) /* * Helpers to operate ccwchain. @@ -524,7 +525,7 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int dma64_t *idaws; dma32_t *idaws_f1; u64 first_idaw; - int idal_len = idaw_nr * sizeof(*idaws); + int idal_len = idaw_nr * get_idaw_size(cp); int idaw_size = idal_is_2k(cp) ? PAGE_SIZE / 2 : PAGE_SIZE; int idaw_mask = ~(idaw_size - 1); int i, ret; @@ -593,7 +594,7 @@ static int ccw_count_idaws(struct ccw1 *ccw, struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; u64 iova; - int size = cp->orb.cmd.c64 ? sizeof(u64) : sizeof(u32); + int size = get_idaw_size(cp); int ret; int bytes = 1; -- cgit v1.2.3 From 9f5f9a78fedc45bc29d6a0a64e3a3472361afae5 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:18 +0200 Subject: s390/vfio_ccw: Ensure index for read/write regions are within range The introduction of the capability chain rightly clamped the region indexes to the range of the capabilities itself, but neglected to do so for the existing read/write regions which should also be enforced. Fixes: db8e5d17ac03 ("vfio-ccw: add capabilities chain") Cc: stable@vger.kernel.org Cc: Cornelia Huck Reviewed-by: Matthew Rosato Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_async.c | 16 ++++++++++++++++ drivers/s390/cio/vfio_ccw_chp.c | 15 +++++++++++++++ drivers/s390/cio/vfio_ccw_ops.c | 7 +++---- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_async.c b/drivers/s390/cio/vfio_ccw_async.c index 420d89ba7f83..4aff0b58fa5d 100644 --- a/drivers/s390/cio/vfio_ccw_async.c +++ b/drivers/s390/cio/vfio_ccw_async.c @@ -8,6 +8,7 @@ */ #include +#include #include "vfio_ccw_private.h" @@ -24,11 +25,20 @@ static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private, return -EINVAL; mutex_lock(&private->io_mutex); + + if (i >= private->num_regions) { + ret = -EINVAL; + goto out_unlock; + } + + i = array_index_nospec(i, private->num_regions); region = private->region[i].data; if (copy_to_user(buf, (void *)region + pos, count)) ret = -EFAULT; else ret = count; + +out_unlock: mutex_unlock(&private->io_mutex); return ret; } @@ -48,6 +58,12 @@ static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private, if (!mutex_trylock(&private->io_mutex)) return -EAGAIN; + if (i >= private->num_regions) { + ret = -EINVAL; + goto out_unlock; + } + + i = array_index_nospec(i, private->num_regions); region = private->region[i].data; if (copy_from_user((void *)region + pos, buf, count)) { ret = -EFAULT; diff --git a/drivers/s390/cio/vfio_ccw_chp.c b/drivers/s390/cio/vfio_ccw_chp.c index 38c176cf6295..f3015132d4b5 100644 --- a/drivers/s390/cio/vfio_ccw_chp.c +++ b/drivers/s390/cio/vfio_ccw_chp.c @@ -9,6 +9,7 @@ */ #include +#include #include #include "vfio_ccw_private.h" @@ -26,6 +27,13 @@ static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private, return -EINVAL; mutex_lock(&private->io_mutex); + + if (i >= private->num_regions) { + ret = -EINVAL; + goto out; + } + + i = array_index_nospec(i, private->num_regions); region = private->region[i].data; if (cio_update_schib(sch)) { @@ -97,6 +105,12 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, list_del(&crw->next); mutex_lock(&private->io_mutex); + if (i >= private->num_regions) { + ret = -EINVAL; + goto out; + } + + i = array_index_nospec(i, private->num_regions); region = private->region[i].data; if (crw) @@ -109,6 +123,7 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, region->crw = 0; +out: mutex_unlock(&private->io_mutex); kfree(crw); diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c index 45ec722d25ea..032a1cdf4df7 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -243,6 +243,7 @@ static ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev, return vfio_ccw_mdev_read_io_region(private, buf, count, ppos); default: index -= VFIO_CCW_NUM_REGIONS; + index = array_index_nospec(index, private->num_regions); return private->region[index].ops->read(private, buf, count, ppos); } @@ -295,6 +296,7 @@ static ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev, return vfio_ccw_mdev_write_io_region(private, buf, count, ppos); default: index -= VFIO_CCW_NUM_REGIONS; + index = array_index_nospec(index, private->num_regions); return private->region[index].ops->write(private, buf, count, ppos); } @@ -338,11 +340,8 @@ static int vfio_ccw_mdev_ioctl_get_region_info(struct vfio_device *vdev, VFIO_CCW_NUM_REGIONS + private->num_regions) return -EINVAL; - info->index = array_index_nospec(info->index, - VFIO_CCW_NUM_REGIONS + - private->num_regions); - i = info->index - VFIO_CCW_NUM_REGIONS; + i = array_index_nospec(i, private->num_regions); info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index); info->size = private->region[i].size; -- cgit v1.2.3 From 79c60b2c61105368dcc8444eb45847e21734f7c4 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:19 +0200 Subject: s390/vfio_ccw: Cancel existing workqueues The initialization of the io_work and crw_work workqueues begs the question of whether they should be un-initialized. Add the corresponding cleanup tags in _release_dev to ensure work isn't dispatched after the private struct is free'd. Suggested-by: Matthew Rosato Fixes: e5f84dbaea59 ("vfio: ccw: return I/O results asynchronously") Fixes: 3f02cb2fd9d2 ("vfio-ccw: Wire up the CRW irq and CRW region") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_ops.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c index 032a1cdf4df7..d361d1fde3a0 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -131,6 +131,13 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev) container_of(vdev, struct vfio_ccw_private, vdev); struct vfio_ccw_crw *crw, *temp; + /* + * Ensure these work items are fully drained, so none can + * fire after being released. + */ + cancel_work_sync(&private->io_work); + cancel_work_sync(&private->crw_work); + list_for_each_entry_safe(crw, temp, &private->crw, next) { list_del(&crw->next); kfree(crw); @@ -202,6 +209,14 @@ static void vfio_ccw_mdev_close_device(struct vfio_device *vdev) container_of(vdev, struct vfio_ccw_private, vdev); vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE); + + /* + * Ensure these work items are drained, in the event the + * device is re-opened instead of released. + */ + cancel_work_sync(&private->io_work); + cancel_work_sync(&private->crw_work); + vfio_ccw_unregister_dev_regions(private); } -- cgit v1.2.3 From 0c11f61a876ed6fcca53d442ed3f33ea8362a0f9 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:20 +0200 Subject: s390/vfio_ccw: Move cp cleanup out of not operational The fsm_notoper() routine is called when the device has been lost, and is (by definition) no longer operational. Since this can happen asynchronously from the normal behavior of the driver, the cleanup may happen when holding other locks in the calling sequence (notably, the cio subchannel lock). Push the cleanup of the private->cp resources to a workqueue, where it can be done out from under that lock sequence and a future patch can safely manage the locking requirements. Fixes: 204b394a23ad ("vfio/ccw: Move FSM open/close to MDEV open/close") Cc: stable@vger.kernel.org Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_drv.c | 9 +++++++++ drivers/s390/cio/vfio_ccw_fsm.c | 3 +-- drivers/s390/cio/vfio_ccw_ops.c | 13 +++++++++++++ drivers/s390/cio/vfio_ccw_private.h | 3 +++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c index 1a095085bc72..c197ad5ab580 100644 --- a/drivers/s390/cio/vfio_ccw_drv.c +++ b/drivers/s390/cio/vfio_ccw_drv.c @@ -125,6 +125,15 @@ void vfio_ccw_crw_todo(struct work_struct *work) eventfd_signal(private->crw_trigger); } +void vfio_ccw_notoper_todo(struct work_struct *work) +{ + struct vfio_ccw_private *private; + + private = container_of(work, struct vfio_ccw_private, notoper_work); + + cp_free(&private->cp); +} + /* * Css driver callbacks */ diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index 4d7988ea47ef..4d47a3c7b9a0 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -170,8 +170,7 @@ static void fsm_notoper(struct vfio_ccw_private *private, css_sched_sch_todo(sch, SCH_TODO_UNREG); private->state = VFIO_CCW_STATE_NOT_OPER; - /* This is usually handled during CLOSE event */ - cp_free(&private->cp); + queue_work(vfio_ccw_work_q, &private->notoper_work); } /* diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c index d361d1fde3a0..1df6d649565b 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -54,6 +54,7 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev) INIT_LIST_HEAD(&private->crw); INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo); INIT_WORK(&private->crw_work, vfio_ccw_crw_todo); + INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo); private->cp.guest_cp = kzalloc_objs(struct ccw1, CCWCHAIN_LEN_MAX); if (!private->cp.guest_cp) @@ -134,9 +135,16 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev) /* * Ensure these work items are fully drained, so none can * fire after being released. + * + * notoper_work should have nothing to do here, because only + * open devices could have channel_program resources in use + * and those would be released during close. Nevertheless, + * call flush here as well to be certain anything that was + * allocated is freed. */ cancel_work_sync(&private->io_work); cancel_work_sync(&private->crw_work); + flush_work(&private->notoper_work); list_for_each_entry_safe(crw, temp, &private->crw, next) { list_del(&crw->next); @@ -213,9 +221,14 @@ static void vfio_ccw_mdev_close_device(struct vfio_device *vdev) /* * Ensure these work items are drained, in the event the * device is re-opened instead of released. + * + * notoper_work needs to be given a chance to run if it + * is queued, so any memory associated with the channel + * program can be returned. */ cancel_work_sync(&private->io_work); cancel_work_sync(&private->crw_work); + flush_work(&private->notoper_work); vfio_ccw_unregister_dev_regions(private); } diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h index 0501d4bbcdbd..e2256402b089 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -102,6 +102,7 @@ struct vfio_ccw_parent { * @req_trigger: eventfd ctx for signaling userspace to return device * @io_work: work for deferral process of I/O handling * @crw_work: work for deferral process of CRW handling + * @notoper_work: work for deferred processing in not-operational state */ struct vfio_ccw_private { struct vfio_device vdev; @@ -125,11 +126,13 @@ struct vfio_ccw_private { struct eventfd_ctx *req_trigger; struct work_struct io_work; struct work_struct crw_work; + struct work_struct notoper_work; } __aligned(8); int vfio_ccw_sch_quiesce(struct subchannel *sch); void vfio_ccw_sch_io_todo(struct work_struct *work); void vfio_ccw_crw_todo(struct work_struct *work); +void vfio_ccw_notoper_todo(struct work_struct *work); extern struct mdev_driver vfio_ccw_mdev_driver; -- cgit v1.2.3 From 34f4feff3e90bd09308fad0974e97113b23b812a Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:21 +0200 Subject: s390/vfio_ccw: Selectively expand io_mutex The io_mutex was defined to serialize the io_regions, but then has also sort of been associated with the I/O themselves because of the close relationship they share. With the handful of races that are possible, the choices are either to: A) expand the scope of io_mutex to close these remaining windows, or B) reduce the scope of io_mutex to just io_region, and introduce a new lock mechanism for the remaining I/O resources This patch implements A, since B brings with it a lot more interactions that would need to be tracked and kept in a correct hierarchy. It also takes advantage of the workqueue element for cp_free() that now gets called out of fsm_notoper(), which could be invoked out of an interrupt context and thus cannot acquire a mutex itself. Fixes: 4f76617378ee ("vfio-ccw: protect the I/O region") Cc: stable@vger.kernel.org Signed-off-by: Eric Farman Reviewed-by: Matthew Rosato Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_chp.c | 2 +- drivers/s390/cio/vfio_ccw_cp.c | 8 +++++++- drivers/s390/cio/vfio_ccw_drv.c | 6 ++++-- drivers/s390/cio/vfio_ccw_fsm.c | 5 +++++ drivers/s390/cio/vfio_ccw_private.h | 3 ++- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_chp.c b/drivers/s390/cio/vfio_ccw_chp.c index f3015132d4b5..9269b54f5cfd 100644 --- a/drivers/s390/cio/vfio_ccw_chp.c +++ b/drivers/s390/cio/vfio_ccw_chp.c @@ -98,13 +98,13 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, if (pos + count > sizeof(*region)) return -EINVAL; + mutex_lock(&private->io_mutex); crw = list_first_entry_or_null(&private->crw, struct vfio_ccw_crw, next); if (crw) list_del(&crw->next); - mutex_lock(&private->io_mutex); if (i >= private->num_regions) { ret = -EINVAL; goto out; diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 5ef082b8289a..58722c4baa25 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -977,17 +977,23 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw) */ bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length) { + struct vfio_ccw_private *private = + container_of(cp, struct vfio_ccw_private, cp); struct ccwchain *chain; int i; if (!cp->initialized) return false; + mutex_lock(&private->io_mutex); list_for_each_entry(chain, &cp->ccwchain_list, next) { for (i = 0; i < chain->ch_len; i++) - if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) + if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) { + mutex_unlock(&private->io_mutex); return true; + } } + mutex_unlock(&private->io_mutex); return false; } diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c index c197ad5ab580..757ff5b2556e 100644 --- a/drivers/s390/cio/vfio_ccw_drv.c +++ b/drivers/s390/cio/vfio_ccw_drv.c @@ -91,6 +91,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work) is_final = !(scsw_actl(&irb->scsw) & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)); + mutex_lock(&private->io_mutex); if (scsw_is_solicited(&irb->scsw)) { cp_update_scsw(&private->cp, &irb->scsw); if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING) { @@ -98,9 +99,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work) cp_is_finished = true; } } - mutex_lock(&private->io_mutex); memcpy(private->io_region->irb_area, irb, sizeof(*irb)); - mutex_unlock(&private->io_mutex); /* * Reset to IDLE only if processing of a channel program @@ -110,6 +109,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work) */ if (cp_is_finished) private->state = VFIO_CCW_STATE_IDLE; + mutex_unlock(&private->io_mutex); if (private->io_trigger) eventfd_signal(private->io_trigger); @@ -131,7 +131,9 @@ void vfio_ccw_notoper_todo(struct work_struct *work) private = container_of(work, struct vfio_ccw_private, notoper_work); + mutex_lock(&private->io_mutex); cp_free(&private->cp); + mutex_unlock(&private->io_mutex); } /* diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index 4d47a3c7b9a0..5fd94e9d5c61 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -170,6 +170,7 @@ static void fsm_notoper(struct vfio_ccw_private *private, css_sched_sch_todo(sch, SCH_TODO_UNREG); private->state = VFIO_CCW_STATE_NOT_OPER; + /* This routine could be called from IRQ context, so defer */ queue_work(vfio_ccw_work_q, &private->notoper_work); } @@ -409,7 +410,11 @@ static void fsm_close(struct vfio_ccw_private *private, private->state = VFIO_CCW_STATE_STANDBY; spin_unlock_irq(&sch->lock); + + mutex_lock(&private->io_mutex); cp_free(&private->cp); + mutex_unlock(&private->io_mutex); + return; err_unlock: diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h index e2256402b089..739121116ab6 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -88,7 +88,8 @@ struct vfio_ccw_parent { * @state: internal state of the device * @completion: synchronization helper of the I/O completion * @io_region: MMIO region to input/output I/O arguments/results - * @io_mutex: protect against concurrent update of I/O regions + * @io_mutex: protect against concurrent update of I/O resources + * and @cp lifecycle * @region: additional regions for other subchannel operations * @cmd_region: MMIO region for asynchronous I/O commands other than START * @schib_region: MMIO region for SCHIB information -- cgit v1.2.3 From 16b0798024c0e9117e395829ddbbe70981c79d9c Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 05:30:22 +0200 Subject: s390/vfio_ccw: Implement a crw lock Unlike the channel_program struct, which covers synchronous I/O submissions and asynchronous interrupts, the CRW region relies exclusively on asynchronous events coming from hardware. Implement a lock to manage the list of those payloads, to ensure they are read cohesively. Fixes: 3f02cb2fd9d2 ("vfio-ccw: Wire up the CRW irq and CRW region") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato Reviewed-by: Farhan Ali Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger --- drivers/s390/cio/vfio_ccw_chp.c | 26 ++++++++++++++++---------- drivers/s390/cio/vfio_ccw_drv.c | 6 ++++++ drivers/s390/cio/vfio_ccw_ops.c | 4 ++++ drivers/s390/cio/vfio_ccw_private.h | 4 ++++ 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_chp.c b/drivers/s390/cio/vfio_ccw_chp.c index 9269b54f5cfd..7708eb4d6de0 100644 --- a/drivers/s390/cio/vfio_ccw_chp.c +++ b/drivers/s390/cio/vfio_ccw_chp.c @@ -93,18 +93,13 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; struct ccw_crw_region *region; struct vfio_ccw_crw *crw; + unsigned long flags; int ret; if (pos + count > sizeof(*region)) return -EINVAL; mutex_lock(&private->io_mutex); - crw = list_first_entry_or_null(&private->crw, - struct vfio_ccw_crw, next); - - if (crw) - list_del(&crw->next); - if (i >= private->num_regions) { ret = -EINVAL; goto out; @@ -113,6 +108,16 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, i = array_index_nospec(i, private->num_regions); region = private->region[i].data; + spin_lock_irqsave(&private->crw_lock, flags); + crw = list_first_entry_or_null(&private->crw, + struct vfio_ccw_crw, next); + + if (crw) + list_del(&crw->next); + + /* Drop CRW lock while copying to userspace */ + spin_unlock_irqrestore(&private->crw_lock, flags); + if (crw) memcpy(®ion->crw, &crw->crw, sizeof(region->crw)); @@ -122,15 +127,16 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, ret = count; region->crw = 0; - -out: - mutex_unlock(&private->io_mutex); - kfree(crw); /* Notify the guest if more CRWs are on our queue */ + spin_lock_irqsave(&private->crw_lock, flags); if (!list_empty(&private->crw) && private->crw_trigger) eventfd_signal(private->crw_trigger); + spin_unlock_irqrestore(&private->crw_lock, flags); + +out: + mutex_unlock(&private->io_mutex); return ret; } diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c index 757ff5b2556e..ab6b518cc353 100644 --- a/drivers/s390/cio/vfio_ccw_drv.c +++ b/drivers/s390/cio/vfio_ccw_drv.c @@ -118,11 +118,14 @@ void vfio_ccw_sch_io_todo(struct work_struct *work) void vfio_ccw_crw_todo(struct work_struct *work) { struct vfio_ccw_private *private; + unsigned long flags; private = container_of(work, struct vfio_ccw_private, crw_work); + spin_lock_irqsave(&private->crw_lock, flags); if (!list_empty(&private->crw) && private->crw_trigger) eventfd_signal(private->crw_trigger); + spin_unlock_irqrestore(&private->crw_lock, flags); } void vfio_ccw_notoper_todo(struct work_struct *work) @@ -286,6 +289,7 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private, unsigned int rsid) { struct vfio_ccw_crw *crw; + unsigned long flags; /* * If unable to allocate a CRW, just drop the event and @@ -303,7 +307,9 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private, crw->crw.erc = erc; crw->crw.rsid = rsid; + spin_lock_irqsave(&private->crw_lock, flags); list_add_tail(&crw->next, &private->crw); + spin_unlock_irqrestore(&private->crw_lock, flags); queue_work(vfio_ccw_work_q, &private->crw_work); } diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c index 1df6d649565b..5ce91285c7d5 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -55,6 +55,7 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev) INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo); INIT_WORK(&private->crw_work, vfio_ccw_crw_todo); INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo); + spin_lock_init(&private->crw_lock); private->cp.guest_cp = kzalloc_objs(struct ccw1, CCWCHAIN_LEN_MAX); if (!private->cp.guest_cp) @@ -131,6 +132,7 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev) struct vfio_ccw_private *private = container_of(vdev, struct vfio_ccw_private, vdev); struct vfio_ccw_crw *crw, *temp; + unsigned long flags; /* * Ensure these work items are fully drained, so none can @@ -146,10 +148,12 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev) cancel_work_sync(&private->crw_work); flush_work(&private->notoper_work); + spin_lock_irqsave(&private->crw_lock, flags); list_for_each_entry_safe(crw, temp, &private->crw, next) { list_del(&crw->next); kfree(crw); } + spin_unlock_irqrestore(&private->crw_lock, flags); kmem_cache_free(vfio_ccw_crw_region, private->crw_region); kmem_cache_free(vfio_ccw_schib_region, private->schib_region); diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h index 739121116ab6..3bd0171d38d0 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -98,6 +98,8 @@ struct vfio_ccw_parent { * @cp: channel program for the current I/O operation * @irb: irb info received from interrupt * @scsw: scsw info + * @crw_lock: serialization of CRW list information + * @crw: list of Channel Report Word elements * @io_trigger: eventfd ctx for signaling userspace I/O results * @crw_trigger: eventfd ctx for signaling userspace CRW information * @req_trigger: eventfd ctx for signaling userspace to return device @@ -120,6 +122,8 @@ struct vfio_ccw_private { struct channel_program cp; struct irb irb; union scsw scsw; + + spinlock_t crw_lock; struct list_head crw; struct eventfd_ctx *io_trigger; -- cgit v1.2.3 From 0c0e418dbcf0582bf80d8dbfd9b306607c065992 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 08:48:22 +0300 Subject: ASoC: SOF: ipc4-topology: Refresh copier IPC payload before widget setup The ipc_config_data buffer for copier widgets is built once during ipc_prepare (called from sof_pcm_setup_connected_widgets) and cached for reuse. For host copiers this buffer contains the copier_data with gtw_cfg.node_id (host DMA ID). For DAI copiers it additionally includes a dma_config_tlv trailer with stream_id and dma_channel_id for HDA link DMA. On suspend/resume, both host and link DMA streams are released and re-allocated with potentially different stream tags. The underlying copier_data and dma_config_tlv structures are correctly updated by host_config and sdw_hda_dai_hw_params respectively. However, since the widget list (spcm->stream[].list) persists across suspend, sof_pcm_hw_params skips sof_pcm_setup_connected_widgets and ipc_prepare never runs again to rebuild ipc_config_data. The stale cached payload is then sent to firmware with boot-time DMA channel assignments, causing DMA channel conflicts that lead to firmware errors and crashes. Fix this by refreshing copier_data and dma_config_tlv portions of ipc_config_data in sof_ipc4_widget_setup right before the IPC message is sent. This ensures the payload always reflects the current DMA state regardless of whether ipc_prepare ran. For DAI copiers, the gtw_cfg.config_length in copier_data is temporarily inflated to include the TLV size (matching the ipc_config_data layout) before copying, then restored, mirroring what sof_ipc4_prepare_copier_module does when first building the buffer. Fixes: e9c6b118de1a ("ASoC: SOF: make dma_config_tlv be an array") Cc: stable@vger.kernel.org Link: https://github.com/thesofproject/sof/issues/10700 Link: https://github.com/thesofproject/sof/issues/10955 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730054822.5913-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 95ad5266b0c6..6fdfb667cce8 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -3204,6 +3204,15 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data; + /* + * Refresh copier_data in ipc_config_data for host copiers. + * The node_id may have been updated by host_config after + * ipc_prepare, e.g. when host stream tags change after a + * suspend/resume cycle. + */ + if (swidget->id != snd_soc_dapm_buffer) + memcpy(ipc_data, &ipc4_copier->data, sizeof(ipc4_copier->data)); + msg = &ipc4_copier->msg; break; } @@ -3212,6 +3221,9 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget { struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier = dai->private; + struct sof_ipc4_copier_data *copier_data; + u32 gtw_cfg_config_length; + u32 tlv_size; pipeline = pipe_widget->private; if (pipeline->use_chain_dma) @@ -3220,6 +3232,27 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data; + /* + * Refresh copier_data and dma_config_tlv in ipc_config_data. + * These may have been updated after ipc_prepare, e.g. when + * link DMA stream tags change after a suspend/resume cycle. + * + * copier_data->gtw_cfg.config_length does not include the + * TLV size (it was restored after sof_ipc4_prepare_copier_module), + * so temporarily inflate it to match the ipc_config_data layout. + */ + copier_data = &ipc4_copier->data; + gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4; + tlv_size = ipc_size - sizeof(*copier_data) - gtw_cfg_config_length; + + copier_data->gtw_cfg.config_length += tlv_size / 4; + memcpy(ipc_data, copier_data, sizeof(*copier_data)); + copier_data->gtw_cfg.config_length = gtw_cfg_config_length / 4; + + if (tlv_size) + memcpy(ipc_data + sizeof(*copier_data) + gtw_cfg_config_length, + &ipc4_copier->dma_config_tlv, tlv_size); + msg = &ipc4_copier->msg; break; } -- cgit v1.2.3 From fa1ac3b9eb62918f039276d4d11cc02f682bf93c Mon Sep 17 00:00:00 2001 From: Woraphat Khiaodaeng Date: Thu, 30 Jul 2026 16:27:41 +0000 Subject: io_uring/zcrx: don't clear master_ctx from the import path import_zcrx() attaches an existing ifq to another ring. It never calls zcrx_set_ring_ctx() and so never takes the ->master_ctx reference, but its error path still passes @ctx to zcrx_unregister(), which clears ->master_ctx and drops its percpu_ref whenever ifq->master_ctx == ctx. That condition is reachable. A ring that registers an ifq with a non-zero event type_mask gets ->master_ctx pointed at itself, and nothing stops it from exporting that ifq with ZCRX_CTRL_EXPORT and importing the resulting fd back into the same ring. Failing the import after the refcount bumps -- an argument page mapped PROT_READ makes the copy_to_user() in import_zcrx() return -EFAULT -- then clears the ->master_ctx owned by the original registration, which is still live. Refcounts stay balanced and nothing is freed early, so there is no splat. The ring silently stops receiving ZCRX_EVENT_ALLOC_FAIL and ZCRX_EVENT_COPY: zcrx_send_notif() returns early on a NULL ->master_ctx, and ->master_ctx is only ever set on a freshly allocated ifq, so it cannot be restored without tearing the ring down. Pass NULL instead, matching zcrx_box_release() and the zcrx_export() error path. io_register_zcrx() only gets away with passing @ctx because zcrx_set_ring_ctx() runs after its last goto err. Fixes: 00d91481279f ("io_uring/zcrx: share an ifq between rings") Signed-off-by: Woraphat Khiaodaeng Link: https://patch.msgid.link/20260730162741.1125-1-worapat.kd2@gmail.com Reviewed-by: Pavel Begunkov [axboe: add pavel edit] Signed-off-by: Jens Axboe --- io_uring/zcrx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 76b9b0d54af9..f1464ea8ca64 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -808,7 +808,8 @@ err_xa_erase: scoped_guard(mutex, &ctx->mmap_lock) xa_erase(&ctx->zcrx_ctxs, id); err: - zcrx_unregister(ifq, ctx); + /* the import path never set the ->master_ctx ref, don't drop it */ + zcrx_unregister(ifq, NULL); return ret; } -- cgit v1.2.3 From 1afb8eaeec44fd011f2b93ccd9fd426d753d963b Mon Sep 17 00:00:00 2001 From: Jerome Tollet Date: Wed, 20 May 2026 07:55:44 +0530 Subject: drm/i915/hdmi: Poll for 200 msec for TMDS_Scrambler_Status HDMI 2.0 section 6.1.3.1 specifies that after enabling Scrambling_Enable and starting scrambled video transmission, the source should poll Scrambling_Status until it reads 1 or until a timeout of 200 ms expires. Add a polling step after enabling the HDMI port to check the scrambling status when HDMI scrambling is enabled. On some HDMI 2.0 sinks, omitting this check can result in 4K@60Hz (594 MHz) failing to come up correctly because the sink has not yet finished its scrambling setup. In practice, waiting for the scrambling status here fixes such sinks. While this synchronous polling is not itself explicitly required for correct modeset sequencing, HDMI 2.0 section 6.1.3.1 does recommend it as the way for the source to verify that the TMDS link is functioning correctly with scrambling enabled. v3: - Add explicit HDMI 2.0 section reference in code comment - Clarify commit message around the observed sink fix v2: - Poll TMDS_Scrambler_Status for up to 200 ms instead of using a fixed delay Reported-by: Jerome Tollet Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6868 Link: https://lore.kernel.org/dri-devel/20251230091037.5603-1-jerome.tollet@gmail.com/ Signed-off-by: Jerome Tollet Signed-off-by: Ankit Nautiyal Reviewed-by: Arun R Murthy Link: https://patch.msgid.link/20260520022544.3097252-1-ankit.k.nautiyal@intel.com (cherry picked from commit b7d51d65e4f12a48392d260613108ec262bc7774) Fixes: 15953637886d ("drm/i915: enable scrambling") Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 ++ drivers/gpu/drm/i915/display/intel_hdmi.c | 26 ++++++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_hdmi.h | 2 ++ 3 files changed, 30 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 6296635c4e79..95008e0c0eb8 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3501,6 +3501,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state, } intel_ddi_buf_enable(encoder, buf_ctl); + + intel_hdmi_poll_for_scrambling_enable(crtc_state, connector); } static void intel_ddi_enable(struct intel_atomic_state *state, diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 9076c2b176ec..b9d11fb8559d 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2728,6 +2728,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_ drm_connector_attach_max_bpc_property(&connector->base, 8, 12); } +/* + * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after + * enabling Scrambling_Enable and starting scrambled video + * transmission, poll Scrambling_Status for up to 200 ms. + */ +void +intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector) +{ + struct intel_connector *connector = to_intel_connector(_connector); + struct intel_display *display = to_intel_display(crtc_state); + bool scrambling_enabled = false; + int ret; + + if (!crtc_state->hdmi_scrambling) + return; + + /* Poll for a max of 200 msec as per HDMI spec */ + ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base), + scrambling_enabled, 1000, 200 * 1000, false); + if (ret) + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n", + connector->base.base.id, connector->base.name); +} + /* * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup * @encoder: intel_encoder diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index be2fad57e4ad..0fa3661568e8 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, unsigned int type, void *frame, ssize_t len); +void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector); #endif /* __INTEL_HDMI_H__ */ -- cgit v1.2.3 From d14b5d0e97fccd27974fedc03b903408872907fd Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 27 Jul 2026 20:30:58 -0500 Subject: selinux: reject a permission value exceeding the class permission count perm_read() bounds a permission value by SEL_VEC_MAX but never by the nprim of the owning class or common, which is taken verbatim from the policy image. security_get_permissions() then writes perms[value - 1] into an nprim-sized kcalloc() array, so a class declaring fewer permissions than its largest permission value drives an out-of-bounds heap write. The top-level symbol tables are validated this way; the nested per-class permission table is not. Reject a permission whose value exceeds nprim, which is already set when perm_read() runs. Well-formed policies are unaffected. Cc: stable@vger.kernel.org Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley [PM: tweak comment for line length] Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index ead504a639e3..5ff4d095ec2e 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1175,6 +1175,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f rc = -EINVAL; if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX) goto bad; + /* indexes an nprim-sized array in security_get_permissions() */ + if (perdatum->value > s->nprim) + goto bad; rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) -- cgit v1.2.3 From 9a82dcd98b6e6e11cfd162410967951f12152528 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 27 Jul 2026 20:30:59 -0500 Subject: selinux: reject a class permission count below its inherited common security_get_permissions() maps an inherited common's permissions into an array sized by the class's own permissions.nprim, but class_read() takes that nprim verbatim from the policy image and never checks that it covers the common. A class that inherits a common of N permissions while declaring a smaller nprim is accepted, and on load the common's permissions are written past the class-sized array -- an out-of-bounds heap write. Reject a class whose permission count is below its inherited common's. Well-formed policies, where the class count already includes the inherited permissions, are unaffected. Cc: stable@vger.kernel.org Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 5ff4d095ec2e..69777e885ae7 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1422,6 +1422,18 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * cladatum->comkey); goto bad; } + + /* + * security_get_permissions() maps the common's permissions + * into an array sized by this class's nprim, so a class must + * declare at least as many as the common it inherits. + */ + if (cladatum->permissions.nprim < + cladatum->comdatum->permissions.nprim) { + pr_err("SELinux: class %s has fewer permissions than common %s\n", + key, cladatum->comkey); + goto bad; + } } for (i = 0; i < nel; i++) { rc = perm_read(p, &cladatum->permissions, fp); -- cgit v1.2.3 From bc0e8faf90e776a2f1f3967a04e8091e6bdb4977 Mon Sep 17 00:00:00 2001 From: Kyumin Lee Date: Fri, 31 Jul 2026 04:27:34 +0900 Subject: io_uring: preserve task restrictions across exec Per-task restrictions apply to all rings created by a task. Once installed, they should not be dropped across exec. For a task that has used io_uring, the exec cancellation path calls __io_uring_free(). This frees both the task context and the per-task restriction, so a ring created after exec is unrestricted. Split task context cleanup into io_uring_free_tctx(), and use it from the exec cancellation path. Keep __io_uring_free() for final task cleanup, where both the context and restriction are released. Fixes: ed82f35b926b ("io_uring: allow registration of per-task restrictions") Cc: stable@vger.kernel.org # 7.1+ Signed-off-by: Kyumin Lee Link: https://patch.msgid.link/20260730192734.459247-1-fyonglkm@gmail.com Signed-off-by: Jens Axboe --- io_uring/cancel.c | 2 +- io_uring/tctx.c | 7 ++++++- io_uring/tctx.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/io_uring/cancel.c b/io_uring/cancel.c index 8c6fa6f367e4..7d7820eab878 100644 --- a/io_uring/cancel.c +++ b/io_uring/cancel.c @@ -660,6 +660,6 @@ end_wait: */ atomic_dec(&tctx->in_cancel); /* for exec all current's requests should be gone, kill tctx */ - __io_uring_free(current); + io_uring_free_tctx(current); } } diff --git a/io_uring/tctx.c b/io_uring/tctx.c index cc3bf2b3bdbc..466b7300e208 100644 --- a/io_uring/tctx.c +++ b/io_uring/tctx.c @@ -43,7 +43,7 @@ static struct io_wq *io_init_wq_offload(struct io_ring_ctx *ctx, return io_wq_create(concurrency, &data); } -void __io_uring_free(struct task_struct *tsk) +void io_uring_free_tctx(struct task_struct *tsk) { struct io_uring_task *tctx = tsk->io_uring; struct io_tctx_node *node; @@ -67,6 +67,11 @@ void __io_uring_free(struct task_struct *tsk) kfree(tctx); tsk->io_uring = NULL; } +} + +void __io_uring_free(struct task_struct *tsk) +{ + io_uring_free_tctx(tsk); if (tsk->io_uring_restrict) { io_put_bpf_filters(tsk->io_uring_restrict); kfree(tsk->io_uring_restrict); diff --git a/io_uring/tctx.h b/io_uring/tctx.h index 2310d2a0c46d..76ad1ad4594e 100644 --- a/io_uring/tctx.h +++ b/io_uring/tctx.h @@ -12,6 +12,7 @@ void io_uring_del_tctx_node(unsigned long index); int __io_uring_add_tctx_node(struct io_ring_ctx *ctx); int __io_uring_add_tctx_node_from_submit(struct io_ring_ctx *ctx); void io_uring_clean_tctx(struct io_uring_task *tctx); +void io_uring_free_tctx(struct task_struct *tsk); void io_uring_unreg_ringfd(void); int io_ringfd_register(struct io_ring_ctx *ctx, void __user *__arg, -- cgit v1.2.3 From 20697ecb299cd77b4cf8b28f655e56606b0472d8 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 23 Jul 2026 10:38:06 +0800 Subject: drm/bridge: ps8640: propagate AUX transfer register errors ps8640_aux_transfer_msg() programs the AUX address registers, starts the AUX transfer, waits for SWAUX_SEND to clear, and reads the AUX status register. Several of those regmap operations have return values, but the function only checks a stale ret after the status read. Propagate failures from the address write, transfer start, completion poll, and status read. This avoids returning a transfer length when the bridge register transaction or AUX completion wait failed. Fixes: 13afcdd7277e ("drm/bridge: parade-ps8640: Add support for AUX channel") Signed-off-by: Pengpeng Hou Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260723103509.2-ps8640-v2-pengpeng@iscas.ac.cn --- drivers/gpu/drm/bridge/parade-ps8640.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index b93514023baa..39ab94a73c88 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -257,8 +257,14 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux, addr_len[PAGE0_SWAUX_LENGTH - base] = (len == 0) ? SWAUX_NO_PAYLOAD : ((len - 1) & SWAUX_LENGTH_MASK); - regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len, - ARRAY_SIZE(addr_len)); + ret = regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len, + ARRAY_SIZE(addr_len)); + if (ret) { + DRM_DEV_ERROR(dev, + "failed to write AUX address %#x, len %zu: %d\n", + msg->address, len, ret); + return ret; + } if (len && (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE)) { @@ -274,13 +280,22 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux, } } - regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND); + ret = regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND); + if (ret) { + DRM_DEV_ERROR(dev, "failed to start AUX transfer: %d\n", ret); + return ret; + } /* Zero delay loop because i2c transactions are slow already */ - regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data, - !(data & SWAUX_SEND), 0, 50 * 1000); + ret = regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data, + !(data & SWAUX_SEND), 0, 50 * 1000); + if (ret) { + DRM_DEV_ERROR(dev, "failed to complete AUX transfer: %d\n", + ret); + return ret; + } - regmap_read(map, PAGE0_SWAUX_STATUS, &data); + ret = regmap_read(map, PAGE0_SWAUX_STATUS, &data); if (ret) { DRM_DEV_ERROR(dev, "failed to read PAGE0_SWAUX_STATUS: %d\n", ret); -- cgit v1.2.3 From ae63720dd7c3647d64f7a85e5e1870f90eb569d6 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 30 Jul 2026 10:17:24 +0300 Subject: ASoC: SOF: topology: Use acpi mach from the machine driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parameters may be changed by the sof_sdw machine driver is the SOC_SDW_PCH_DMIC quirk is set. Use the mach_params from the machine driver to ensure the sof_sdw_get_tplg_files() function select the right function topologies. Fixes: 2fbeff33381c ("ASoC: Intel: add sof_sdw_get_tplg_files ops") Cc: stable@vger.kernel.org Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730071724.22296-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/sof-function-topology-lib.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index 2f2c902ef90c..b6e5a40b78cc 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -31,7 +31,12 @@ enum tplg_device_id { int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, const char *prefix, const char ***tplg_files, bool best_effort) { - struct snd_soc_acpi_mach_params mach_params = mach->mach_params; + struct snd_soc_acpi_mach *card_mach = dev_get_platdata(card->dev); + /* + * Use the acpi mach from the machine driver because the machine driver + * may change the dmic_num based on the machine driver quirk. + */ + struct snd_soc_acpi_mach_params mach_params = card_mach->mach_params; struct snd_soc_dai_link *dai_link; const struct firmware *fw; char platform[SOF_INTEL_PLATFORM_NAME_MAX]; -- cgit v1.2.3 From 3860d8748af315bfee6fe669fddc1fc17d3214db Mon Sep 17 00:00:00 2001 From: Jijie Shao Date: Fri, 24 Jul 2026 17:30:36 +0800 Subject: net: hns3: fix speed configuration residue after driver reload After setting a 100G optical port to 40G via ethtool and reloading the driver, the port remains at 40G instead of reverting to the firmware default speed of 100G. The commit referenced in Fixes: added two overwrites in hclge_init_ae_dev() for non-copper media, so that optical ports connected to forced-mode remotes inherit the firmware-preset autoneg and speed instead of the hardcoded defaults: req_autoneg = mac.autoneg req_speed = mac.speed (when autoneg disabled) The autoneg overwrite keeps existing behavior: hclge_set_autoneg_speed_dup() already uses mac.autoneg (not req_autoneg) since it was introduced, so autoneg inheritance from firmware was already in place. This part is kept. The speed overwrite, however, introduces the residue: mac.speed reflects whatever was last programmed into the MAC, and after unload firmware does not restore the MAC speed to the flash default. So if the user changed speed via ethtool in a prior load, mac.speed still carries that value on reload and req_speed inherits it. Fix by dropping the req_speed overwrite only. req_speed keeps the firmware default value set in hclge_configure() (cfg.default_speed), so a reload reverts the speed to default, matching the expectation that a driver reload resets link configuration. Trade-off: on optical ports whose firmware default speed does not match a forced-mode remote, reload now drops the link and the user must re-apply ethtool configuration. This is acceptable: a driver reload is expected to reset link configuration, not to inherit runtime state from before unload. The autoneg inheritance is left in place as established behavior; changing it is out of scope for this patch and would itself be a user-perceivable behavior change. Fixes: d9d349c4e8a0 ("net: hns3: differentiate autoneg default values between copper and fiber") Signed-off-by: Jijie Shao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260724093036.426631-1-shaojijie@huawei.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index fc8587c80813..164c3ecf195c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -9498,12 +9498,8 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_ptp_uninit; - if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER) { + if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER) hdev->hw.mac.req_autoneg = hdev->hw.mac.autoneg; - if (hdev->hw.mac.autoneg == AUTONEG_DISABLE && - hdev->hw.mac.speed != SPEED_UNKNOWN) - hdev->hw.mac.req_speed = hdev->hw.mac.speed; - } ret = hclge_set_autoneg_speed_dup(hdev); if (ret) { -- cgit v1.2.3 From e780e4917d43683224812400fe3dc4816fceba75 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 11:59:14 +0300 Subject: ASoC: SOF: sof-audio: Fix error path in sof_widget_setup_unlocked() If either tplg_ops->dai_config or widget_kcontrol_setup fail during widget setup we would double decrement the use_count of the widget because the sof_widget_free_unlocked() would be called twice, similarly the core_put would be invoked twice as well. Since the use_count and core_put() is handled within the widget_free function we need to return without falling through the pipe_widget_free label. The fixes tag is picked to the last change around this part of the code which is adequately old enough for backporting purposes. Link: https://github.com/thesofproject/sof/issues/10826 Fixes: 31ed8da1c8e5 ("ASoC: SOF: sof-audio: Modify logic for enabling/disabling topology cores") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260730085914.27546-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index acf56607bc9c..24614e506019 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -146,7 +146,6 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_pipeline *spipe = swidget->spipe; - bool use_count_decremented = false; int ret; int i; @@ -225,9 +224,10 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, return 0; widget_free: - /* widget use_count will be decremented by sof_widget_free() */ + /* widget use_count and core_put handled by sof_widget_free() */ sof_widget_free_unlocked(sdev, swidget); - use_count_decremented = true; + return ret; + pipe_widget_free: if (swidget->id != snd_soc_dapm_scheduler) { sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); @@ -242,8 +242,7 @@ pipe_widget_free: } } use_count_dec: - if (!use_count_decremented) - swidget->use_count--; + swidget->use_count--; return ret; } -- cgit v1.2.3 From 1881f2efbf7f78dc0a79a387b29fde6ff56d3731 Mon Sep 17 00:00:00 2001 From: Fan Ye Date: Mon, 27 Jul 2026 12:29:48 +0000 Subject: Revert "net: thunderbolt: Enable end-to-end flow control also in transmit" This reverts commit a8065af3346ebd7c76ebc113451fb3ba94cf7769. Per the USB4 spec, a Transmit Descriptor Ring with E2E flow control disabled does not require any credits to be available before the Host Interface Adapter Layer transmits a tunneled packet from it. Once E2E is enabled on that ring the controller must first obtain end-to-end credits. The ASMedia ASM4242 USB4 host router (PCI 1b21:2425) never delivers those credits. The controller does accept the configuration: reading the ring OPTIONS register back right after tb_ring_start() returns exactly what was written, including RING_FLAG_E2E_FLOW_CONTROL (bit 28) and the E2E HopID field. No credit ever arrives though, so the Tx ring's hardware consumer index never advances and the link carries no traffic at all. Measured on two hosts connected point to point, onboard ASM4242 on MSI X870E and X870, v6.17, stock drivers/net/thunderbolt/main.c with only this revert applied on top: before: 100% packet loss to the peer; thunderbolt0 is up and the XDomain handshake completes ("new host found"), but iperf3 fails with "No route to host" once the neighbour entry expires after: 0% packet loss, 0.28 ms RTT; iperf3 4.21 Gb/s one way and 5.17 Gb/s the other (5 runs each, stddev <= 0.02), 1 retransmit in 10 s An instrumented build additionally showed a frozen-Tx-consumer watchdog firing ~30k times in a 10 s window before this change. Rx-side E2E is not touched by this revert, so peers that do return credits keep receive-side flow control. ASMedia does not look like an isolated case. The out-of-tree thunderbolt-ibverbs project disables native E2E on AMD NHI by default, noting that "Strix Halo has reproduced TX completion wedges with multiple native E2E rings active" -- the same failure mode, on a different vendor. Since the driver has no way to tell in advance which host router returns the credits, going back to the previous behaviour looks safer than adding a quirk per affected part; Tx-side E2E can be reintroduced as an opt-in for controllers that are known to implement the credit return. Note that the reverted commit was not fixing a reported problem, it was derived from the spec wording alone, so this revert is not expected to regress a known workload. Cc'ing the original author in case there was one. Fixes: a8065af3346e ("net: thunderbolt: Enable end-to-end flow control also in transmit") Cc: zhangjianrong Signed-off-by: Fan Ye Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260727123002.25225-1-fy15309206903@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/thunderbolt/main.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 02a91650561a..c5670d61820c 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -925,12 +925,8 @@ static int tbnet_open(struct net_device *dev) netif_carrier_off(dev); - flags = RING_FLAG_FRAME; - /* Only enable full E2E if the other end supports it too */ - if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E) - flags |= RING_FLAG_E2E; - - ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags); + ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE, + RING_FLAG_FRAME); if (!ring) { netdev_err(dev, "failed to allocate Tx ring\n"); return -ENOMEM; @@ -949,6 +945,11 @@ static int tbnet_open(struct net_device *dev) sof_mask = BIT(TBIP_PDF_FRAME_START); eof_mask = BIT(TBIP_PDF_FRAME_END); + flags = RING_FLAG_FRAME; + /* Only enable full E2E if the other end supports it too */ + if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E) + flags |= RING_FLAG_E2E; + ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags, net->tx_ring.ring->hop, sof_mask, eof_mask, tbnet_start_poll, net); -- cgit v1.2.3 From 683c6ba6e58e6ed1037831ea97dd58d9c0e76b8d Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Sat, 25 Jul 2026 23:39:30 +0000 Subject: bonding: alb: re-check primary_is_promisc under RTNL in bond_alb_monitor bond_alb_monitor() reads primary_is_promisc under RCU, then drops RCU and takes RTNL via rtnl_trylock() before undoing the promiscuity it set on the active slave. In that window the active slave can change under RTNL (RTM_DELLINK -> __bond_release_one() -> bond_alb_handle_active_change()), which already drops the promiscuity and clears primary_is_promisc. The monitor still acts on the stale decision: if the slave was removed with no failover, curr_active_slave is now NULL and the deref faults; if it failed over, the stale dev_set_promiscuity(-1) underflows the new slave's promiscuity counter and pins it in IFF_PROMISC. Oops: general protection fault, probably for non-canonical address ... KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Workqueue: b42 bond_alb_monitor RIP: 0010:bond_alb_monitor (drivers/net/bonding/bond_alb.c:1600) 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) Kernel panic - not syncing: Fatal exception Re-check primary_is_promisc (and curr_active_slave) after taking RTNL so the monitor only undoes an increment it still owns. The other bonding monitors already re-read state under RTNL in their commit phase (bond_miimon_commit/bond_ab_arp_commit); bond_alb_monitor() was the only one acting on the pre-trylock decision. Fixes: d0e81b7e2246 ("bonding: Acquire correct locks in alb for promisc change") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Nikolay Aleksandrov Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260725233930.2957317-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_alb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 2d37b07c8215..839f7482dc18 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -1534,8 +1534,8 @@ void bond_alb_monitor(struct work_struct *work) struct bonding *bond = container_of(work, struct bonding, alb_work.work); struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct slave *slave, *curr; struct list_head *iter; - struct slave *slave; if (!bond_has_slaves(bond)) { atomic_set(&bond_info->tx_rebalance_counter, 0); @@ -1597,9 +1597,11 @@ void bond_alb_monitor(struct work_struct *work) * because a slave was disabled then * it can now leave promiscuous mode. */ - dev_set_promiscuity(rtnl_dereference(bond->curr_active_slave)->dev, - -1); - bond_info->primary_is_promisc = 0; + curr = rtnl_dereference(bond->curr_active_slave); + if (bond_info->primary_is_promisc && curr) { + dev_set_promiscuity(curr->dev, -1); + bond_info->primary_is_promisc = 0; + } rtnl_unlock(); rcu_read_lock(); -- cgit v1.2.3 From 53a43508ee332d8bffe40590c3d189c92a551f9f Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Mon, 27 Jul 2026 12:19:37 +0100 Subject: net: devmem: prevent net-iov / page mixing We should either have net_iov or page backed frags in a single skb, otherwise it blows up down the stack. Don't allow mixing in zerocopy_fill_skb_from_devmem(). Fixes: bd61848900bff ("net: devmem: Implement TX path") Cc: stable@vger.kernel.org Signed-off-by: Pavel Begunkov Acked-by: Stanislav Fomichev Reviewed-by: Mina Almasry Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/e3199788c4732545627a4721097ebb71ad737bab.1785150502.git.asml.silence@gmail.com Signed-off-by: Jakub Kicinski --- net/core/datagram.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/datagram.c b/net/core/datagram.c index c285c6465923..173b5d97bd40 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -712,6 +712,9 @@ zerocopy_fill_skb_from_devmem(struct sk_buff *skb, struct iov_iter *from, size_t virt_addr, size, off; struct net_iov *niov; + if (i && skb_frags_readable(skb)) + return -EFAULT; + /* Devmem filling works by taking an IOVEC from the user where the * iov_addrs are interpreted as an offset in bytes into the dma-buf to * send from. We do not support other iter types. -- cgit v1.2.3 From c2707480cfbf19c7619acc9c089d17f20869821f Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Fri, 24 Jul 2026 16:40:15 +0200 Subject: net/packet: reset the MAC header on the packet-socket transmit path packet_parse_headers() resets the MAC header only for a SOCK_RAW frame whose socket did not bind a protocol. A protocol-bound SOCK_RAW socket, any SOCK_DGRAM frame, and the legacy SOCK_PACKET path therefore leave skb->mac_header unset here. For frames sent via __dev_queue_xmit() this is harmless: it resets the MAC header unconditionally. But the packet-socket PACKET_QDISC_BYPASS path uses dev_direct_xmit(), which does not, so the frame reaches ndo_start_xmit() with the MAC header unset. A driver that reads eth_hdr(skb) on transmit then dereferences skb->head + (u16)~0, an out-of-bounds access ~64 KiB past the head -- the same class fixed for one consumer in commit f5089008f90c ("macsec: do not read an unset MAC header in macsec_encrypt()"). packet_parse_headers() runs only on the transmit path, where skb->data points at the start of the L2 header for every packet-socket type regardless of its length: SOCK_RAW and SOCK_PACKET carry a user-supplied header and SOCK_DGRAM has one built by dev_hard_header(). Reset the MAC header unconditionally, mirroring __dev_queue_xmit(), so the frame is anchored on the bypass path too. Found by 0sec (https://0sec.ai) using automated source analysis; verified against source and matched to the macsec KASAN report in f5089008f90c. Compile-tested. Fixes: 75c65772c3d1 ("net/packet: Ask driver for protocol if not provided by user") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260724144015.63219-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski --- net/packet/af_packet.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index e75d2932475a..5ae0511e89e3 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1924,11 +1924,12 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock) { int depth; + /* On TX skb->data is the L2 header; anchor it for all socket types. */ + skb_reset_mac_header(skb); + if ((!skb->protocol || skb->protocol == htons(ETH_P_ALL)) && - sock->type == SOCK_RAW) { - skb_reset_mac_header(skb); + sock->type == SOCK_RAW) skb->protocol = dev_parse_header_protocol(skb); - } /* Move network header to the right position for VLAN tagged packets */ if (likely(skb->dev->type == ARPHRD_ETHER) && -- cgit v1.2.3 From ec680ea4ba1bca92a767fb7e7869758bfdd886e3 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 27 Jul 2026 23:26:30 -0700 Subject: enic: fix tx_hang_reset use-after-free on device removal enic_remove() cancels the reset and change_mtu_work items but does not cancel tx_hang_reset. A TX timeout that fires while the device is being removed can schedule enic_tx_hang_reset() so that it runs after free_netdev(), resulting in a use-after-free. cancel_work_sync() alone is not sufficient here: the still-live watchdog and notify paths can re-schedule these work items in the window between the cancel and unregister_netdev(). Use disable_work_sync(), which cancels the work and blocks any subsequent schedule_work() from requeuing it, and apply it to the reset and change_mtu_work items as well so the same requeue race is closed for all teardown work. Fixes: 937317c7c109 ("enic: do hang reset only in case of tx timeout") Signed-off-by: Satish Kharat Link: https://patch.msgid.link/20260728062730.2394873-1-satishkh@cisco.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cisco/enic/enic_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index e7125b818087..d98f7e7ccab9 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -3011,8 +3011,9 @@ static void enic_remove(struct pci_dev *pdev) if (netdev) { struct enic *enic = netdev_priv(netdev); - cancel_work_sync(&enic->reset); - cancel_work_sync(&enic->change_mtu_work); + disable_work_sync(&enic->reset); + disable_work_sync(&enic->tx_hang_reset); + disable_work_sync(&enic->change_mtu_work); unregister_netdev(netdev); enic_dev_deinit(enic); vnic_dev_close(enic->vdev); -- cgit v1.2.3 From 6ddfba2ea98db21b001e0e5c472499156224650c Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 28 Jul 2026 07:43:38 +0300 Subject: net/mlx5e: TC, Check if flow is PEER before acquiring devcom lock In case __mlx5e_add_fdb_flow() fails in lower levels, the flow is deleted via mlx5e_tc_del_flow(), and mlx5e_tc_del_flow() is acquiring ESW devcom lock without condition. In addition, in case of peer_flow, __mlx5e_add_fdb_flow() is called while holding ESW devcom comp lock. This results in an AA deadlock. To fix this, introduce a new PEER flag that is set on flows created as peer flows (the duplicate flows on peer devices), and check it in mlx5e_tc_del_flow() before acquiring ESW devcom lock. Lockdep splat: ============================================ WARNING: possible recursive locking detected ============================================ Possible unsafe locking scenario: CPU0 ---- lock(&comp->lock_key#2); lock(&comp->lock_key#2); *** DEADLOCK *** Call Trace: dump_stack_lvl+0x69/0xa0 print_deadlock_bug.cold+0xbd/0xca __lock_acquire+0x1671/0x2ec0 lock_acquire+0x10e/0x2e0 down_read+0x95/0x430 mlx5_devcom_for_each_peer_begin+0x4e/0xe0 [mlx5_core] mlx5e_tc_del_flow+0x11d/0xa70 [mlx5_core] mlx5e_flow_put+0x99/0x100 [mlx5_core] __mlx5e_add_fdb_flow+0x409/0xf00 [mlx5_core] mlx5e_configure_flower+0x2a86/0x4100 [mlx5_core] mlx5e_rep_setup_tc_cls_flower+0x12f/0x1b0 [mlx5_core] mlx5e_rep_setup_tc_cb+0x153/0x750 [mlx5_core] tc_setup_cb_add+0x1dc/0x470 fl_change+0x2f4d/0x626d [cls_flower] tc_new_tfilter+0x79b/0x2310 rtnetlink_rcv_msg+0x778/0xad0 do_syscall_64+0x70/0x960 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Fixes: 04de7dda7394 ("net/mlx5e: Infrastructure for duplicated offloading of TC flows") Signed-off-by: Shay Drory Reviewed-by: Cosmin Ratiu Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260728044338.2271143-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h index 28cab4bf525c..7bfe7cdc5770 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h @@ -30,6 +30,7 @@ enum { MLX5E_TC_FLOW_FLAG_FAILED = MLX5E_TC_FLOW_BASE + 9, MLX5E_TC_FLOW_FLAG_SAMPLE = MLX5E_TC_FLOW_BASE + 10, MLX5E_TC_FLOW_FLAG_USE_ACT_STATS = MLX5E_TC_FLOW_BASE + 11, + MLX5E_TC_FLOW_FLAG_PEER = MLX5E_TC_FLOW_BASE + 12, }; struct mlx5e_tc_flow_parse_attr { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 1bc7b9019124..b290beb4369a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -2161,7 +2161,8 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, if (mlx5e_is_eswitch_flow(flow)) { struct mlx5_devcom_comp_dev *devcom = flow->priv->mdev->priv.eswitch->devcom; - if (!mlx5_devcom_for_each_peer_begin(devcom)) { + if (flow_flag_test(flow, PEER) || + !mlx5_devcom_for_each_peer_begin(devcom)) { mlx5e_tc_del_fdb_flow(priv, flow); return; } @@ -4628,6 +4629,7 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f, else in_mdev = priv->mdev; + flow_flags |= BIT(MLX5E_TC_FLOW_FLAG_PEER); parse_attr = flow->attr->parse_attr; peer_flow = __mlx5e_add_fdb_flow(peer_priv, f, flow_flags, parse_attr->filter_dev, -- cgit v1.2.3 From cd09971dcc1c499ae0879010a00e9dba87abdc4f Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Mon, 27 Jul 2026 16:45:48 +0000 Subject: pds_core: keep the health thread stopped during reset Commit d9407ff11809 ("pds_core: Prevent health thread from running during reset/remove") stops the health thread with cancel_work_sync() before a reset, but a devcmd timeout during pdsc_fw_down() re-queues health_work, so pdsc_health_thread() runs again mid-reset and double allocates the core DMA queues via pdsc_fw_up(). Only the reset path is affected: on remove PDSC_S_STOPPING_DRIVER gates the health thread and the workqueue is destroyed. Use disable_work_sync() to cancel health_work and block further queue_work() on it, and enable_work() in pdsc_restart_health_thread() to re-allow it after the reset. disable_work_sync() keeps a disable depth, so every disable must be matched by one enable. pdsc_reset_prepare() stops the health thread and pdsc_reset_done() restarts it. On the AER path pdsc_pci_error_detected() calls pdsc_reset_prepare(), then pdsc_pci_error_resume() re-inits via pci_reset_function_locked() (pds_core has no .slot_reset handler), which runs the pair again - stopping the thread twice but restarting it once. Gate the disable and enable on a health_stopped flag so each fires at most once per stopped/running transition. Fixes: d9407ff11809 ("pds_core: Prevent health thread from running during reset/remove") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260629200358.2626129-1-nikhil.rao%40amd.com?part=2 Signed-off-by: Nikhil P. Rao Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260727164548.359562-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/core.h | 1 + drivers/net/ethernet/amd/pds_core/main.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h index b7fe9ad73349..a1d41329209f 100644 --- a/drivers/net/ethernet/amd/pds_core/core.h +++ b/drivers/net/ethernet/amd/pds_core/core.h @@ -171,6 +171,7 @@ struct pdsc { struct timer_list wdtimer; unsigned int wdtimer_period; struct work_struct health_work; + bool health_stopped; struct devlink_health_reporter *fw_reporter; u32 fw_recoveries; diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 8d94a4d70395..71a1d4b001c2 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -470,8 +470,10 @@ static void pdsc_stop_health_thread(struct pdsc *pdsc) return; timer_shutdown_sync(&pdsc->wdtimer); - if (pdsc->health_work.func) - cancel_work_sync(&pdsc->health_work); + if (pdsc->health_work.func && !pdsc->health_stopped) { + disable_work_sync(&pdsc->health_work); + pdsc->health_stopped = true; + } } static void pdsc_restart_health_thread(struct pdsc *pdsc) @@ -479,6 +481,10 @@ static void pdsc_restart_health_thread(struct pdsc *pdsc) if (pdsc->pdev->is_virtfn) return; + if (pdsc->health_stopped) { + enable_work(&pdsc->health_work); + pdsc->health_stopped = false; + } timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0); mod_timer(&pdsc->wdtimer, jiffies + 1); } -- cgit v1.2.3 From 57d635329d799b79096155cdf47ee0013d6780d1 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Mon, 27 Jul 2026 17:00:30 +0000 Subject: pds_core: cancel pending PCI reset work on AER recovery pdsc_check_pci_health() queues pci_reset_work when it sees a broken PCI connection, and nothing cancels it. When the PCI core starts AER recovery, pdsc_pci_error_detected() runs pdsc_reset_prepare() and recovers the device, but a pci_reset_work queued just before is left pending. If it runs after recovery released the device lock, it resets a device the driver now considers healthy, bouncing the link for no reason. Cancel pci_reset_work in pdsc_pci_error_detected() after pdsc_reset_prepare(), which has already stopped the health thread so it cannot requeue the work. cancel_work_sync() is safe under the device lock here because pdsc_pci_reset_thread() uses pci_try_reset_function(), which returns instead of blocking on the lock. Only PFs initialize pci_reset_work, so guard the cancel with !is_virtfn. Fixes: 81665adf25d2 ("pds_core: Fix pdsc_check_pci_health function to use work thread") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260714180223.1642792-2-nikhil.rao%40amd.com?part=1 Signed-off-by: Nikhil P. Rao Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260727170030.361116-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 71a1d4b001c2..9a2c64198d03 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -561,7 +561,11 @@ static pci_ers_result_t pdsc_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t error) { if (error == pci_channel_io_frozen) { + struct pdsc *pdsc = pci_get_drvdata(pdev); + pdsc_reset_prepare(pdev); + if (!pdev->is_virtfn) + cancel_work_sync(&pdsc->pci_reset_work); return PCI_ERS_RESULT_NEED_RESET; } -- cgit v1.2.3 From e7188199eff46a636f3436356f0aae039be6dd66 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 22 Jul 2026 10:04:19 +0900 Subject: ksmbd: fix use-after-free in __close_file_table_ids() A ksmbd_file can remain alive after logical close while another session holds a temporary reference obtained through ksmbd_lookup_fd_inode(). ksmbd_close_fd() currently marks the file closed and drops the idr-owned reference, but leaves the pointer published in the closing session's idr until the final reference is dropped. If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() supplies the foreign session's file table to __ksmbd_close_fd(). The object is then freed without being removed from its owner's idr, and the owner session later dereferences the stale pointer during file-table teardown. Remove the volatile id from the owner's idr while ksmbd_close_fd() still holds that table's lock, and clear volatile_id before dropping the idr-owned reference. A later foreign final put then only performs physical destruction and cannot remove the object from the wrong table. Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp") Reported-by: Yunseong Kim Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index d95c405eab11..a141025581af 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -697,6 +697,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp = NULL; else { fp->f_state = FP_CLOSED; + idr_remove(ft->idr, id); + fp->volatile_id = KSMBD_NO_FID; closed = true; if (!atomic_dec_and_test(&fp->refcount)) fp = NULL; -- cgit v1.2.3 From cb469993b3a61a72653770856d37af616d72d05f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 23 Jul 2026 23:07:14 +0900 Subject: ksmbd: reject repeated SMB2 NEGOTIATE requests Unauthenticated client can send multiple successful SMB2 NEGOTIATE requests on one connection before SESSION_SETUP. While the connection is in KSMBD_SESS_NEED_SETUP, smb2_handle_negotiate() accepts another SMB3.1.1 NEGOTIATE and overwrites conn->preauth_info with a new allocation. Only the final allocation is freed when the connection is released, leaking one object for every additional successful request. A repeated SMB2 NEGOTIATE after a dialect has been selected is a protocol violation. MS-SMB2 section 3.3.5.4 requires the server to disconnect without replying in this case. Set the connection exiting when rejecting the request, in addition to suppressing the response. Reject SMB2 NEGOTIATE unless the connection is new or is waiting for the SMB2 NEGOTIATE that follows an SMB1 multi-protocol negotiate. Serialize both SMB1 and SMB2 negotiation paths under conn->srv_mutex, since they update connection-wide dialect and negotiation state. Move the locking contract to ksmbd_smb_negotiate_common(), where the state and dialect are selected, and add ksmbd_conn_new() for consistent state access. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Reported-by: Runa Takemoto Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/connection.h | 5 +++++ fs/smb/server/smb2pdu.c | 10 ++-------- fs/smb/server/smb_common.c | 37 ++++++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index ec75633b7da0..2a194ee36fb4 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -200,6 +200,11 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn); * This is a hack. We will move status to a proper place once we land * a multi-sessions support. */ +static inline bool ksmbd_conn_new(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEW; +} + static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) { return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c1ba5e01aa7f..404a4203f7da 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1325,6 +1325,8 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, * smb2_handle_negotiate() - handler for smb2 negotiate command * @work: smb work containing smb request buffer * + * The caller holds conn->srv_mutex. + * * Return: 0 */ int smb2_handle_negotiate(struct ksmbd_work *work) @@ -1338,13 +1340,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ksmbd_debug(SMB, "Received negotiate request\n"); conn->need_neg = false; - if (ksmbd_conn_good(conn)) { - pr_err("conn->tcp_status is already in CifsGood State\n"); - work->send_no_response = 1; - return rc; - } - - ksmbd_conn_lock(conn); smb2_buf_len = get_rfc1002_len(work->request_buf); smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); if (smb2_neg_size > smb2_buf_len) { @@ -1495,7 +1490,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ksmbd_conn_set_need_setup(conn); err_out: - ksmbd_conn_unlock(conn); if (rc) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index 7de73223189a..080fbc9eb470 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -608,23 +608,46 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) struct ksmbd_conn *conn = work->conn; int ret; - conn->dialect = - ksmbd_negotiate_smb_dialect(work->request_buf); - ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); - if (command == SMB2_NEGOTIATE_HE) { + /* + * An SMB2 NEGOTIATE is valid for a new connection, or after an + * SMB1 multi-protocol negotiate has selected SMB2. Do not allow + * a second SMB2 NEGOTIATE to replace connection-wide state + * while a session setup is pending. KSMBD_SESS_NEED_RECONNECT + * is a transient session state and does not restart transport + * negotiation. + */ + ksmbd_conn_lock(conn); + if (!ksmbd_conn_new(conn) && + !ksmbd_conn_need_negotiate(conn)) { + work->send_no_response = 1; + ksmbd_conn_set_exiting(conn); + ksmbd_conn_unlock(conn); + return 0; + } + + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); ret = smb2_handle_negotiate(work); + ksmbd_conn_unlock(conn); return ret; } if (command == SMB_COM_NEGOTIATE) { + ksmbd_conn_lock(conn); + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); if (__smb2_negotiate(conn)) { init_smb3_11_server(conn); - init_smb2_neg_rsp(work); + ret = init_smb2_neg_rsp(work); ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); - return 0; + } else { + ret = smb_handle_negotiate(work); } - return smb_handle_negotiate(work); + ksmbd_conn_unlock(conn); + return ret; } pr_err("Unknown SMB negotiation command: %u\n", command); -- cgit v1.2.3 From e8bb506e6ef749ac0336f3e579d8d02396b7d832 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 20 Jul 2026 10:32:01 +0900 Subject: ksmbd: use memcmp() to compare ClientGUIDs ClientGUID is a fixed-size binary value and can contain embedded NUL bytes. strncmp() stops comparing at the first NUL byte, so different ClientGUID values can incorrectly be treated as equal. Use memcmp() in SMB3 multichannel session binding and FSCTL_VALIDATE_NEGOTIATE_INFO to compare all SMB2_CLIENT_GUID_SIZE bytes. Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel") Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Reported-by: Samu Suggested-by: Samu Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 404a4203f7da..76f63f9adc72 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1969,7 +1969,7 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; } - if (strncmp(conn->ClientGUID, sess->ClientGUID, + if (memcmp(conn->ClientGUID, sess->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { rc = -ENOENT; goto out_err; @@ -8690,7 +8690,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, goto err_out; } - if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { + if (memcmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { ret = -EINVAL; goto err_out; } -- cgit v1.2.3 From 2195424c3da2ef1829a63b807e3a900a90e57d85 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Mon, 27 Jul 2026 07:03:42 +0900 Subject: net/x25: fix use-after-free of the socket by its timers The x25 timers are armed with mod_timer() and cancelled with timer_delete(), so a pending timer holds no reference on the socket and a cancel does not wait for a callback already running on another CPU. x25_heartbeat_expiry() also rearms unconditionally, so it can reinstall sk->sk_timer after __x25_destroy_socket() has passed its cancel point. The following __sock_put() frees the socket while the timer is still queued, and the next expiry uses freed memory. KASAN reports a slab-use-after-free on the kmalloc-2k object freed by close(). timer_delete_sync() cannot be used here: x25_heartbeat_expiry() and x25_timer_expiry() both reach the cancels from inside the timer they would wait on, through __x25_destroy_socket() and x25_disconnect(). Arm the timers with sk_reset_timer() and cancel them with sk_stop_timer() so that an armed timer owns a reference, and release it in both expiry handlers. Rearm the heartbeat only while sk_hashed(sk) is still true, since __x25_destroy_socket() unlinks the socket before dropping it. Arm the deferred destroy timer the same way and drop its reference in x25_destroy_timer(). Reproduced on net with KASAN, with the heartbeat period shortened so the window recurs. With this patch the reproducer no longer triggers a report and /proc/net/x25 drains. Discovered by XBOW, triaged by Baul Lee Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726220342.47245-1-baul.lee@xbow.com Signed-off-by: Jakub Kicinski --- net/x25/af_x25.c | 4 ++-- net/x25/x25_timer.c | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 8aae9273b7c1..033e7d059f58 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -363,6 +363,7 @@ static void x25_destroy_timer(struct timer_list *t) struct sock *sk = timer_container_of(sk, t, sk_timer); x25_destroy_socket_from_timer(sk); + sock_put(sk); } /* @@ -398,9 +399,8 @@ static void __x25_destroy_socket(struct sock *sk) if (sk_has_allocations(sk)) { /* Defer: outstanding buffers */ - sk->sk_timer.expires = jiffies + 10 * HZ; sk->sk_timer.function = x25_destroy_timer; - add_timer(&sk->sk_timer); + sk_reset_timer(sk, &sk->sk_timer, jiffies + 10 * HZ); } else { /* drop last reference so sock_put will free */ __sock_put(sk); diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c index 2ec63a1f4c6d..7896cd43f1cc 100644 --- a/net/x25/x25_timer.c +++ b/net/x25/x25_timer.c @@ -36,45 +36,45 @@ void x25_init_timers(struct sock *sk) void x25_start_heartbeat(struct sock *sk) { - mod_timer(&sk->sk_timer, jiffies + 5 * HZ); + sk_reset_timer(sk, &sk->sk_timer, jiffies + 5 * HZ); } void x25_stop_heartbeat(struct sock *sk) { - timer_delete(&sk->sk_timer); + sk_stop_timer(sk, &sk->sk_timer); } void x25_start_t2timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t2); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t2); } void x25_start_t21timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t21); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t21); } void x25_start_t22timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t22); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t22); } void x25_start_t23timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t23); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t23); } void x25_stop_timer(struct sock *sk) { - timer_delete(&x25_sk(sk)->timer); + sk_stop_timer(sk, &x25_sk(sk)->timer); } unsigned long x25_display_timer(struct sock *sk) @@ -108,7 +108,7 @@ static void x25_heartbeat_expiry(struct timer_list *t) sock_flag(sk, SOCK_DEAD))) { bh_unlock_sock(sk); x25_destroy_socket_from_timer(sk); - return; + goto out; } break; @@ -120,8 +120,14 @@ static void x25_heartbeat_expiry(struct timer_list *t) break; } restart_heartbeat: - x25_start_heartbeat(sk); + /* Do not rearm once __x25_destroy_socket() has unlinked the socket: + * it is past its cancel point and owns the teardown from there on. + */ + if (sk_hashed(sk)) + x25_start_heartbeat(sk); bh_unlock_sock(sk); +out: + sock_put(sk); } /* @@ -166,4 +172,5 @@ static void x25_timer_expiry(struct timer_list *t) } else x25_do_timer_expiry(sk); bh_unlock_sock(sk); + sock_put(sk); } -- cgit v1.2.3 From cc679d7a6303e84d769f2afcde1fc51c51f127cd Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 29 Jul 2026 07:44:40 -0700 Subject: uprobes: Fix NULL pointer dereference in hprobe_expire() Forking a task that has a pending uretprobe can oops the kernel with a NULL pointer dereference in the clone() path: BUG: kernel NULL pointer dereference, address: 0000000000000018 Oops: 0002 [#1] SMP NOPTI RIP: 0010:hprobe_expire CR2: 0000000000000018 Call Trace: uprobe_copy_process copy_process kernel_clone __x64_sys_clone do_syscall_64 entry_SYSCALL_64_after_hwframe This was found on real hosts on Meta fleet. I've got the impression that this is what is happening: CPU 1 CPU 2 (traced task) ----- ------------------- hit uprobe, prepare_uretprobe(): hprobe LEASED, refcount >= 1 uprobe_unregister() put_uprobe(): refcount -> 0 fork() -> dup_utask() hprobe_expire(hprobe, true) try_get_uprobe() -> NULL get_uprobe(NULL) <-- Oops Only take the extra reference when the uprobe is non-NULL; a NULL means it is gone and is the correct value to return. Fixes: dd1a7567784e ("uprobes: SRCU-protect uretprobe lifetime (with timeout)") Signed-off-by: Breno Leitao Signed-off-by: Peter Zijlstra (Intel) Acked-by: Oleg Nesterov Acked-by: Andrii Nakryiko Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260729-uprobe-v1-1-61896b87c867@debian.org --- kernel/events/uprobes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 4084e926e284..6300b216012c 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -830,7 +830,7 @@ static struct uprobe *hprobe_expire(struct hprobe *hprobe, bool get) if (try_cmpxchg(&hprobe->state, &hstate, uprobe ? HPROBE_STABLE : HPROBE_GONE)) { /* We won the race, we are the ones to unlock SRCU */ __srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx); - return get ? get_uprobe(uprobe) : uprobe; + return get && uprobe ? get_uprobe(uprobe) : uprobe; } /* -- cgit v1.2.3 From d761c7e38a000603a9d16270a1af770a0e8efb5e Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Wed, 29 Jul 2026 11:47:20 +0200 Subject: gpiolib: Check gc->get_direction() before calling gpiod_get_direction() According to 'struct gpio_chip' documentation in linux/gpio/driver.h, implementing .get_direction() is recommended but not mandatory. Most places verify that gc->get_direction() exists before calling gpiod_get_direction(), but gpiolib_dbg_show() doesn't. Until commit 471e998c0e31 ("gpiolib: remove redundant callback check") it was also verified by gpiod_get_direction() itself so calling it at all time from gpiolib_dbg_show() was not an issue. But after the check in gpiod_get_direction() has been removed, calling it inconditionaly leads to a big fat warning in gpiochip_get_direction(). In gpiod_get_direction(), verify that gc->get_direction() exists before calling gpiod_get_direction(). Fixes: 471e998c0e31 ("gpiolib: remove redundant callback check") Signed-off-by: Christophe Leroy (CS GROUP) Link: https://patch.msgid.link/ad89f92f91d004e63dd5599bb58e9581f373a601.1785318183.git.chleroy@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c433a095907f..ef8ccaf17c9c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5420,7 +5420,8 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *gc) flags = READ_ONCE(desc->flags); is_irq = test_bit(GPIOD_FLAG_USED_AS_IRQ, &flags); if (is_irq || test_bit(GPIOD_FLAG_REQUESTED, &flags)) { - gpiod_get_direction(desc); + if (gc->get_direction) + gpiod_get_direction(desc); is_out = test_bit(GPIOD_FLAG_IS_OUT, &flags); value = gpio_chip_get_value(gc, desc); active_low = test_bit(GPIOD_FLAG_ACTIVE_LOW, &flags); -- cgit v1.2.3 From ab99eaafb0c4b412cfeb895a8cf091626e2bbd86 Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Fri, 24 Jul 2026 23:33:37 +0100 Subject: misc: fastrpc: Fix initial memory allocation for Audio PD memory pool The initial buffer allocated for the Audio PD memory pool is never added to the pool because pageslen is set to 0. As a result, the buffer is not registered with Audio PD and is never used, causing a memory leak. Audio PD immediately falls back to allocating memory from the remote heap since the pool starts out empty. Fix this by setting pageslen to 1 so that the initially allocated buffer is correctly registered and becomes part of the Audio PD memory pool. Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd") Cc: stable@kernel.org Reviewed-by: Dmitry Baryshkov Signed-off-by: Ekansh Gupta Signed-off-by: Jianping Li Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260724223342.629168-2-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index f3a49384586d..4f01ebfa6f95 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1370,7 +1370,9 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, err = PTR_ERR(name); goto err; } - + inbuf.client_id = fl->client_id; + inbuf.namelen = init.namelen; + inbuf.pageslen = 0; if (!fl->cctx->remote_heap) { err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen, &fl->cctx->remote_heap); @@ -1393,12 +1395,10 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, goto err_map; } scm_done = true; + inbuf.pageslen = 1; } } - inbuf.client_id = fl->client_id; - inbuf.namelen = init.namelen; - inbuf.pageslen = 0; fl->pd = USER_PD; args[0].ptr = (u64)(uintptr_t)&inbuf; -- cgit v1.2.3 From 6102ceb4eab845743ee57acd3863fbd06e93c927 Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Fri, 24 Jul 2026 23:33:38 +0100 Subject: misc: fastrpc: Remove buffer from list prior to unmap operation fastrpc_req_munmap_impl() is called to unmap any buffer. The buffer is getting removed from the list after it is unmapped from DSP. This can create potential race conditions if multiple threads invoke unmap concurrently, where one thread may remove the entry from the list while another thread's unmap operation is still ongoing. Fix this by removing the buffer entry from the list before calling the unmap operation. If the unmap fails, the entry is re-added to the list so that userspace can retry the unmap, or alternatively, the buffer will be cleaned up during device release when the DSP process is torn down and all DSP-side mappings are freed along with remaining buffers in the list. Fixes: 2419e55e532de ("misc: fastrpc: add mmap/unmap support") Cc: stable@kernel.org Reviewed-by: Dmitry Baryshkov Signed-off-by: Ekansh Gupta Signed-off-by: Jianping Li Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260724223342.629168-3-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 4f01ebfa6f95..12dcd2e737c3 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1889,9 +1889,6 @@ static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, struct fastrpc_buf * &args[0]); if (!err) { dev_dbg(dev, "unmmap\tpt 0x%09lx OK\n", buf->raddr); - spin_lock(&fl->lock); - list_del(&buf->node); - spin_unlock(&fl->lock); fastrpc_buf_free(buf); } else { dev_err(dev, "unmmap\tpt 0x%09lx ERROR\n", buf->raddr); @@ -1905,6 +1902,7 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp) struct fastrpc_buf *buf = NULL, *iter, *b; struct fastrpc_req_munmap req; struct device *dev = fl->sctx->dev; + int err; if (copy_from_user(&req, argp, sizeof(req))) return -EFAULT; @@ -1912,6 +1910,7 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp) spin_lock(&fl->lock); list_for_each_entry_safe(iter, b, &fl->mmaps, node) { if ((iter->raddr == req.vaddrout) && (iter->size == req.size)) { + list_del(&iter->node); buf = iter; break; } @@ -1924,7 +1923,14 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp) return -EINVAL; } - return fastrpc_req_munmap_impl(fl, buf); + err = fastrpc_req_munmap_impl(fl, buf); + if (err) { + spin_lock(&fl->lock); + list_add_tail(&buf->node, &fl->mmaps); + spin_unlock(&fl->lock); + } + + return err; } static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp) -- cgit v1.2.3 From b85a0e91d7d6cd06a53c881a46f749cfcef416a2 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Fri, 24 Jul 2026 23:33:39 +0100 Subject: misc: fastrpc: take fl->lock when moving mmaps on interrupted invoke When an invoke is interrupted by a signal, wait_for_completion_interruptible() returns -ERESTARTSYS and fastrpc_internal_invoke() moves every buffer from fl->mmaps onto cctx->invoke_interrupted_mmaps. This list_del()/list_add_tail() walk runs without holding fl->lock, the lock that serialises fl->mmaps in fastrpc_req_mmap() and fastrpc_req_munmap() everywhere else. Take fl->lock around the move, matching every other fl->mmaps accessor. Fixes: 76e8e4ace1ed ("misc: fastrpc: Safekeep mmaps on interrupted invoke") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Reviewed-by: Dmitry Baryshkov Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260724223342.629168-4-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 12dcd2e737c3..c75eafe872cc 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1305,10 +1305,12 @@ bail: } if (err == -ERESTARTSYS) { + spin_lock(&fl->lock); list_for_each_entry_safe(buf, b, &fl->mmaps, node) { list_del(&buf->node); list_add_tail(&buf->node, &fl->cctx->invoke_interrupted_mmaps); } + spin_unlock(&fl->lock); } if (err) -- cgit v1.2.3 From 310f7868399668c6d99d88acc9c4cf3462e69d5b Mon Sep 17 00:00:00 2001 From: Anandu Krishnan E Date: Fri, 24 Jul 2026 23:33:40 +0100 Subject: misc: fastrpc: fix channel ctx ref leak when session alloc fails fastrpc_channel_ctx_get() is called in fastrpc_device_open() before fastrpc_session_alloc(). If session alloc fails, the error path returns -EBUSY without calling fastrpc_channel_ctx_put(), leaking the reference. Fix by adding the missing put. Fixes: 278d56f970ae ("misc: fastrpc: Reference count channel context") Cc: stable@kernel.org Signed-off-by: Anandu Krishnan E Reviewed-by: Dmitry Baryshkov Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260724223342.629168-5-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index c75eafe872cc..f8cbe30c5d5c 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1675,7 +1675,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) dev_err(&cctx->rpdev->dev, "No session available\n"); mutex_destroy(&fl->mutex); kfree(fl); - + fastrpc_channel_ctx_put(cctx); return -EBUSY; } -- cgit v1.2.3 From 2fae94ee14f7fea11d3f95e10383a87c01d21518 Mon Sep 17 00:00:00 2001 From: Eddie Lin Date: Fri, 24 Jul 2026 23:33:41 +0100 Subject: misc: fastrpc: fix memory leak in fastrpc_channel_ctx_free The 'ctx_idr' is initialized but never destroyed when the channel context is freed, leading to a memory leak. Add idr_destroy() to properly clean up the IDR resources. Fixes: f6f9279f2bf0 ("misc: fastrpc: Add Qualcomm fastrpc basic driver model") Cc: stable@vger.kernel.org Signed-off-by: Eddie Lin Reviewed-by: Ekansh Gupta Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260724223342.629168-6-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index f8cbe30c5d5c..eb6c2a78d3c7 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -492,6 +492,7 @@ static void fastrpc_channel_ctx_free(struct kref *ref) cctx = container_of(ref, struct fastrpc_channel_ctx, refcount); + idr_destroy(&cctx->ctx_idr); kfree(cctx); } -- cgit v1.2.3 From ff10b6db0ab75b132aed69ab144ac04f63ef9bdb Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Fri, 24 Jul 2026 23:34:03 +0100 Subject: nvmem: apple-spmi-nvmem: wrap regmap calls to satisfy CFI The Apple SPMI NVMEM driver previously cast regmap_bulk_read/write to void * when assigning them to nvmem_config's reg_read/reg_write function pointers. This cast breaks the expected function signature of nvmem_reg_read_t and nvmem_reg_write_t. With CFI enabled, indirect calls through these pointers fail: CFI failure at nvmem_reg_write+0x194/0x1e4 (target: regmap_bulk_write+0x0/0x2c8; expected type: 0x83a189c3) ... Call trace: nvmem_reg_write+0x194/0x1e4 (P) __nvmem_cell_entry_write+0x298/0x2e8 nvmem_cell_write+0x24/0x34 macsmc_reboot_probe+0x1dc/0x454 [macsmc_reboot] ... Introduce thin wrapper functions with the correct nvmem function pointer types to satisfy the CFI checks. Fixes: fe91c24a551c ("nvmem: Add apple-spmi-nvmem driver") Signed-off-by: Aelin Reidel Reported-by: Clayton Craft Tested-by: Clayton Craft Reviewed-by: Sven Peter Cc: stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260724223404.629248-2-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/apple-spmi-nvmem.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/apple-spmi-nvmem.c b/drivers/nvmem/apple-spmi-nvmem.c index 88614005d5ce..7acb0c07d6ab 100644 --- a/drivers/nvmem/apple-spmi-nvmem.c +++ b/drivers/nvmem/apple-spmi-nvmem.c @@ -18,6 +18,22 @@ static const struct regmap_config apple_spmi_regmap_config = { .max_register = 0xffff, }; +static int apple_spmi_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *map = priv; + + return regmap_bulk_read(map, offset, val, bytes); +} + +static int apple_spmi_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *map = priv; + + return regmap_bulk_write(map, offset, val, bytes); +} + static int apple_spmi_nvmem_probe(struct spmi_device *sdev) { struct regmap *regmap; @@ -28,8 +44,8 @@ static int apple_spmi_nvmem_probe(struct spmi_device *sdev) .word_size = 1, .stride = 1, .size = 0xffff, - .reg_read = (void *)regmap_bulk_read, - .reg_write = (void *)regmap_bulk_write, + .reg_read = apple_spmi_nvmem_read, + .reg_write = apple_spmi_nvmem_write, }; regmap = devm_regmap_init_spmi_ext(sdev, &apple_spmi_regmap_config); -- cgit v1.2.3 From b5be879519291f139fa7b365fd0dbc84710e4919 Mon Sep 17 00:00:00 2001 From: Mathieu Dubois-Briand Date: Fri, 24 Jul 2026 23:34:04 +0100 Subject: nvmem: layouts: Add fixed-layout driver Current implementation isn't working well when device tree nodes have a phandle on a fixed-layout nvmem node. As the fixed layout is handled in nvmem core, no driver is ever associated with the layout, and the device consumer driver probe is deferred indefinitely. Remove the specific handling of fixed-layout and add a layout driver. This makes the fixed-layout similar to all other layouts, fixing the whole issue. Fixes: fc29fd821d9a ("nvmem: core: Rework layouts to become regular devices") Cc: stable@vger.kernel.org Reviewed-by: Miquel Raynal Signed-off-by: Mathieu Dubois-Briand Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260724223404.629248-3-srini@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 5 ++++ drivers/nvmem/core.c | 24 ++------------- drivers/nvmem/internals.h | 2 ++ drivers/nvmem/layouts.c | 11 ------- drivers/nvmem/layouts/Makefile | 1 + drivers/nvmem/layouts/fixed-layout.c | 58 ++++++++++++++++++++++++++++++++++++ include/linux/nvmem-provider.h | 6 ++++ 7 files changed, 74 insertions(+), 33 deletions(-) create mode 100644 drivers/nvmem/layouts/fixed-layout.c diff --git a/MAINTAINERS b/MAINTAINERS index 716acfc3d7c1..1b044ddc68f9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10134,6 +10134,11 @@ F: drivers/base/firmware_loader/ F: rust/kernel/firmware.rs F: include/linux/firmware.h +FIXED-LAYOUT NVMEM LAYOUT DRIVER +M: Mathieu Dubois-Briand +S: Maintained +F: drivers/nvmem/layouts/fixed-layout.c + FLEXTIMER FTM-QUADDEC DRIVER M: Patrick Havelange L: linux-iio@vger.kernel.org diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index e871181751f3..2f971b059704 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -786,7 +786,7 @@ static int nvmem_validate_keepouts(struct nvmem_device *nvmem) return 0; } -static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) +int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) { struct device *dev = &nvmem->dev; const __be32 *addr; @@ -834,29 +834,13 @@ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_nod return 0; } +EXPORT_SYMBOL_GPL(nvmem_add_cells_from_dt); static int nvmem_add_cells_from_legacy_of(struct nvmem_device *nvmem) { return nvmem_add_cells_from_dt(nvmem, nvmem->dev.of_node); } -static int nvmem_add_cells_from_fixed_layout(struct nvmem_device *nvmem) -{ - struct device_node *layout_np; - int err = 0; - - layout_np = of_nvmem_layout_get_container(nvmem); - if (!layout_np) - return 0; - - if (of_device_is_compatible(layout_np, "fixed-layout")) - err = nvmem_add_cells_from_dt(nvmem, layout_np); - - of_node_put(layout_np); - - return err; -} - int nvmem_layout_register(struct nvmem_layout *layout) { int ret; @@ -1005,10 +989,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_remove_cells; } - rval = nvmem_add_cells_from_fixed_layout(nvmem); - if (rval) - goto err_remove_cells; - dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); rval = device_add(&nvmem->dev); diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h index 18fed57270e5..f6d452b6a28f 100644 --- a/drivers/nvmem/internals.h +++ b/drivers/nvmem/internals.h @@ -35,6 +35,8 @@ struct nvmem_device { bool sysfs_cells_populated; }; +int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np); + #if IS_ENABLED(CONFIG_OF) int nvmem_layout_bus_register(void); void nvmem_layout_bus_unregister(void); diff --git a/drivers/nvmem/layouts.c b/drivers/nvmem/layouts.c index b90584e1b99e..07a34be9669c 100644 --- a/drivers/nvmem/layouts.c +++ b/drivers/nvmem/layouts.c @@ -125,11 +125,6 @@ static int nvmem_layout_create_device(struct nvmem_device *nvmem, return 0; } -static const struct of_device_id of_nvmem_layout_skip_table[] = { - { .compatible = "fixed-layout", }, - {} -}; - static int nvmem_layout_bus_populate(struct nvmem_device *nvmem, struct device_node *layout_dn) { @@ -142,12 +137,6 @@ static int nvmem_layout_bus_populate(struct nvmem_device *nvmem, return 0; } - /* Fixed layouts are parsed manually somewhere else for now */ - if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) { - pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn); - return 0; - } - if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) { pr_debug("%s() - skipping %pOF, already populated\n", __func__, layout_dn); diff --git a/drivers/nvmem/layouts/Makefile b/drivers/nvmem/layouts/Makefile index 4940c9db0665..dd6c6c70b1a9 100644 --- a/drivers/nvmem/layouts/Makefile +++ b/drivers/nvmem/layouts/Makefile @@ -3,6 +3,7 @@ # Makefile for nvmem layouts. # +obj-$(CONFIG_NVMEM_LAYOUTS) += fixed-layout.o obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o diff --git a/drivers/nvmem/layouts/fixed-layout.c b/drivers/nvmem/layouts/fixed-layout.c new file mode 100644 index 000000000000..635d448b3dd2 --- /dev/null +++ b/drivers/nvmem/layouts/fixed-layout.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2026 Bootlin + * + * Authors: Mathieu Dubois-Briand + */ + +#include +#include + +#include "../internals.h" + +static int fixed_layout_add_cells(struct nvmem_layout *layout) +{ + struct device_node *np; + int ret; + + np = of_nvmem_layout_get_container(layout->nvmem); + if (!np) + return -ENOENT; + + ret = nvmem_add_cells_from_dt(layout->nvmem, np); + of_node_put(np); + + return ret; +} + +static int fixed_layout_probe(struct nvmem_layout *layout) +{ + layout->add_cells = fixed_layout_add_cells; + + return nvmem_layout_register(layout); +} + +static void fixed_layout_remove(struct nvmem_layout *layout) +{ + nvmem_layout_unregister(layout); +} + +static const struct of_device_id fixed_layout_of_match_table[] = { + { .compatible = "fixed-layout", }, + {}, +}; + +static struct nvmem_layout_driver fixed_layout_layout = { + .driver = { + .name = "fixed-layout", + .of_match_table = fixed_layout_of_match_table, + }, + .probe = fixed_layout_probe, + .remove = fixed_layout_remove, +}; +module_nvmem_layout_driver(fixed_layout_layout); + +MODULE_AUTHOR("Mathieu Dubois-Briand"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, fixed_layout_of_match_table); +MODULE_DESCRIPTION("NVMEM fixed-layout driver"); diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index f3b13da78aac..6063fe5b7784 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -214,6 +214,12 @@ static inline int nvmem_layout_register(struct nvmem_layout *layout) static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {} +static inline int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, + struct device_node *np) +{ + return -EOPNOTSUPP; +} + #endif /* CONFIG_NVMEM */ #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) -- cgit v1.2.3 From dd7aea9ee2091cfae3a5e376af87aa106d7735cd Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 27 Jul 2026 12:28:59 +0000 Subject: rust_binder: do not query current thread for all ioctls The get_current_thread() method is currently called for every ioctl to ensure that a Thread struct exists for the thread calling into the driver. However, not all ioctls require a Thread object, so this means we are unnecessarily creating these objects in cases where we don't need to. If said thread does not invoke BINDER_THREAD_EXIT on exit, Binder's Thread struct stays around until the fd is closed. For long-lived processes the Thread object is effectively leaked. Furthermore, when the BINDER_GET_NODE_DEBUG_INFO ioctl is invoked by libmemunreachable to ensure that objects reachable only through the Binder driver are not considered leaked, this is done from a fork of the process owning the fd, which means that it fails the group_leader check inside get_current_thread(). This results in EINVAL errors for this ioctl, causing libmemunreachable to report a false positive memory leak. Thus, do not invoke get_current_thread() for ioctls that do not require it. Signed-off-by: Alice Ryhl Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260727-binder-cur-thread-v1-1-8edf2b64e235@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index cdd1a9079726..5b8f73ec1931 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1586,6 +1586,10 @@ impl Process { cmd: u32, reader: &mut UserSliceReader, ) -> Result { + if cmd == uapi::BINDER_FREEZE { + return ioctl_freeze(reader); + } + let thread = this.get_current_thread()?; match cmd { uapi::BINDER_SET_MAX_THREADS => this.set_max_threads(reader.read()?), @@ -1597,7 +1601,6 @@ impl Process { uapi::BINDER_ENABLE_ONEWAY_SPAM_DETECTION => { this.set_oneway_spam_detection_enabled(reader.read()?) } - uapi::BINDER_FREEZE => ioctl_freeze(reader)?, _ => return Err(EINVAL), } Ok(()) @@ -1612,15 +1615,16 @@ impl Process { cmd: u32, data: UserSlice, ) -> Result { - let thread = this.get_current_thread()?; let blocking = (file.flags() & file::flags::O_NONBLOCK) == 0; match cmd { - uapi::BINDER_WRITE_READ => thread.write_read(data, blocking)?, + uapi::BINDER_WRITE_READ => this.get_current_thread()?.write_read(data, blocking)?, uapi::BINDER_GET_NODE_DEBUG_INFO => this.get_node_debug_info(data)?, uapi::BINDER_GET_NODE_INFO_FOR_REF => this.get_node_info_from_ref(data)?, uapi::BINDER_VERSION => this.version(data)?, uapi::BINDER_GET_FROZEN_INFO => get_frozen_status(data)?, - uapi::BINDER_GET_EXTENDED_ERROR => thread.get_extended_error(data)?, + uapi::BINDER_GET_EXTENDED_ERROR => { + this.get_current_thread()?.get_extended_error(data)? + } _ => return Err(EINVAL), } Ok(()) -- cgit v1.2.3 From c679ce3be6cb63763d68ab9b5d9d73ddc0a40762 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 29 Jun 2026 14:52:29 +0200 Subject: iomap: add a separate bio_set for iomap_split_ioend iomap_split_ioend can split bios that already come from iomap_ioend_bioset and thus deadlock when the bioset is exhausted. Add a separate bio_set to avoid this deadlock. Christian Brauner says: Mark iomap_ioend_split_bioset static as it is only used in ioend.c, fixing the sparse warning reported by the kernel test robot. Fixes: 5fcbd555d483 ("iomap: split bios to zone append limits in the submission handlers") Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260629125229.3400726-1-hch@lst.de Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/ioend.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 30468d51b5ad..fb636dce43af 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -13,6 +13,7 @@ struct bio_set iomap_ioend_bioset; EXPORT_SYMBOL_GPL(iomap_ioend_bioset); +static struct bio_set iomap_ioend_split_bioset; struct iomap_ioend *iomap_init_ioend(struct inode *inode, struct bio *bio, loff_t file_offset, u16 ioend_flags) @@ -488,7 +489,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT, i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT; - split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset); + split = bio_split(bio, sector_offset, GFP_NOFS, + &iomap_ioend_split_bioset); if (IS_ERR(split)) return ERR_CAST(split); split->bi_private = bio->bi_private; @@ -511,8 +513,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend); static int __init iomap_ioend_init(void) { - return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE), + const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE); + int error; + + error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries, offsetof(struct iomap_ioend, io_bio), BIOSET_NEED_BVECS); + if (error) + return error; + error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries, + offsetof(struct iomap_ioend, io_bio), + BIOSET_NEED_BVECS); + if (error) + goto out_exit_ioend_bioset; + return 0; + +out_exit_ioend_bioset: + bioset_exit(&iomap_ioend_bioset); + return error; } fs_initcall(iomap_ioend_init); -- cgit v1.2.3 From b0495bb58af06a7de4628c72d500e3d5e180d808 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 19 Jul 2026 12:57:55 +0300 Subject: mei: pull kvfree out of spinlock The read buffer allocation was changed from kmalloc() to kvmalloc(). This buffer is part of mei_cl_cb structure that can be queued in rd_complete queue protected by spinlock. Releasing the structure leads to errors like below when freeing buffer that allocated non-contiguous: BUG: sleeping function called from invalid context at mm/vmalloc.c:3448 Separate mei_cl_cb structure dequeue and release to perform only dequeue under spinlock and push release out of spinlock. Cc: stable Fixes: 4adf613e01bf ("mei: use kvmalloc for read buffer") Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/16359 Reviewed-by: Menachem Adin Signed-off-by: Alexander Usyskin Link: https://patch.msgid.link/20260719-kvfree_out_of_spinlock-v1-1-e07d6333bea7@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 643b0039cc72..26d2b2742d50 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -425,18 +425,24 @@ static void mei_io_tx_list_free_cl(struct list_head *head, } /** - * mei_io_list_free_fp - free cb from a list that matches file pointer + * mei_io_rd_list_free_fp - free cb from a rd_completed list that matches file pointer * - * @head: io list + * @cl: host client * @fp: file pointer (matching cb file object), may be NULL */ -static void mei_io_list_free_fp(struct list_head *head, const struct file *fp) +static void mei_io_rd_list_free_fp(struct mei_cl *cl, const struct file *fp) { struct mei_cl_cb *cb, *next; + LIST_HEAD(cmpl_list); - list_for_each_entry_safe(cb, next, head, list) + spin_lock(&cl->rd_completed_lock); + list_for_each_entry_safe(cb, next, &cl->rd_completed, list) if (!fp || fp == cb->fp) - mei_io_cb_free(cb); + list_move(&cb->list, &cmpl_list); + spin_unlock(&cl->rd_completed_lock); + + list_for_each_entry_safe(cb, next, &cmpl_list, list) + mei_io_cb_free(cb); } /** @@ -565,9 +571,7 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl); mei_cl_free_pending(cl); } - spin_lock(&cl->rd_completed_lock); - mei_io_list_free_fp(&cl->rd_completed, fp); - spin_unlock(&cl->rd_completed_lock); + mei_io_rd_list_free_fp(cl, fp); return 0; } @@ -1401,7 +1405,7 @@ void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb) } /** - * mei_cl_del_rd_completed - free read completed callback with lock + * mei_cl_del_rd_completed - unlink read completed callback with lock and free it * * @cl: host client * @cb: callback block @@ -1410,8 +1414,9 @@ void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb) void mei_cl_del_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb) { spin_lock(&cl->rd_completed_lock); - mei_io_cb_free(cb); + list_del_init(&cb->list); spin_unlock(&cl->rd_completed_lock); + mei_io_cb_free(cb); } /** -- cgit v1.2.3 From b7ab86bdc65eadcfc43a0e3faf682a3f750cfb96 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 22 Jul 2026 15:08:34 +0200 Subject: s390/pci: Fix s390_pci_mmio_write syscall error return without MIO On a machine without PCI memory-I/O (MIO) support or when running with pci=nomio the s390 specific PCI MMIO write syscall checks if the MMIO cookie is above ZPCI_IOMAP_ADDR_BASE as a sanity check before even trying to perform the MMIO. If this check fails the return value was left unchanged and thus 0 from prior operations falsely indicating success. This could potentially confuse user-space into falsely believing the MMIO, on a mapping not valid for MMIO was successful. Fix this by setting the return value to -EFAULT prior to the check following the same pattern as elsewhere in the same function. Cc: stable@vger.kernel.org Reviewed-by: Julian Ruess Reviewed-by: Farhan Ali Fixes: a67a88b0b8de ("s390/pci: remove races against pte updates") Signed-off-by: Niklas Schnelle Signed-off-by: Vasily Gorbik --- arch/s390/pci/pci_mmio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/pci/pci_mmio.c b/arch/s390/pci/pci_mmio.c index 51e7a28af899..f3f79ba78410 100644 --- a/arch/s390/pci/pci_mmio.c +++ b/arch/s390/pci/pci_mmio.c @@ -188,6 +188,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr, goto out_unlock_mmap; } + ret = -EFAULT; io_addr = (void __iomem *)((args.pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); -- cgit v1.2.3 From 06afe425d5283b9764303de47f554da5a808ce8a Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 29 Jul 2026 11:36:15 +0200 Subject: s390/zcrypt: Validate length for CCA AES cipher key requests cca_cipher2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block. Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Signed-off-by: Holger Dengler Cc: stable@vger.kernel.org # 5.4+ Reviewed-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 84936a795b95..db6a4211a97e 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1261,6 +1261,9 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey, } __packed * prepparm; int keytoklen = ((struct cipherkeytoken *)ckey)->len; + if (keytoklen > PARMBSIZE - sizeof(struct aureqparm)) + return -EINVAL; + /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk, xflags); -- cgit v1.2.3 From a9ae0f6dd45c3ccc1d69363f7aea8af179122730 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 29 Jul 2026 11:36:16 +0200 Subject: s390/zcrypt: Validate length for CCA ECC private key requests cca_ecc2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block. Fixes: fa6999e326fe ("s390/pkey: support CCA and EP11 secure ECC private keys") Signed-off-by: Holger Dengler Cc: stable@vger.kernel.org # 5.10+ Reviewed-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index db6a4211a97e..322677a8d320 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1428,6 +1428,9 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key, } __packed * prepparm; int keylen = ((struct eccprivkeytoken *)key)->len; + if (keylen > PARMBSIZE - sizeof(struct aureqparm)) + return -EINVAL; + /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk, xflags); -- cgit v1.2.3 From 36b230835b8a008266aad22168ca52afacc8a58d Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Wed, 29 Jul 2026 13:40:09 +0200 Subject: s390/zcrypt: Fix buffer over-read in cca_cipher2protkey Add validation of both the actual key buffer size and token length fields in all the cca_check_sec*token() functions. Additionally check in cca_gencipherkey() for possible underflow with returned key size. The CCA token structures contain user-controlled len fields that were used in operations without proper validation against both the actual buffer size and minimum token structure size. An attacker could set this field larger than the actual buffer size, leading to reading beyond buffer boundaries. This may result in a kernel crash or exposure of memory via sending this as part of a request down to the crypto card. Also an attacker could have used a very small len value and thus enforce a buffer under-run which may produce similar effects as a over-read. So now a key must - key buf length must be at least sizeof the token struct - the key len field inside the token must fit into the range of sizeof key token struct ... key buf length Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Cc: stable@vger.kernel.org Reviewed-by: Ingo Franzki Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/pkey_cca.c | 15 +++------ drivers/s390/crypto/zcrypt_ccamisc.c | 64 ++++++++++++++++++++++++++++++------ drivers/s390/crypto/zcrypt_ccamisc.h | 6 ++-- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/drivers/s390/crypto/pkey_cca.c b/drivers/s390/crypto/pkey_cca.c index 4bc47952324e..e8dc2f77c137 100644 --- a/drivers/s390/crypto/pkey_cca.c +++ b/drivers/s390/crypto/pkey_cca.c @@ -236,22 +236,16 @@ static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns, if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_AES) { /* CCA AES data key */ - if (keylen < sizeof(struct secaeskeytoken)) - return -EINVAL; - if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0)) + if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0)) return -EINVAL; } else if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_VLSC) { /* CCA AES cipher key */ - if (keylen < hdr->len) - return -EINVAL; if (cca_check_secaescipherkey(pkey_dbf_info, - 3, key, 0, 1)) + 3, key, keylen, 0, 1)) return -EINVAL; } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { /* CCA ECC (private) key */ - if (keylen < sizeof(struct eccprivkeytoken)) - return -EINVAL; if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1)) return -EINVAL; } else { @@ -484,7 +478,7 @@ static int cca_verifykey(const u8 *key, u32 keylen, hdr->version == TOKVER_CCA_AES) { struct secaeskeytoken *t = (struct secaeskeytoken *)key; - rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0); + rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0); if (rc) goto out; *keytype = PKEY_TYPE_CCA_DATA; @@ -512,7 +506,8 @@ static int cca_verifykey(const u8 *key, u32 keylen, hdr->version == TOKVER_CCA_VLSC) { struct cipherkeytoken *t = (struct cipherkeytoken *)key; - rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1); + rc = cca_check_secaescipherkey(pkey_dbf_info, 3, + key, keylen, 0, 1); if (rc) goto out; *keytype = PKEY_TYPE_CCA_CIPHER; diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 322677a8d320..f797786107b9 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -62,12 +62,18 @@ static DEFINE_MUTEX(dev_status_mem_mutex); * also checked. Returns 0 on success or errno value on failure. */ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize) + const u8 *token, u32 keysize, int keybitsize) { struct secaeskeytoken *t = (struct secaeskeytoken *)token; #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -101,14 +107,20 @@ EXPORT_SYMBOL(cca_check_secaeskeytoken); * Returns 0 on success or errno value on failure. */ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize, - int checkcpacfexport) + const u8 *token, u32 keysize, + int keybitsize, int checkcpacfexport) { struct cipherkeytoken *t = (struct cipherkeytoken *)token; bool keybitsizeok = true; #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -121,6 +133,18 @@ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, __func__, (int)t->version, TOKVER_CCA_VLSC); return -EINVAL; } + if (t->len > keysize) { + if (dbg) + DBF("%s token check failed, len %d > keysize %u\n", + __func__, (int)t->len, keysize); + return -EINVAL; + } + if (t->len < sizeof(*t)) { + if (dbg) + DBF("%s token check failed, len %d < min token size %zu\n", + __func__, (int)t->len, sizeof(*t)); + return -EINVAL; + } if (t->algtype != 0x02) { if (dbg) DBF("%s token check failed, algtype 0x%02x != 0x02\n", @@ -195,6 +219,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl, #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL_PKA) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -207,6 +237,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl, __func__, (int)t->len, keysize); return -EINVAL; } + if (t->len < sizeof(*t)) { + if (dbg) + DBF("%s token check failed, len %d < min token size %zu\n", + __func__, (int)t->len, sizeof(*t)); + return -EINVAL; + } if (t->secid != 0x20) { if (dbg) DBF("%s token check failed, secid 0x%02x != 0x20\n", @@ -443,7 +479,8 @@ int cca_genseckey(u16 cardnr, u16 domain, /* check secure key token */ rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR, - prepparm->lv3.keyblock.tok, 8 * keysize); + prepparm->lv3.keyblock.tok, + seckeysize, 8 * keysize); if (rc) { rc = -EIO; goto out; @@ -582,7 +619,8 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize, /* check secure key token */ rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR, - prepparm->lv3.keyblock.tok, 8 * keysize); + prepparm->lv3.keyblock.tok, + seckeysize, 8 * keysize); if (rc) { rc = -EIO; goto out; @@ -842,6 +880,7 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags, } kb; } __packed * prepparm; struct cipherkeytoken *t; + u32 keybuflen; /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, @@ -936,23 +975,28 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags, } /* and some checks on the generated key */ + t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key; + if (prepparm->kb.tlv1.len < 2 * sizeof(uint16_t) + sizeof(*t)) { + rc = -EIO; + goto out; + } + keybuflen = prepparm->kb.tlv1.len - 2 * sizeof(uint16_t); rc = cca_check_secaescipherkey(zcrypt_dbf_info, DBF_ERR, prepparm->kb.tlv1.gen_key, - keybitsize, 1); + keybuflen, keybitsize, 1); if (rc) { rc = -EIO; goto out; } /* copy the generated vlsc key token */ - t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key; if (keybuf) { - if (*keybufsize >= t->len) - memcpy(keybuf, t, t->len); + if (*keybufsize >= keybuflen) + memcpy(keybuf, t, keybuflen); else rc = -EINVAL; } - *keybufsize = t->len; + *keybufsize = keybuflen; out: free_cprbmem(mem, PARMBSIZE, false, xflags); diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h index 07bbb1c20022..a4534f8b2f1d 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.h +++ b/drivers/s390/crypto/zcrypt_ccamisc.h @@ -136,7 +136,7 @@ struct eccprivkeytoken { * also checked. Returns 0 on success or errno value on failure. */ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize); + const u8 *token, u32 keysize, int keybitsize); /* * Simple check if the token is a valid CCA secure AES cipher key @@ -146,8 +146,8 @@ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, * Returns 0 on success or errno value on failure. */ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize, - int checkcpacfexport); + const u8 *token, u32 keysize, + int keybitsize, int checkcpacfexport); /* * Simple check if the token is a valid CCA secure ECC private -- cgit v1.2.3 From 983279d7f86ade73db86f886e09172dd567031b5 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 23 Jul 2026 11:54:52 +0200 Subject: s390/zcrypt: Fix wrong domain value verification with EP11 CPRBs There is a wrong upper limit check for the domain value when an EP11 CPRB is processed for sending to a crypto card. This check is only active on custom device nodes but may lead to access heap memory behind perms->adm when an administrative CPRB is sent. Add correct limit (AP_DOMAINS = 256) checking to fix this. Fixes: cfd68b33094e ("s390/zcrypt: Filter admin CPRBs on custom devices") Cc: stable@vger.kernel.org Reviewed-by: Finn Callies Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index f57189c2b839..81eefdeae248 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1077,7 +1077,7 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms, print_hex_dump_debug("ep11req: ", DUMP_PREFIX_ADDRESS, 16, 1, ap_msg.msg, ap_msg.len, false); - if (perms != &ap_perms && domain < AUTOSEL_DOM) { + if (perms != &ap_perms && domain < AP_DOMAINS) { if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { if (!test_bit_inv(domain, perms->adm)) { rc = -ENODEV; -- cgit v1.2.3 From e935cd525af4c6ed2e2c6404aa27ca19c7f39ddb Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 23 Jul 2026 11:54:53 +0200 Subject: s390/zcrypt: Close speculative mem read possibility The domain value is extracted from a given CCA or EP11 ioctl struct when a CPRB is about to be sent. Thus this is a user controlled value. Under some special conditions (custom device node used, administrative load) this value is used as an array index after bounds checking, but without speculation barrier. Add the missing array_index_nospec() call to prevent speculative execution where this domain value is used. Fixes: cfd68b33094e ("s390/zcrypt: Filter admin CPRBs on custom devices") Cc: stable@vger.kernel.org Reported-by: Christian Borntraeger Reviewed-by: Finn Callies Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_api.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 81eefdeae248..ec6a4c2f9f04 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -879,6 +879,7 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms, if (perms != &ap_perms && domain < AP_DOMAINS) { if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { + domain = array_index_nospec(domain, AP_DOMAINS); if (!test_bit_inv(domain, perms->adm)) { rc = -ENODEV; goto out; @@ -1079,6 +1080,7 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms, if (perms != &ap_perms && domain < AP_DOMAINS) { if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { + domain = array_index_nospec(domain, AP_DOMAINS); if (!test_bit_inv(domain, perms->adm)) { rc = -ENODEV; goto out; -- cgit v1.2.3 From 01476391aecef36a3b789ee844357b22fbc90665 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Wed, 29 Jul 2026 16:01:34 +0200 Subject: s390/zcrypt: Fix missing mem scrub at clear key import in cca_clr2cipherkey() The helper function _ip_cprb_helper() uses internal buffer memory for building and processing CPRBs. After use this buffer was never scrubbed which could lead to leaving for example clear key material in memory which could be exposed via tricky reuse of this same memory. Extend the _ip_cprb_helper() function with another parameter 'scrub' used to steer scrubbing of this buffer. So now the caller has the opportunity to decide if scrubbing is needed or not. Extend the clear key to secure key token import process in function cca_clr2cipherkey() to tell the helper function from above to scrub the cprb buffer when the clear key value is part of the request data. Add explicit scrubbing on return from function cca_clr2cipherkey() for the random EXOR buffer and the cprb buffer. Overall this cleans the internal used buffer in case of clear key import to prevent sensitive data to get exposed. Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Cc: stable@vger.kernel.org Reviewed-by: Holger Dengler Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_ccamisc.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index f797786107b9..86d2ee78c9f4 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1015,7 +1015,8 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain, int clr_key_bit_size, u8 *key_token, int *key_token_size, - u32 xflags) + u32 xflags, + bool scrub) { int rc, n; u8 *mem, *ptr; @@ -1155,7 +1156,7 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain, *key_token_size = t->len; out: - free_cprbmem(mem, PARMBSIZE, false, xflags); + free_cprbmem(mem, PARMBSIZE, scrub, xflags); return rc; } @@ -1206,28 +1207,32 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags, * 4/4 COMPLETE the secure cipher key import */ rc = _ip_cprb_helper(card, dom, "AES ", "FIRST ", "MIN3PART", - exorbuf, keybitsize, token, &tokensize, xflags); + exorbuf, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 1/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL, - clrkey, keybitsize, token, &tokensize, xflags); + clrkey, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 2/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL, - exorbuf, keybitsize, token, &tokensize, xflags); + exorbuf, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 3/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "COMPLETE", NULL, - NULL, keybitsize, token, &tokensize, xflags); + NULL, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 4/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); @@ -1244,6 +1249,8 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags, *keybufsize = tokensize; out: + memzero_explicit(exorbuf, sizeof(exorbuf)); + memzero_explicit(mem, CPRB_MEMPOOL_ITEM_SIZE); mempool_free(mem, cprb_mempool); return rc; } -- cgit v1.2.3 From 992eb0dfbc4f594d60ae57974efa835bfac34d1e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 30 Jul 2026 09:25:18 +0800 Subject: ASoC/soundwire: Intel: reset the PCMSyCM registers in hda_sdw_bpt_close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resetting the PCMSyCM registers is required for Intel SoundWire stream. The same procedure is done in sdw_hda_dai_hw_params() for the normal SoundWire stream, too. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Pierre-Louis Bossart Acked-by: Vinod Koul Link: https://patch.msgid.link/20260730012518.2180906-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/intel_ace2x.c | 4 +++- include/sound/hda-sdw-bpt.h | 5 +++-- sound/soc/sof/intel/hda-sdw-bpt.c | 31 ++++++++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index b37933efac5d..63f131d5682b 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -261,6 +261,7 @@ static int intel_ace2x_bpt_open_stream(struct sdw_intel *sdw, struct sdw_slave * __func__, str_read_write(command), ret); ret1 = hda_sdw_bpt_close(cdns->dev->parent, /* PCI device */ + sdw->instance, sdw->bpt_ctx.bpt_tx_stream, &sdw->bpt_ctx.dmab_tx_bdl, sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl); if (ret1 < 0) @@ -295,7 +296,8 @@ static void intel_ace2x_bpt_close_stream(struct sdw_intel *sdw, struct sdw_slave struct sdw_cdns *cdns = &sdw->cdns; int ret; - ret = hda_sdw_bpt_close(cdns->dev->parent /* PCI device */, sdw->bpt_ctx.bpt_tx_stream, + ret = hda_sdw_bpt_close(cdns->dev->parent /* PCI device */, sdw->instance, + sdw->bpt_ctx.bpt_tx_stream, &sdw->bpt_ctx.dmab_tx_bdl, sdw->bpt_ctx.bpt_rx_stream, &sdw->bpt_ctx.dmab_rx_bdl); if (ret < 0) diff --git a/include/sound/hda-sdw-bpt.h b/include/sound/hda-sdw-bpt.h index 9b654c31829a..e24a549f7d49 100644 --- a/include/sound/hda-sdw-bpt.h +++ b/include/sound/hda-sdw-bpt.h @@ -27,7 +27,7 @@ int hda_sdw_bpt_send_async(struct device *dev, struct hdac_ext_stream *bpt_tx_st int hda_sdw_bpt_wait(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, struct hdac_ext_stream *bpt_rx_stream); -int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, +int hda_sdw_bpt_close(struct device *dev, int link_id, struct hdac_ext_stream *bpt_tx_stream, struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream, struct snd_dma_buffer *dmab_rx_bdl); @@ -58,7 +58,8 @@ static inline int hda_sdw_bpt_wait(struct device *dev, struct hdac_ext_stream *b return -EOPNOTSUPP; } -static inline int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, +static inline int hda_sdw_bpt_close(struct device *dev, int link_id, + struct hdac_ext_stream *bpt_tx_stream, struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream, struct snd_dma_buffer *dmab_rx_bdl) diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c index 728ffe7ae54d..7351a0870725 100644 --- a/sound/soc/sof/intel/hda-sdw-bpt.c +++ b/sound/soc/sof/intel/hda-sdw-bpt.c @@ -322,7 +322,8 @@ int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **b __func__, ret); close: - ret1 = hda_sdw_bpt_close(dev, *bpt_tx_stream, dmab_tx_bdl, *bpt_rx_stream, dmab_rx_bdl); + ret1 = hda_sdw_bpt_close(dev, link_id, *bpt_tx_stream, dmab_tx_bdl, + *bpt_rx_stream, dmab_rx_bdl); if (ret1 < 0) dev_err(dev, "%s: hda_sdw_bpt_close failed: %d\n", __func__, ret1); @@ -447,14 +448,38 @@ dma_disable: } EXPORT_SYMBOL_NS(hda_sdw_bpt_wait, "SND_SOC_SOF_INTEL_HDA_SDW_BPT"); -int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream, +int hda_sdw_bpt_close(struct device *dev, int link_id, struct hdac_ext_stream *bpt_tx_stream, struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream, struct snd_dma_buffer *dmab_rx_bdl) { + struct snd_sof_dev *sdev = dev_get_drvdata(dev); int ret; int ret1; - ret = hda_sdw_bpt_dma_deprepare(dev, bpt_rx_stream, dmab_rx_bdl); + /* + * In the case of SoundWire we need to reset the PCMSyCM registers. + * Need to continue depreparing the DMA buffers even if this fails. + */ + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, + 0, /* PDI0 */ + 0, 0, SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) + dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d for PDI0\n", + __func__, ret); + + ret1 = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, + 1, /* PDI1 */ + 0, 0, SNDRV_PCM_STREAM_CAPTURE); + if (ret1 < 0) { + dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d for PDI1\n", + __func__, ret1); + if (!ret) + ret = ret1; + } + + ret1 = hda_sdw_bpt_dma_deprepare(dev, bpt_rx_stream, dmab_rx_bdl); + if (!ret) + ret = ret1; ret1 = hda_sdw_bpt_dma_deprepare(dev, bpt_tx_stream, dmab_tx_bdl); if (!ret) -- cgit v1.2.3 From 558f67f1340f803a346ecd14a69c49653111c5f4 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Wed, 29 Jul 2026 21:56:59 +0800 Subject: ipvs: stop estimator after disabled calc phase IPVS estimator kthread 0 starts with zeroed chain and tick limits until its initial calculation phase completes. If network namespace teardown clears ipvs->enable during that phase, ip_vs_est_calc_phase() can return without installing positive limits. The kthread can then continue into its main loop and drain est_temp_list with zero chain_max, tick_max and est_max_count values. Each enqueue consumes one available tick row, but est_count never reaches the zero est_max_count value. After all rows are consumed, the row lookup returns IPVS_EST_NTICKS and ip_vs_enqueue_estimator() writes past the ticks and tick_len arrays. Exit kthread 0 after the calculation phase if the kthread is stopping or IPVS has been disabled. That keeps temporary estimators from being drained after the limits failed to initialize. Estimator kthreads can now self-exit before teardown or reload stops kd->task. Keep an extra task reference after creation and release it with kthread_stop_put(), so kd->task remains valid until the stop paths consume that reference. Fixes: 705dd3444081 ("ipvs: use kthreads for stats estimation") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_est.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index ab09f5182951..05a216a47b45 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -191,8 +191,11 @@ static int ip_vs_estimation_kthread(void *data) } /* kthread 0 will handle the calc phase */ - if (ipvs->est_calc_phase) + if (ipvs->est_calc_phase) { ip_vs_est_calc_phase(ipvs); + if (kthread_should_stop() || !READ_ONCE(ipvs->enable)) + return 0; + } } while (1) { @@ -270,6 +273,7 @@ int ip_vs_est_kthread_start(struct netns_ipvs *ipvs, kd->task = NULL; goto out; } + get_task_struct(kd->task); set_user_nice(kd->task, sysctl_est_nice(ipvs)); if (sysctl_est_preferred_cpulist(ipvs)) @@ -286,7 +290,7 @@ void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd) { if (kd->task) { pr_info("stopping estimator thread %d...\n", kd->id); - kthread_stop(kd->task); + kthread_stop_put(kd->task); kd->task = NULL; } } @@ -526,7 +530,7 @@ static void ip_vs_est_kthread_destroy(struct ip_vs_est_kt_data *kd) if (kd) { if (kd->task) { pr_info("stop unused estimator thread %d...\n", kd->id); - kthread_stop(kd->task); + kthread_stop_put(kd->task); } ip_vs_stats_free(kd->calc_stats); kfree(kd); -- cgit v1.2.3 From 30825970339c107bacaf7f61af90fcdb1f597ca1 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Thu, 30 Jul 2026 01:31:00 +0800 Subject: netfilter: ebt_nflog: pin the NFLOG backend nf_log_unregister() runs after the per-net teardown so its final RCU grace period also drains readers that obtained the logger from a per-net binding. However, ebt_nflog passes an explicit ULOG log type to nf_log_packet() without holding a reference on the selected logger module, unlike the xt_NFLOG and nft_log frontends. An ebtables nflog rule can therefore remain callable while nfnetlink_log is unloaded. The resulting interleaving is: CPU 0 CPU 1 nfnetlink_log_fini() unregister_pernet_subsys() kfree(nfnl_log_pernet(net)) ebt_nflog_tg() nf_log_packet() nfulnl_log_packet() instance_lookup_get_rcu() The global ULOG logger is still registered at this point, so CPU 1 dereferences the per-net state after CPU 0 has freed it. KASAN reported: BUG: KASAN: slab-use-after-free in instance_lookup_get_rcu Read of size 8 at addr ff110001052e6210 by task poc/92 Call Trace: instance_lookup_get_rcu+0x1ce/0x1f0 [nfnetlink_log] nfulnl_log_packet+0x248/0x2fb0 [nfnetlink_log] nf_log_packet+0x204/0x300 ebt_nflog_tg+0x351/0x550 ebt_do_table+0xedf/0x22b0 Allocated by task 90: __kmalloc_noprof+0x186/0x470 ops_init+0x6d/0x420 register_pernet_operations+0x2f6/0x670 register_pernet_subsys+0x23/0x40 Freed by task 93: kfree+0x131/0x3c0 ops_undo_list+0x3e3/0x700 unregister_pernet_operations+0x232/0x490 unregister_pernet_subsys+0x1c/0x30 nfnetlink_log_fini+0x34/0x450 [nfnetlink_log] Acquire the ULOG logger module reference when an ebt_nflog rule is validated and release it when the rule is destroyed. Request the NFLOG backend for legacy callers when needed, matching xt_NFLOG. This prevents module teardown until all ebt_nflog rules have stopped using the logger. Fixes: c83fa19603bd ("netfilter: nf_log: don't call synchronize_rcu in nf_log_unset") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebt_nflog.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 61bf8f4465ab..426f8adc912c 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -41,11 +41,25 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par) static int ebt_nflog_tg_check(const struct xt_tgchk_param *par) { struct ebt_nflog_info *info = par->targinfo; + int ret; if (info->flags & ~EBT_NFLOG_MASK) return -EINVAL; info->prefix[EBT_NFLOG_PREFIX_SIZE - 1] = '\0'; - return 0; + + ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG); + if (ret != 0 && !par->nft_compat) { + request_module("%s", "nfnetlink_log"); + + ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG); + } + + return ret; +} + +static void ebt_nflog_tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_logger_put(par->family, NF_LOG_TYPE_ULOG); } static struct xt_target ebt_nflog_tg_reg __read_mostly = { @@ -54,6 +68,7 @@ static struct xt_target ebt_nflog_tg_reg __read_mostly = { .family = NFPROTO_BRIDGE, .target = ebt_nflog_tg, .checkentry = ebt_nflog_tg_check, + .destroy = ebt_nflog_tg_destroy, .targetsize = sizeof(struct ebt_nflog_info), .me = THIS_MODULE, }; -- cgit v1.2.3 From 8e5fd2a55e2468f6225ef5af01ae58cdbcbae8cf Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 30 Jul 2026 20:38:49 +0200 Subject: netfilter: ipset: rework cidr bookkeeping According to sashiko, the current bookkeeping of cidr values are unsafe on weakly-ordered architectures. Replace the in-place updating with an RCU based method: create the new bookeeping structure, update and replace the old one with the new. Downside that we need to allocate memory when deleting a cidr entry - in case of memory pressure fall back to leave holes which possibility is taken into account at evaluation time. Thanks to Pablo (Pablo Neira Ayuso ) and Cyntia (Cynthia ) for helping me in debugging which resulted the patch "netfilter: ipset: allocate the proper memory for the generic hash structure" on which this very patch depends. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 235 ++++++++++++++++++--------- net/netfilter/ipset/ip_set_hash_ipportnet.c | 4 +- net/netfilter/ipset/ip_set_hash_net.c | 4 +- net/netfilter/ipset/ip_set_hash_netiface.c | 4 +- net/netfilter/ipset/ip_set_hash_netnet.c | 12 +- net/netfilter/ipset/ip_set_hash_netport.c | 4 +- net/netfilter/ipset/ip_set_hash_netportnet.c | 12 +- 7 files changed, 182 insertions(+), 93 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index b2d77973272d..dd31992c915c 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -99,9 +99,15 @@ struct htable { #endif /* Book-keeping of the prefixes added to the set */ +struct net_prefix { + u8 cidr; /* the cidr value */ + u32 count; /* number of elements of this cidr */ +}; + struct net_prefixes { - u32 nets[IPSET_NET_COUNT]; /* number of elements for this cidr */ - u8 cidr[IPSET_NET_COUNT]; /* the cidr value */ + struct rcu_head rcu; + u8 len; + struct net_prefix nets[] __counted_by(len); }; /* Compute the hash table size */ @@ -127,11 +133,6 @@ htable_size(u8 hbits) #else #define __CIDR(cidr, i) (cidr) #endif - -/* cidr + 1 is stored in net_prefixes to support /0 */ -#define NCIDR_PUT(cidr) ((cidr) + 1) -#define NCIDR_GET(cidr) ((cidr) - 1) - #ifdef IP_SET_HASH_WITH_NETS_PACKED /* When cidr is packed with nomatch, cidr - 1 is stored in the data entry */ #define DCIDR_PUT(cidr) ((cidr) - 1) @@ -141,21 +142,11 @@ htable_size(u8 hbits) #define DCIDR_GET(cidr, i) __CIDR(cidr, i) #endif -#define INIT_CIDR(cidr, host_mask) \ - DCIDR_PUT(((cidr) ? NCIDR_GET(cidr) : host_mask)) - -#ifdef IP_SET_HASH_WITH_NET0 -/* cidr from 0 to HOST_MASK value and c = cidr + 1 */ -#define NLEN (HOST_MASK + 1) -#define CIDR_POS(c) ((c) - 1) -#else -/* cidr from 1 to HOST_MASK value and c = cidr + 1 */ -#define NLEN HOST_MASK -#define CIDR_POS(c) ((c) - 2) -#endif +#define INIT_CIDR(n, host_mask) ({ \ + const struct net_prefixes *__n = rcu_dereference(n); \ + DCIDR_PUT((__n)->len ? (__n)->nets[0].cidr : host_mask);\ +}) -#else -#define NLEN 0 #endif /* IP_SET_HASH_WITH_NETS */ #define SET_ELEM_EXPIRED(set, d) \ @@ -292,6 +283,7 @@ static const union nf_inet_addr zeromask = {}; /* The generic hash structure */ struct htype { struct htable __rcu *table; /* the hash table */ + struct net_prefixes __rcu *rnets[IPSET_NET_COUNT]; /* cidr prefixes */ struct htable_gc gc; /* gc workqueue */ u32 maxelem; /* max elements in the hash */ u32 initval; /* random jhash init value */ @@ -302,9 +294,6 @@ struct htype { #if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK) u8 netmask; /* netmask value for subnets to store */ union nf_inet_addr bitmask; /* stores bitmask */ -#endif -#ifdef IP_SET_HASH_WITH_NETS - struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */ #endif /* Because 'next' is IPv4/IPv6 dependent, no elements of this * structure and referred in create() may come after 'next'. @@ -326,50 +315,92 @@ struct mtype_resize_ad { /* Network cidr size book keeping when the hash stores different * sized networks. cidr == real cidr + 1 to support /0. */ -static void +static int mtype_add_cidr(struct ip_set *set, struct htype *h, u8 cidr, u8 n) { - int i, j; + struct net_prefixes *nets, *tmp; + int i, j, found, len = 0, ret = 0; spin_lock_bh(&set->lock); + nets = __ipset_dereference(h->rnets[n]); /* Add in increasing prefix order, so larger cidr first */ - for (i = 0, j = -1; i < NLEN && h->nets[i].cidr[n]; i++) { - if (j != -1) { + for (i = 0, found = -1; i < nets->len; i++) { + if (nets->nets[i].count) + len++; + if (found != -1) { continue; - } else if (h->nets[i].cidr[n] < cidr) { - j = i; - } else if (h->nets[i].cidr[n] == cidr) { - h->nets[CIDR_POS(cidr)].nets[n]++; + } else if (nets->nets[i].cidr < cidr) { + found = i; + } else if (nets->nets[i].cidr == cidr) { + nets->nets[i].count++; goto unlock; } } - if (j != -1) { - for (; i > j; i--) - h->nets[i].cidr[n] = h->nets[i - 1].cidr[n]; + len++; + tmp = kzalloc_flex(*tmp, nets, len, GFP_ATOMIC); + if (!tmp) { + ret = -ENOMEM; + goto unlock; } - h->nets[i].cidr[n] = cidr; - h->nets[CIDR_POS(cidr)].nets[n] = 1; + + tmp->len = len; + for (i = 0, j = 0; i < nets->len; i++) { + if (i == found) { + tmp->nets[j].cidr = cidr; + tmp->nets[j++].count = 1; + } + if (!nets->nets[i].count) + continue; + tmp->nets[j].cidr = nets->nets[i].cidr; + tmp->nets[j++].count = nets->nets[i].count; + } + if (found == -1) { + tmp->nets[j].cidr = cidr; + tmp->nets[j].count = 1; + } + rcu_assign_pointer(h->rnets[n], tmp); + kfree_rcu(nets, rcu); unlock: spin_unlock_bh(&set->lock); + return ret; } static void mtype_del_cidr(struct ip_set *set, struct htype *h, u8 cidr, u8 n) { - u8 i, j, net_end = NLEN - 1; + struct net_prefixes *nets, *tmp; + u8 i, j, len = 0; + int found; spin_lock_bh(&set->lock); - for (i = 0; i < NLEN; i++) { - if (h->nets[i].cidr[n] != cidr) - continue; - h->nets[CIDR_POS(cidr)].nets[n]--; - if (h->nets[CIDR_POS(cidr)].nets[n] > 0) - goto unlock; - for (j = i; j < net_end && h->nets[j].cidr[n]; j++) - h->nets[j].cidr[n] = h->nets[j + 1].cidr[n]; - h->nets[j].cidr[n] = 0; + nets = __ipset_dereference(h->rnets[n]); + for (i = 0, found = -1; i < nets->len; i++) { + if (nets->nets[i].count) + len++; + if (nets->nets[i].cidr == cidr) + found = i; + } + if (unlikely(found == -1)) + goto unlock; + + nets->nets[found].count--; + if (nets->nets[found].count) goto unlock; + len--; + tmp = kzalloc_flex(*tmp, nets, len, GFP_ATOMIC); + if (!tmp) + /* Leave a hole */ + goto unlock; + + tmp->len = len; + for (i = 0, j = 0; i < nets->len; i++) { + if (!nets->nets[i].count || i == found) + continue; + tmp->nets[j].cidr = nets->nets[i].cidr; + tmp->nets[j++].count = nets->nets[i].count; } + rcu_assign_pointer(h->rnets[n], tmp); + kfree_rcu(nets, rcu); unlock: spin_unlock_bh(&set->lock); } @@ -402,6 +433,9 @@ static void mtype_flush(struct ip_set *set) { struct htype *h = set->data; +#ifdef IP_SET_HASH_WITH_NETS + struct net_prefixes *nets, *tmp; +#endif struct htable *t; struct hbucket *n; u32 r, i; @@ -425,7 +459,19 @@ mtype_flush(struct ip_set *set) spin_unlock_bh(&t->hregion[r].lock); } #ifdef IP_SET_HASH_WITH_NETS - memset(h->nets, 0, sizeof(h->nets)); + for (i = 0; i < IPSET_NET_COUNT; i++) { + nets = ipset_dereference_nfnl(h->rnets[i]); + tmp = kzalloc_obj(*tmp, GFP_ATOMIC); + if (!tmp) { + u8 j; + + for (j = 0; j < nets->len; j++) + nets->nets[j].count = 0; + } else { + rcu_assign_pointer(h->rnets[i], tmp); + kfree_rcu(nets, rcu); + } + } #endif } @@ -433,6 +479,9 @@ mtype_flush(struct ip_set *set) static void mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy) { +#ifdef IP_SET_HASH_WITH_NETS + struct htype *h = set->data; +#endif struct hbucket *n; u32 i; @@ -446,6 +495,11 @@ mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy) kfree(n); } +#ifdef IP_SET_HASH_WITH_NETS + if (ext_destroy) + for (i = 0; i < IPSET_NET_COUNT; i++) + kfree(rcu_dereference_raw(h->rnets[i])); +#endif ip_set_free(t->hregion); ip_set_free(t); } @@ -519,8 +573,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) #ifdef IP_SET_HASH_WITH_NETS for (k = 0; k < IPSET_NET_COUNT; k++) mtype_del_cidr(set, h, - NCIDR_PUT(DCIDR_GET(data->cidr, k)), - k); + DCIDR_GET(data->cidr, k), k); #endif t->hregion[r].elements--; ip_set_ext_destroy(set, data); @@ -950,8 +1003,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, #ifdef IP_SET_HASH_WITH_NETS for (i = 0; i < IPSET_NET_COUNT; i++) mtype_del_cidr(set, h, - NCIDR_PUT(DCIDR_GET(data->cidr, i)), - i); + DCIDR_GET(data->cidr, i), i); #endif ip_set_ext_destroy(set, data); t->hregion[r].elements--; @@ -996,7 +1048,7 @@ copy_data: t->hregion[r].elements++; #ifdef IP_SET_HASH_WITH_NETS for (i = 0; i < IPSET_NET_COUNT; i++) - mtype_add_cidr(set, h, NCIDR_PUT(DCIDR_GET(d->cidr, i)), i); + mtype_add_cidr(set, h, DCIDR_GET(d->cidr, i), i); #endif memcpy(data, d, sizeof(struct mtype_elem)); overwrite_extensions: @@ -1110,7 +1162,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, #ifdef IP_SET_HASH_WITH_NETS for (j = 0; j < IPSET_NET_COUNT; j++) mtype_del_cidr(set, h, - NCIDR_PUT(DCIDR_GET(d->cidr, j)), j); + DCIDR_GET(d->cidr, j), j); #endif ip_set_ext_destroy(set, data); @@ -1193,28 +1245,37 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, { struct htype *h = set->data; struct htable *t = rcu_dereference_bh(h->table); + struct net_prefixes *nets0; struct hbucket *n; struct mtype_elem *data; #if IPSET_NET_COUNT == 2 + struct net_prefixes *nets1; struct mtype_elem orig = *d; - int ret, i, j = 0, k; + int ret, i, j, k; #else - int ret, i, j = 0; + int ret, i, j; #endif u32 key, multi = 0; u8 pos; pr_debug("test by nets\n"); - for (; j < NLEN && h->nets[j].cidr[0] && !multi; j++) { + rcu_read_lock_bh(); + nets0 = rcu_dereference_bh(h->rnets[0]); +#if IPSET_NET_COUNT == 2 + nets1 = rcu_dereference_bh(h->rnets[1]); +#endif + for (j = 0; j < nets0->len && !multi; j++) { + if (!nets0->nets[j].count) + continue; #if IPSET_NET_COUNT == 2 mtype_data_reset_elem(d, &orig); - mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0]), false); - for (k = 0; k < NLEN && h->nets[k].cidr[1] && !multi; - k++) { - mtype_data_netmask(d, NCIDR_GET(h->nets[k].cidr[1]), - true); + mtype_data_netmask(d, nets0->nets[j].cidr, false); + for (k = 0; k < nets1->len && !multi; k++) { + if (!nets1->nets[k].count) + continue; + mtype_data_netmask(d, nets1->nets[k].cidr, true); #else - mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0])); + mtype_data_netmask(d, nets0->nets[j].cidr); #endif key = HKEY(d, h->initval, t->htable_bits); n = rcu_dereference_bh(hbucket(t, key)); @@ -1229,7 +1290,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, continue; ret = mtype_data_match(data, ext, mext, set, flags); if (ret != 0) - return ret; + goto unlock; #ifdef IP_SET_HASH_WITH_MULTI /* No match, reset multiple match flag */ multi = 0; @@ -1239,7 +1300,10 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, } #endif } - return 0; + ret = 0; +unlock: + rcu_read_unlock_bh(); + return ret; } #endif @@ -1504,6 +1568,9 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, int ret __attribute__((unused)) = 0; u8 netmask = set->family == NFPROTO_IPV4 ? 32 : 128; union nf_inet_addr bitmask = onesmask; +#endif +#ifdef IP_SET_HASH_WITH_NETS + struct net_prefixes *nets; #endif size_t hsize; struct htype *h; @@ -1604,21 +1671,25 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, */ hbits = fls(hashsize - 1); hsize = htable_size(hbits); - if (hsize == 0) { - kfree(h); - return -ENOMEM; - } + if (hsize == 0) + goto free_h; t = ip_set_alloc(hsize); - if (!t) { - kfree(h); - return -ENOMEM; - } + if (!t) + goto free_h; t->hregion = ip_set_alloc(ahash_sizeof_regions(hbits)); - if (!t->hregion) { - ip_set_free(t); - kfree(h); - return -ENOMEM; + if (!t->hregion) + goto free_t; +#ifdef IP_SET_HASH_WITH_NETS + for (i = 0; i < IPSET_NET_COUNT; i++) { + nets = kzalloc_obj(*nets); + if (!nets) { + while (i > 0) + kfree(rcu_dereference_raw(h->rnets[--i])); + goto free_hregion; + } + RCU_INIT_POINTER(h->rnets[i], nets); } +#endif h->gc.set = set; spin_lock_init(&h->gc.lock); for (i = 0; i < ahash_numof_locks(hbits); i++) @@ -1682,6 +1753,16 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, t->htable_bits, h->maxelem, set->data, t); return 0; + +#ifdef IP_SET_HASH_WITH_NETS +free_hregion: + ip_set_free(t->hregion); +#endif +free_t: + ip_set_free(t); +free_h: + kfree(h); + return -ENOMEM; } #endif /* IP_SET_EMIT_CREATE */ diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 2d6652d43199..195853a25b06 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -138,7 +138,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ipportnet4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -398,7 +398,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ipportnet6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index ce0a9ce5a91f..092f3c9281b8 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -117,7 +117,7 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_net4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -291,7 +291,7 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_net6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index 30a655e5c4fd..b44b95f766b7 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -161,7 +161,7 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netiface4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), .elem = 1, }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -382,7 +382,7 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netiface6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface6_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), .elem = 1, }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c index 8fbe649c9dd3..f7c8a1cc30fc 100644 --- a/net/netfilter/ipset/ip_set_hash_netnet.c +++ b/net/netfilter/ipset/ip_set_hash_netnet.c @@ -149,8 +149,10 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netnet4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + rcu_read_lock_bh(); + e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK); + e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK); + rcu_read_unlock_bh(); if (adt == IPSET_TEST) e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; @@ -388,8 +390,10 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netnet6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + rcu_read_lock_bh(); + e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK); + e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK); + rcu_read_unlock_bh(); if (adt == IPSET_TEST) e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK; diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index d1a0628df4ef..5de4b511de76 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -133,7 +133,7 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_netport4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -353,7 +353,7 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_netport6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c index bf4f91b78e1d..6291532be7a5 100644 --- a/net/netfilter/ipset/ip_set_hash_netportnet.c +++ b/net/netfilter/ipset/ip_set_hash_netportnet.c @@ -157,8 +157,10 @@ hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netportnet4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + rcu_read_lock_bh(); + e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK); + e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK); + rcu_read_unlock_bh(); if (adt == IPSET_TEST) e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; @@ -452,8 +454,10 @@ hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netportnet6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + rcu_read_lock_bh(); + e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK); + e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK); + rcu_read_unlock_bh(); if (adt == IPSET_TEST) e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK; -- cgit v1.2.3 From 712a6f545c359b427daa9a5a782e30d2f8331e25 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 30 Jul 2026 20:38:50 +0200 Subject: netfilter: ipset: switch ext_size to atomic64_t The hash types do not acquire set->lock, they use 'region locking' where only part of the hash table is locked. Parallel inserts and deletes are possible and CPUs can race on ->ext_size update. Switch to atomic64_t. This leaves another bug unresolved: there still can be a race on comment extension re-init. This will be handled in a later commit when converting to rhashtable backend. Fixes: f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 2 +- net/netfilter/ipset/ip_set_bitmap_gen.h | 4 ++-- net/netfilter/ipset/ip_set_core.c | 6 +++--- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- net/netfilter/ipset/ip_set_list_set.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index b98331572ad2..cadae9b2578f 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -273,7 +273,7 @@ struct ip_set { /* Number of elements (vs timeout) */ u32 elements; /* Size of the dynamic extensions (vs timeout) */ - size_t ext_size; + atomic64_t ext_size; /* Element data size */ size_t dsize; /* Offsets to extensions in elements */ diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index bb9b5bed10e1..226fdf17b683 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set) mtype_ext_cleanup(set); bitmap_zero(map->members, map->elements); set->elements = 0; - set->ext_size = 0; + atomic64_set(&set->ext_size, 0); } /* Calculate the actual memory size of the set data */ @@ -93,7 +93,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) { const struct mtype *map = set->data; struct nlattr *nested; - size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size; + size_t memsize = mtype_memsize(map, set->dsize) + atomic64_read(&set->ext_size); nested = nla_nest_start(skb, IPSET_ATTR_DATA); if (!nested) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 6cfad152d7d1..822a53a7f502 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -350,7 +350,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment, size_t len = ext->comment ? strlen(ext->comment) : 0; if (unlikely(c)) { - set->ext_size -= sizeof(*c) + strlen(c->str) + 1; + atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size); rcu_assign_pointer(comment->c, NULL); kfree_rcu(c, rcu); } @@ -362,7 +362,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment, if (unlikely(!c)) return; strscpy(c->str, ext->comment, len + 1); - set->ext_size += sizeof(*c) + strlen(c->str) + 1; + atomic64_add(sizeof(*c) + strlen(c->str) + 1, &set->ext_size); rcu_assign_pointer(comment->c, c); } EXPORT_SYMBOL_GPL(ip_set_init_comment); @@ -392,7 +392,7 @@ ip_set_comment_free(struct ip_set *set, void *ptr) c = rcu_dereference_protected(comment->c, 1); if (unlikely(!c)) return; - set->ext_size -= sizeof(*c) + strlen(c->str) + 1; + atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size); rcu_assign_pointer(comment->c, NULL); kfree_rcu(c, rcu); } diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index dd31992c915c..8841daf28f01 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1373,7 +1373,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); mtype_ext_size(set, &elements, &ext_size); - memsize = mtype_ahash_memsize(h, t) + ext_size + set->ext_size; + memsize = mtype_ahash_memsize(h, t) + ext_size + atomic64_read(&set->ext_size); htable_bits = t->htable_bits; rcu_read_unlock_bh(); diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 1cef84f15e8c..ca3ef9479e83 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -421,7 +421,7 @@ list_set_flush(struct ip_set *set) list_for_each_entry_safe(e, n, &map->members, list) list_set_del(set, e); set->elements = 0; - set->ext_size = 0; + atomic64_set(&set->ext_size, 0); } static void @@ -455,7 +455,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) { const struct list_set *map = set->data; struct nlattr *nested; - size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size; + size_t memsize = list_set_memsize(map, set->dsize) + atomic64_read(&set->ext_size); nested = nla_nest_start(skb, IPSET_ATTR_DATA); if (!nested) -- cgit v1.2.3 From c266769e9ede96b95f9cffc90274da1e25f7a74e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 30 Jul 2026 20:38:51 +0200 Subject: netfilter: ipset: add small wrappers for hash and bucket sizes Preparation patch. Once the ipset hash table is replaced with rhashtable these functions are needed. Add them in extra commit to have reviewable chunks. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 39 ++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 8841daf28f01..ef586b486f51 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -201,6 +201,8 @@ static const union nf_inet_addr zeromask = {}; #undef mtype_same_set #undef mtype_kadt #undef mtype_uadt +#undef mtype_bucket_size +#undef mtype_hash_size #undef mtype_add #undef mtype_del @@ -246,6 +248,8 @@ static const union nf_inet_addr zeromask = {}; #define mtype_same_set IPSET_TOKEN(MTYPE, _same_set) #define mtype_kadt IPSET_TOKEN(MTYPE, _kadt) #define mtype_uadt IPSET_TOKEN(MTYPE, _uadt) +#define mtype_bucket_size IPSET_TOKEN(MTYPE, _bucket_size) +#define mtype_hash_size IPSET_TOKEN(MTYPE, _hash_size) #define mtype_add IPSET_TOKEN(MTYPE, _add) #define mtype_del IPSET_TOKEN(MTYPE, _del) @@ -1358,6 +1362,24 @@ out: return ret; } +static u32 mtype_hash_size(const struct htype *h) +{ + const struct htable *t; + u8 htable_bits; + + rcu_read_lock(); + t = rcu_dereference(h->table); + htable_bits = t->htable_bits; + rcu_read_unlock(); + + return jhash_size(htable_bits); +} + +static u32 mtype_bucket_size(const struct htype *h) +{ + return h->bucketsize; +} + /* Reply a HEADER request: fill out the header part of the set */ static int mtype_head(struct ip_set *set, struct sk_buff *skb) @@ -1368,21 +1390,20 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) size_t memsize; u32 elements = 0; size_t ext_size = 0; - u8 htable_bits; rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); mtype_ext_size(set, &elements, &ext_size); memsize = mtype_ahash_memsize(h, t) + ext_size + atomic64_read(&set->ext_size); - htable_bits = t->htable_bits; rcu_read_unlock_bh(); nested = nla_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; - if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, - htonl(jhash_size(htable_bits))) || - nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) + + if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, htonl(mtype_hash_size(h)))) + goto nla_put_failure; + if (nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) goto nla_put_failure; #ifdef IP_SET_HASH_WITH_BITMASK /* if netmask is set to anything other than HOST_MASK we know that the user supplied netmask @@ -1406,8 +1427,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) goto nla_put_failure; #endif if (set->flags & IPSET_CREATE_FLAG_BUCKETSIZE) { - if (nla_put_u8(skb, IPSET_ATTR_BUCKETSIZE, h->bucketsize) || - nla_put_net32(skb, IPSET_ATTR_INITVAL, htonl(h->initval))) + if (nla_put_u8(skb, IPSET_ATTR_BUCKETSIZE, mtype_bucket_size(h))) + goto nla_put_failure; + if (nla_put_net32(skb, IPSET_ATTR_INITVAL, htonl(h->initval))) goto nla_put_failure; } if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) || @@ -1721,6 +1743,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, INIT_LIST_HEAD(&t->ad); RCU_INIT_POINTER(h->table, t); set->data = h; + #ifndef IP_SET_PROTO_UNDEF if (set->family == NFPROTO_IPV4) { #endif @@ -1749,7 +1772,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, #endif } pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", - set->name, jhash_size(t->htable_bits), + set->name, mtype_hash_size(h), t->htable_bits, h->maxelem, set->data, t); return 0; -- cgit v1.2.3 From cdd97fae0e96fc0f78418b93ca864f98cd63e99a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 30 Jul 2026 20:38:52 +0200 Subject: netfilter: ipset: add and use mtype_del_cidr_all helper Reduces size of upcoming rhashtable conversion. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index ef586b486f51..f00c82acd7f0 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -195,6 +195,7 @@ static const union nf_inet_addr zeromask = {}; #undef mtype_ext_cleanup #undef mtype_add_cidr #undef mtype_del_cidr +#undef mtype_del_cidr_all #undef mtype_ahash_memsize #undef mtype_flush #undef mtype_destroy @@ -242,6 +243,7 @@ static const union nf_inet_addr zeromask = {}; #define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup) #define mtype_add_cidr IPSET_TOKEN(MTYPE, _add_cidr) #define mtype_del_cidr IPSET_TOKEN(MTYPE, _del_cidr) +#define mtype_del_cidr_all IPSET_TOKEN(MTYPE, _del_cidr_all) #define mtype_ahash_memsize IPSET_TOKEN(MTYPE, _ahash_memsize) #define mtype_flush IPSET_TOKEN(MTYPE, _flush) #define mtype_destroy IPSET_TOKEN(MTYPE, _destroy) @@ -410,6 +412,17 @@ unlock: } #endif +static void +mtype_del_cidr_all(struct ip_set *set, struct htype *h, const struct mtype_elem *data) +{ +#ifdef IP_SET_HASH_WITH_NETS + int k; + + for (k = 0; k < IPSET_NET_COUNT; k++) + mtype_del_cidr(set, h, DCIDR_GET(data->cidr, k), k); +#endif +} + /* Calculate the actual memory size of the set data */ static size_t mtype_ahash_memsize(const struct htype *h, const struct htable *t) @@ -551,9 +564,6 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) struct mtype_elem *data; u32 i, j, d; size_t dsize = set->dsize; -#ifdef IP_SET_HASH_WITH_NETS - u8 k; -#endif u8 pos, htable_bits = t->htable_bits; spin_lock_bh(&t->hregion[r].lock); @@ -574,11 +584,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) pr_debug("expired %u/%u\n", i, j); clear_bit(j, n->used); smp_mb__after_atomic(); -#ifdef IP_SET_HASH_WITH_NETS - for (k = 0; k < IPSET_NET_COUNT; k++) - mtype_del_cidr(set, h, - DCIDR_GET(data->cidr, k), k); -#endif + mtype_del_cidr_all(set, h, data); t->hregion[r].elements--; ip_set_ext_destroy(set, data); d++; @@ -1004,11 +1010,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, j = 0; data = ahash_data(n, j, set->dsize); if (!deleted) { -#ifdef IP_SET_HASH_WITH_NETS - for (i = 0; i < IPSET_NET_COUNT; i++) - mtype_del_cidr(set, h, - DCIDR_GET(data->cidr, i), i); -#endif + mtype_del_cidr_all(set, h, data); ip_set_ext_destroy(set, data); t->hregion[r].elements--; } @@ -1163,11 +1165,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (i + 1 == pos) smp_store_release(&n->pos, --pos); t->hregion[r].elements--; -#ifdef IP_SET_HASH_WITH_NETS - for (j = 0; j < IPSET_NET_COUNT; j++) - mtype_del_cidr(set, h, - DCIDR_GET(d->cidr, j), j); -#endif + mtype_del_cidr_all(set, h, d); ip_set_ext_destroy(set, data); if (t->resizing && ext && ext->target) { -- cgit v1.2.3 From 7defddefa95b3699b05b23bf3ceea94d872509bb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 30 Jul 2026 20:38:53 +0200 Subject: netfilter: ipset: switch to rcu work In the initial ipset rhashtable conversion RFC series syzbot reported following splat: BUG: sleeping function [..] at kernel/irq_work.c:289 in_atomic(): 1, [..] irq_work_sync.. kernel/irq_work.c:289 rhashtable_free_and_destroy.. lib/rhashtable.c:1295 hash_netport4_destroy.. net/netfilter/ipset/ip_set_hash_gen.h:420 ip_set_destroy_set_rcu.. net/netfilter/ipset/ip_set_core.c:1169 rcu_core.. kernel/rcu/tree.c:2897 This is because post-rhashtable-conversion hash implementation needs to schedule in the destroy callback. At this time this isn't allowed. Replace existing call_rcu() based destruction with rcu_work api. Also allows to undo split of set destruction and gc work cancelling in a future patch. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 4 +-- net/netfilter/ipset/ip_set_core.c | 46 +++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index cadae9b2578f..c46864cc6623 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -244,8 +244,8 @@ extern void ip_set_type_unregister(struct ip_set_type *set_type); /* A generic IP set */ struct ip_set { - /* For call_cru in destroy */ - struct rcu_head rcu; + /* for set destruction */ + struct rcu_work rwork; /* The name of the set */ char name[IPSET_MAXNAMELEN]; /* Lock protecting the set data */ diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 822a53a7f502..543851a923d0 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -25,6 +25,7 @@ static LIST_HEAD(ip_set_type_list); /* all registered set types */ static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */ +static struct workqueue_struct *ipset_destroy_wq; struct ip_set_net { struct ip_set * __rcu *ip_set_list; /* all individual sets */ @@ -1178,22 +1179,26 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = { .len = IPSET_MAXNAMELEN - 1 }, }; -/* In order to return quickly when destroying a single set, it is split - * into two stages: - * - Cancel garbage collector - * - Destroy the set itself via call_rcu() - */ - static void -ip_set_destroy_set_rcu(struct rcu_head *head) +destroy_and_free_set(struct ip_set *set) { - struct ip_set *set = container_of(head, struct ip_set, rcu); - set->variant->destroy(set); module_put(set->type->me); kfree(set); } +/* In order to return quickly when destroying a single set, + * destruction is done asynchronously via work queues. + */ +static void +ip_set_destroy_set_work(struct work_struct *work) +{ + struct ip_set *set = container_of(to_rcu_work(work), + struct ip_set, rwork); + + destroy_and_free_set(set); +} + static void _destroy_all_sets(struct ip_set_net *inst) { @@ -1283,7 +1288,8 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, /* Must wait for flush to be really finished */ rcu_barrier(); } - call_rcu(&s->rcu, ip_set_destroy_set_rcu); + INIT_RCU_WORK(&s->rwork, ip_set_destroy_set_work); + queue_rcu_work(ipset_destroy_wq, &s->rwork); } return 0; out: @@ -2421,18 +2427,23 @@ static struct pernet_operations ip_set_net_ops = { static int __init ip_set_init(void) { - int ret = register_pernet_subsys(&ip_set_net_ops); + int ret; + + ipset_destroy_wq = alloc_ordered_workqueue("ipset_destroy_wq", 0); + if (!ipset_destroy_wq) + return -ENOMEM; + ret = register_pernet_subsys(&ip_set_net_ops); if (ret) { pr_err("ip_set: cannot register pernet_subsys.\n"); - return ret; + goto out_wq; } ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); if (ret != 0) { pr_err("ip_set: cannot register with nfnetlink.\n"); unregister_pernet_subsys(&ip_set_net_ops); - return ret; + goto out_wq; } ret = nf_register_sockopt(&so_set); @@ -2440,10 +2451,13 @@ ip_set_init(void) pr_err("SO_SET registry failed: %d\n", ret); nfnetlink_subsys_unregister(&ip_set_netlink_subsys); unregister_pernet_subsys(&ip_set_net_ops); - return ret; + goto out_wq; } return 0; +out_wq: + destroy_workqueue(ipset_destroy_wq); + return ret; } static void __exit @@ -2453,9 +2467,7 @@ ip_set_fini(void) nfnetlink_subsys_unregister(&ip_set_netlink_subsys); unregister_pernet_subsys(&ip_set_net_ops); - /* Wait for call_rcu() in destroy */ - rcu_barrier(); - + destroy_workqueue(ipset_destroy_wq); pr_debug("these are the famous last words\n"); } -- cgit v1.2.3 From 646922a0379496154e8c8faca4f8e2fd9100cacc Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 30 Jul 2026 21:35:05 +0300 Subject: ipvs: avoid out-of-bounds write in ip_vs_nat_icmp Sashiko warns that local attacker can modify the packet while it is processed by IPVS. Some places read the IP ihl field multiple times which can cause out-of-bounds access. One such place is ip_vs_nat_icmp where we can write after the validated area. Fix it by providing ciph argument just like it is done for IPv6 and use ciph->len as offset to the embedded transport header. Modify some IPv4 header checks by reading the ihl field only once. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260722101517.36313-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_core.c | 67 +++++++++++++++++++++-------------------- net/netfilter/ipvs/ip_vs_xmit.c | 2 +- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e6ca930a3507..1235f1934e94 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -2062,7 +2062,7 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir, unsigned int toff, - bool has_ports); + bool has_ports, struct ip_vs_iphdr *ciph); #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 6b79e0c4d9e2..0bdaeb4ed61e 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -925,28 +925,27 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, */ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int inout, unsigned int toff, - bool has_ports) + bool has_ports, struct ip_vs_iphdr *ciph) { struct iphdr *iph = ip_hdr(skb); struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); - struct iphdr *ciph = (struct iphdr *)(icmph + 1); - unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); + struct iphdr *cih = (struct iphdr *)(icmph + 1); if (inout) { iph->saddr = cp->vaddr.ip; ip_send_check(iph); - ciph->daddr = cp->vaddr.ip; - ip_send_check(ciph); + cih->daddr = cp->vaddr.ip; + ip_send_check(cih); } else { iph->daddr = cp->daddr.ip; ip_send_check(iph); - ciph->saddr = cp->daddr.ip; - ip_send_check(ciph); + cih->saddr = cp->daddr.ip; + ip_send_check(cih); } /* the TCP/UDP/SCTP port */ if (has_ports) { - __be16 *ports = (void *)ciph + ciph->ihl*4; + __be16 *ports = (void *)(skb->data + ciph->len); if (inout) ports[1] = cp->vport; @@ -960,10 +959,10 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, skb->ip_summed = CHECKSUM_UNNECESSARY; if (inout) - IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, + IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off, "Forwarding altered outgoing ICMP"); else - IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, + IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off, "Forwarding altered incoming ICMP"); } @@ -1056,7 +1055,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); else #endif - ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); + ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph); if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) goto out; @@ -1092,7 +1091,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, struct ip_vs_iphdr ciph; struct ip_vs_conn *cp; struct ip_vs_protocol *pp; - unsigned int offset, ihl; + unsigned int offset; union nf_inet_addr snet; *related = 1; @@ -1105,7 +1104,6 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; } - ihl = ipvsh->len; offset = ipvsh->len; ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); if (ic == NULL) @@ -1131,11 +1129,15 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, /* Now find the contained IP header */ offset += sizeof(_icmph); + if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph)) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (!(cih && cih->version == 4 && cih->ihl >= 5)) + if (!(cih && cih->version == 4 && + ciph.len - ciph.off >= sizeof(struct iphdr))) return NF_ACCEPT; /* The packet looks wrong, ignore */ - pp = ip_vs_proto_get(cih->protocol); + pp = ip_vs_proto_get(ciph.protocol); if (!pp) return NF_ACCEPT; @@ -1146,8 +1148,6 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, "Checking outgoing ICMP for"); - ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph); - /* The embedded headers contain source and dest in reverse order */ cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, ipvs, AF_INET, skb, &ciph); @@ -1155,8 +1155,8 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; snet.ip = ipvsh->saddr.ip; - return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, - hooknum); + return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, + ipvsh->len, hooknum); } #ifdef CONFIG_IP_VS_IPV6 @@ -1803,10 +1803,12 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Now find the contained IP header */ offset += sizeof(_icmph); cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (!(cih && cih->version == 4 && cih->ihl >= 5)) + if (!cih) return NF_ACCEPT; /* The packet looks wrong, ignore */ - raddr = (union nf_inet_addr *)&cih->daddr; hlen_ipip = cih->ihl * 4; + if (!(cih->version == 4 && hlen_ipip >= sizeof(struct iphdr))) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + raddr = (union nf_inet_addr *)&cih->daddr; /* Special case for errors for IPIP/UDP/GRE tunnel packets */ tunnel = false; @@ -1823,9 +1825,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP) return NF_ACCEPT; offset += hlen_ipip; - cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (!(cih && cih->version == 4 && cih->ihl >= 5)) - return NF_ACCEPT; /* The packet looks wrong, ignore */ tunnel = true; } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */ cih->protocol == IPPROTO_GRE) && /* Can be GRE encap */ @@ -1850,21 +1849,25 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Skip IP and UDP/GRE tunnel headers */ offset = offset2 + ulen; /* Now we should be at the original IP header */ - cih = skb_header_pointer(skb, offset, sizeof(_ciph), - &_ciph); - if (cih && cih->version == 4 && cih->ihl >= 5 && - iproto == IPPROTO_IPIP) + if (iproto == IPPROTO_IPIP) tunnel = true; else return NF_ACCEPT; } } - pd = ip_vs_proto_data_get(ipvs, cih->protocol); + if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph)) + return NF_ACCEPT; + pd = ip_vs_proto_data_get(ipvs, ciph.protocol); if (!pd) return NF_ACCEPT; pp = pd->pp; + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); + if (!(cih && cih->version == 4 && + ciph.len - ciph.off >= sizeof(struct iphdr))) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + /* Is the embedded protocol header present? */ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) return NF_ACCEPT; @@ -1872,9 +1875,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, "Checking incoming ICMP for"); - offset2 = offset; - ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); - /* The embedded headers contain source and dest in reverse order. * For IPIP/UDP/GRE tunnel this is error for request, not for reply. */ @@ -1904,11 +1904,12 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, } if (tunnel) { - unsigned int hlen_orig = cih->ihl * 4; + unsigned int hlen_orig = ciph.len - ciph.off; __be32 info = ic->un.gateway; __u8 type = ic->type; __u8 code = ic->code; + offset2 = offset; /* Update the MTU */ if (ic->type == ICMP_DEST_UNREACH && ic->code == ICMP_FRAG_NEEDED) { diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 0b0c5304993a..c4508f3f43dd 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1580,7 +1580,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); + ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; -- cgit v1.2.3 From 99609cb0aa789c8d071050ce8579989551882cc6 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 30 Jul 2026 21:35:06 +0300 Subject: ipvs: return the csum validation for forward hook Sashiko notes that playing games with the skb dst and rt flags instead of providing hooknum is not a good idea when validating the checksums. Also, skipping checksum validation for FORWARD packets risk silent data corruption, even if the only user is the FTP-CMD packets coming from the real server. Sashiko also noticed that by using common checksum helper in the previous commit we actually fixed old bug where the TCP/UDP checksum for IPv6 on CHECKSUM_COMPLETE was not validated correctly. Fixes: e876b75b9020 ("ipvs: fix the checksum validations") Link: https://sashiko.dev/#/patchset/20260722211420.153933-1-pablo%40netfilter.org Link: https://sashiko.dev/#/patchset/20260727185024.67534-1-ja%40ssi.bg Link: https://sashiko.dev/#/patchset/20260728202520.59179-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 19 +++++-------------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 +- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 1235f1934e94..d2813eb795be 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -25,9 +25,7 @@ #include /* for union nf_inet_addr */ #include #include /* for struct ipv6hdr */ -#include #include -#include #if IS_ENABLED(CONFIG_NF_CONNTRACK) #include #endif @@ -2095,30 +2093,23 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) return csum_partial(diff, sizeof(diff), oldsum); } -static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) +static inline bool ip_vs_checksum_needed(struct sk_buff *skb) { /* Checksum unnecessary or already validated? */ if (skb_csum_unnecessary(skb)) return false; - /* LOCAL_OUT ? */ - if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) + /* Locally generated ? */ + if (!skb->dev) return false; - /* !LOCAL_IN (FORWARD) ? */ - if (af == AF_INET6) { - if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) - return false; - } else { - if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) - return false; - } return true; } static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, int offset, int proto, int af) { - if (!ip_vs_checksum_needed(skb, af)) + if (!ip_vs_checksum_needed(skb)) return true; + /* Validate csum even for FORWARD */ return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); } diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 3dbd3096e163..c80567c73469 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -193,7 +193,7 @@ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, struct sctphdr *sh; __le32 cmp, val; - if (!ip_vs_checksum_needed(skb, af)) + if (!ip_vs_checksum_needed(skb)) return 1; sh = (struct sctphdr *)(skb->data + sctphoff); cmp = sh->checksum; -- cgit v1.2.3 From 1246aa2b6ccc8944676bd24ff3e37cc56b93b51b Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Fri, 31 Jul 2026 12:36:28 +0300 Subject: watchdog: bd96801_wdt: Fix timeout for enabled WDG When watchdog is enabled at the probe time, the bd96801 driver retrieves the timeout configuration from the registers to set-up the heart-beat values. As Sashiko pointed out at https://lore.kernel.org/all/20260722085819.495211F000E9@smtp.kernel.org/ the timeout values are incorrectly computed in driver, resulting wrong heartbeat. This leads to devere problems if watchdog was enabled at probe time. According to the data-sheet, the "too fast" ping limit is configured as multiple of FASTNG_MIN. Furthermore, the "too slow" ping limit is configured as multiples of "too fast" timeout. The FASTNG_MIN is set to 11, meaning 1.1 mS and "too fast" and "too slow" limits are computed from this. Hence, converting the limits to mS should be done by dividing by 10, not by dividing by USEC_PER_MSEC. Fix this by dividing the timeout values with correct scaling factor. While at it, fix whitespace problem (double empty line). Signed-off-by: Matti Vaittinen Fixes: 09dad69757b6 ("watchdog: ROHM BD96801 PMIC WDG driver") Link: https://lore.kernel.org/r/amxskHmQbi9v-8_l@mva-rohm [groeck: Added reference to whitespace change to description] Signed-off-by: Guenter Roeck --- drivers/watchdog/bd96801_wdt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/bd96801_wdt.c b/drivers/watchdog/bd96801_wdt.c index 12b74fd2bc05..a25b7cf1488b 100644 --- a/drivers/watchdog/bd96801_wdt.c +++ b/drivers/watchdog/bd96801_wdt.c @@ -169,7 +169,6 @@ static int bd96801_set_wdt_mode(struct wdtbd96801 *w, unsigned int hw_margin, int fastng, slowng, type, ret, reg, mask; struct device *dev = w->dev; - if (hw_margin_min * 1000 > FASTNG_MAX_US) { dev_err(dev, "Unsupported fast timeout %u uS [max %u]\n", hw_margin_min * 1000, FASTNG_MAX_US); @@ -258,10 +257,10 @@ static int bd96801_set_heartbeat_from_hw(struct wdtbd96801 *w, fast = FASTNG_MIN << sel; sel = (val & BD96801_WD_RATIO_MASK) + 1; - w->wdt.max_hw_heartbeat_ms = (fast << sel) / USEC_PER_MSEC; + w->wdt.max_hw_heartbeat_ms = (fast << sel) / 10; if ((conf_reg & BD96801_WD_TYPE_MASK) == BD96801_WD_TYPE_WIN) - w->wdt.min_hw_heartbeat_ms = fast / USEC_PER_MSEC; + w->wdt.min_hw_heartbeat_ms = fast / 10; return 0; } -- cgit v1.2.3 From dcba277d119af323c267d1f27f61b868dac62753 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 27 Jul 2026 16:28:38 +0200 Subject: s390/dasd: Fix path verification interrupted by concurrent dasd_sleep_on_immediatly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When all channel paths to a DASD device are lost and subsequently recovered, the path event handler starts one IO per path via dasd_sleep_on_immediatly() to execute read configuration data (RCD) with high priority. dasd_sleep_on_immediatly() works by terminating the currently running request before inserting the new request. If a concurrent caller, such as the attention handler dasd_eckd_check_attention_work() or the summary unit check handler summary_unit_check_handling_work(), also calls dasd_sleep_on_immediatly() while a path verification RCD is in progress, the RCD gets terminated. The problem is that a terminated request transitions from CLEARED to TERMINATED without going through the normal retry path in __dasd_device_process_ccw_queue. The RCD therefore returns -EIO, and the affected paths remain non-operational after recovery. RCD CQRs used for path verification already carry the DASD_CQR_VERIFY_PATH flag. Extend _dasd_term_running_cqr() to check this flag: instead of terminating such a request, return -EAGAIN. In dasd_sleep_on_immediatly(), loop on -EAGAIN with a short sleep, waiting for the path verification request to complete before inserting the new request. This is consistent with the already indefinite wait_event() that dasd_sleep_on_immediatly() uses for its own request, and all other callers (attention handler, summary unit check handler, reserve/release/steal-lock) benefit automatically without requiring changes. Reviewed-by: Jan Höppner Signed-off-by: Stefan Haberland Link: https://patch.msgid.link/20260727142840.567286-2-sth@linux.ibm.com Signed-off-by: Jens Axboe --- drivers/s390/block/dasd.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 3181c06d91ce..d8d912a3b3fe 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -2511,6 +2512,13 @@ static inline int _dasd_term_running_cqr(struct dasd_device *device) if (list_empty(&device->ccw_queue)) return 0; cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); + /* + * Path verification requests must not be terminated. They are critical + * for bringing paths back online. Terminating them would cause rc=-EIO + * because CLEARED requests skip the retry path. + */ + if (test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) + return -EAGAIN; rc = device->discipline->term_IO(cqr); if (!rc) /* @@ -2535,7 +2543,11 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) return -EIO; } spin_lock_irq(get_ccwdev_lock(device->cdev)); - rc = _dasd_term_running_cqr(device); + while ((rc = _dasd_term_running_cqr(device)) == -EAGAIN) { + spin_unlock_irq(get_ccwdev_lock(device->cdev)); + msleep(1); + spin_lock_irq(get_ccwdev_lock(device->cdev)); + } if (rc) { spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; -- cgit v1.2.3 From 9973026f572db6b67570cadc30942f3014e41079 Mon Sep 17 00:00:00 2001 From: Jan Höppner Date: Mon, 27 Jul 2026 16:28:39 +0200 Subject: s390/dasd: Fix potential NULL pointer dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dasd_release_space() checks the implementation of the is_ese() discipline function before calling it to determine if a given device is an ESE DASD. The current usage of the logical AND operator will lead to a NULL pointer dereference as the function is called even if the function pointer is NULL. Fix this by using the logical OR operator. Fixes: 91dc4a197569 ("s390/dasd: Add new ioctl to release space") Cc: stable@vger.kernel.org # v5.3+ Reported-by: Vasily Gorbik Acked-by: Eduard Shishkin Reviewed-by: Stefan Haberland Signed-off-by: Jan Höppner Signed-off-by: Stefan Haberland Link: https://patch.msgid.link/20260727142840.567286-3-sth@linux.ibm.com Signed-off-by: Jens Axboe --- drivers/s390/block/dasd_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index c85ee42732a3..e5b8b413f5ab 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -324,7 +324,7 @@ out_err: static int dasd_release_space(struct dasd_device *device, struct format_data_t *rdata) { - if (!device->discipline->is_ese && !device->discipline->is_ese(device)) + if (!device->discipline->is_ese || !device->discipline->is_ese(device)) return -ENOTSUPP; if (!device->discipline->release_space) return -ENOTSUPP; -- cgit v1.2.3 From 7f40b346462f563a0d6e841a77b5163d2a882a04 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 27 Jul 2026 16:28:40 +0200 Subject: s390/dasd: Fix undersized format-check buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fmt_buffer_size in dasd_eckd_check_device_format() is declared as int, even though one of the multiplicands, sizeof(struct eckd_count), is a size_t. The expression trkcount * rpt_max * sizeof(struct eckd_count) is therefore correctly evaluated at 64-bit width, but the result is silently truncated when it is stored back into the 32-bit fmt_buffer_size variable. For a sufficiently large track range (start_unit/stop_unit are caller-controlled) this truncation yields a buffer size far smaller than the number of tracks actually requested. kzalloc() then succeeds with an undersized allocation, while the subsequent channel program build still operates on the untruncated track count and writes past the end of that buffer. Compute the buffer size with check_mul_overflow() and keep it in a size_t, so that a value that no longer fits results in -EINVAL instead of a silently truncated allocation size. Fixes: 8fd575200db5 ("s390/dasd: Add new ioctl BIODASDCHECKFMT") Cc: stable@vger.kernel.org #4.7 Reviewed-by: Jan Höppner Signed-off-by: Stefan Haberland Link: https://patch.msgid.link/20260727142840.567286-4-sth@linux.ibm.com Signed-off-by: Jens Axboe --- drivers/s390/block/dasd_eckd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 74fe73b5738a..d356a9f8f016 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -3475,11 +3476,11 @@ static int dasd_eckd_check_device_format(struct dasd_device *base, { struct dasd_eckd_private *private = base->private; struct eckd_count *fmt_buffer; - struct irb irb; + size_t fmt_buffer_size; + unsigned int trkcount; int rpt_max, rpt_exp; - int fmt_buffer_size; + struct irb irb; int trk_per_cyl; - int trkcount; int tpm = 0; int rc; @@ -3490,7 +3491,9 @@ static int dasd_eckd_check_device_format(struct dasd_device *base, rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize); trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; - fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count); + if (check_mul_overflow(trkcount, rpt_max, &fmt_buffer_size) || + check_mul_overflow(fmt_buffer_size, sizeof(struct eckd_count), &fmt_buffer_size)) + return -EINVAL; fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA); if (!fmt_buffer) -- cgit v1.2.3 From c89039b724cc4d5e63f295d45b754e0de0faa0ad Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 31 Jul 2026 15:00:36 +0200 Subject: Revert "thermal: hwmon: Use extra_groups for adding temperature attributes" Revert commit cfb5dc0f60fb ("thermal: hwmon: Use extra_groups for adding temperature attributes") because it is depended on by another one that turned out to be problematic. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1992232.tdWV9SEqCh@rafael.j.wysocki --- drivers/hwmon/hwmon.c | 6 +- drivers/thermal/thermal_hwmon.c | 122 +++++++++++++++++++++++++--------------- include/linux/hwmon.h | 3 +- 3 files changed, 80 insertions(+), 51 deletions(-) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 55a9a3ddd4aa..29dc90a2c3fe 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -1083,7 +1083,6 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); * @dev: the parent device * @name: hwmon name attribute * @drvdata: driver data to attach to created device - * @extra_groups: pointer to list of additional non-standard attribute groups * * The use of this function is restricted. It is provided for legacy reasons * and must only be called from the thermal subsystem. @@ -1095,13 +1094,12 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); */ struct device * hwmon_device_register_for_thermal(struct device *dev, const char *name, - void *drvdata, - const struct attribute_group **extra_groups) + void *drvdata) { if (!name || !dev) return ERR_PTR(-EINVAL); - return __hwmon_device_register(dev, name, drvdata, NULL, extra_groups); + return __hwmon_device_register(dev, name, drvdata, NULL, NULL); } EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, "HWMON_THERMAL"); diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 386dfb9f559e..223ae1571655 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -25,13 +25,25 @@ */ #define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11) +struct thermal_hwmon_attr { + struct device_attribute attr; +}; + +/* one temperature input for each thermal zone */ +struct thermal_hwmon_temp { + struct thermal_zone_device *tz; + struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ + struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ + bool temp_crit_present; +}; + /* hwmon sys I/F */ /* thermal zone devices with the same type share one hwmon device */ struct thermal_hwmon_device { char name[THERMAL_HWMON_NAME_LENGTH]; struct device *device; struct list_head node; - struct thermal_zone_device *tz; + struct thermal_hwmon_temp tz_temp; }; static LIST_HEAD(thermal_hwmon_list); @@ -39,14 +51,19 @@ static LIST_HEAD(thermal_hwmon_list); static DEFINE_MUTEX(thermal_hwmon_list_lock); static ssize_t -temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf) +temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev); - struct thermal_zone_device *tz = hwmon->tz; int temperature; int ret; + struct thermal_hwmon_attr *hwmon_attr + = container_of(attr, struct thermal_hwmon_attr, attr); + struct thermal_hwmon_temp *temp + = container_of(hwmon_attr, struct thermal_hwmon_temp, + temp_input); + struct thermal_zone_device *tz = temp->tz; ret = thermal_zone_get_temp(tz, &temperature); + if (ret) return ret; @@ -54,10 +71,14 @@ temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -temp1_crit_show(struct device *dev, struct device_attribute *attr, char *buf) +temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev); - struct thermal_zone_device *tz = hwmon->tz; + struct thermal_hwmon_attr *hwmon_attr + = container_of(attr, struct thermal_hwmon_attr, attr); + struct thermal_hwmon_temp *temp + = container_of(hwmon_attr, struct thermal_hwmon_temp, + temp_crit); + struct thermal_zone_device *tz = temp->tz; int temperature; int ret; @@ -70,49 +91,22 @@ temp1_crit_show(struct device *dev, struct device_attribute *attr, char *buf) return sysfs_emit(buf, "%d\n", temperature); } -static DEVICE_ATTR_RO(temp1_input); -static DEVICE_ATTR_RO(temp1_crit); - -static struct attribute *thermal_hwmon_attrs[] = { - &dev_attr_temp1_input.attr, - &dev_attr_temp1_crit.attr, - NULL, -}; - -static umode_t thermal_hwmon_attr_is_visible(struct kobject *kobj, - struct attribute *a, int n) +static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz) { - if (a == &dev_attr_temp1_input.attr) - return a->mode; - - if (a == &dev_attr_temp1_crit.attr) { - struct thermal_hwmon_device *hwmon = dev_get_drvdata(kobj_to_dev(kobj)); - struct thermal_zone_device *tz = hwmon->tz; - int dummy; - - if (tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &dummy)) - return a->mode; - } - - return 0; + int temp; + return tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &temp); } -static const struct attribute_group thermal_hwmon_group = { - .attrs = thermal_hwmon_attrs, - .is_visible = thermal_hwmon_attr_is_visible, -}; - -__ATTRIBUTE_GROUPS(thermal_hwmon); - int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; + struct thermal_hwmon_temp *temp; + int result; hwmon = kzalloc_obj(*hwmon); if (!hwmon) return -ENOMEM; - hwmon->tz = tz; /* * Append the thermal zone ID preceded by an underline character to the * type to disambiguate the sensors command output. @@ -120,13 +114,35 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id); strreplace(hwmon->name, '-', '_'); hwmon->device = hwmon_device_register_for_thermal(&tz->device, - hwmon->name, hwmon, - thermal_hwmon_groups); + hwmon->name, hwmon); if (IS_ERR(hwmon->device)) { - int result = PTR_ERR(hwmon->device); + result = PTR_ERR(hwmon->device); + goto free_mem; + } - kfree(hwmon); - return result; + temp = &hwmon->tz_temp; + + temp->tz = tz; + + temp->temp_input.attr.attr.name = "temp1_input"; + temp->temp_input.attr.attr.mode = 0444; + temp->temp_input.attr.show = temp_input_show; + sysfs_attr_init(&temp->temp_input.attr.attr); + result = device_create_file(hwmon->device, &temp->temp_input.attr); + if (result) + goto unregister_name; + + if (thermal_zone_crit_temp_valid(tz)) { + temp->temp_crit.attr.attr.name = "temp1_crit"; + temp->temp_crit.attr.attr.mode = 0444; + temp->temp_crit.attr.show = temp_crit_show; + sysfs_attr_init(&temp->temp_crit.attr.attr); + result = device_create_file(hwmon->device, + &temp->temp_crit.attr); + if (result) + goto unregister_input; + + temp->temp_crit_present = true; } /* The list is needed for hwmon lookup during removal. */ @@ -135,6 +151,15 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) mutex_unlock(&thermal_hwmon_list_lock); return 0; + + unregister_input: + device_remove_file(hwmon->device, &temp->temp_input.attr); + unregister_name: + hwmon_device_unregister(hwmon->device); + free_mem: + kfree(hwmon); + + return result; } EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs); @@ -144,7 +169,7 @@ thermal_hwmon_lookup(const struct thermal_zone_device *tz) struct thermal_hwmon_device *hwmon; list_for_each_entry(hwmon, &thermal_hwmon_list, node) { - if (hwmon->tz == tz) + if (hwmon->tz_temp.tz == tz) return hwmon; } return NULL; @@ -153,6 +178,7 @@ thermal_hwmon_lookup(const struct thermal_zone_device *tz) void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; + struct thermal_hwmon_temp *temp; scoped_guard(mutex, &thermal_hwmon_list_lock) { hwmon = thermal_hwmon_lookup(tz); @@ -162,6 +188,12 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) list_del(&hwmon->node); } + temp = &hwmon->tz_temp; + + device_remove_file(hwmon->device, &temp->temp_input.attr); + if (temp->temp_crit_present) + device_remove_file(hwmon->device, &temp->temp_crit.attr); + hwmon_device_unregister(hwmon->device); kfree(hwmon); } diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 77a6f2bffcba..dd713e193d0c 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -480,8 +480,7 @@ hwmon_device_register_with_info(struct device *dev, const struct attribute_group **extra_groups); struct device * hwmon_device_register_for_thermal(struct device *dev, const char *name, - void *drvdata, - const struct attribute_group **extra_groups); + void *drvdata); struct device * devm_hwmon_device_register_with_info(struct device *dev, const char *name, void *drvdata, -- cgit v1.2.3 From f93d951ce0d02b5dca01c0c72add411fb17849bb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 31 Jul 2026 15:01:15 +0200 Subject: Revert "thermal: hwmon: Register a hwmon device for each thermal zone" Revert commit d6323469bcfb ("thermal: hwmon: Register a hwmon device for each thermal zone") that changed the names of hwmon class devices associated with thermal zones and their sysfs layout which made user space unhappy. Closes: https://lore.kernel.org/linux-pm/cafd8af9-c6e9-4bf2-b496-23e796fbc9a6@linux.dev/ Closes: https://lore.kernel.org/linux-hwmon/ab8b093b-46e6-4738-afcf-4b97c9ad5af9@googlemail.com/ Cc: stable@vger.kernel.org Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2301040.irdbgypaU6@rafael.j.wysocki --- drivers/thermal/thermal_hwmon.c | 151 +++++++++++++++++++++++++++------------- 1 file changed, 104 insertions(+), 47 deletions(-) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 223ae1571655..597c33c8a555 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -19,33 +19,30 @@ #include "thermal_hwmon.h" #include "thermal_core.h" -/* - * Needs to be large enough to hold a thermal zone type string followed by an - * underline character and a 32-bit integer in decimal representation. - */ -#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11) +/* hwmon sys I/F */ +/* thermal zone devices with the same type share one hwmon device */ +struct thermal_hwmon_device { + char type[THERMAL_NAME_LENGTH]; + struct device *device; + int count; + struct list_head tz_list; + struct list_head node; +}; struct thermal_hwmon_attr { struct device_attribute attr; + char name[16]; }; /* one temperature input for each thermal zone */ struct thermal_hwmon_temp { + struct list_head hwmon_node; struct thermal_zone_device *tz; struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ bool temp_crit_present; }; -/* hwmon sys I/F */ -/* thermal zone devices with the same type share one hwmon device */ -struct thermal_hwmon_device { - char name[THERMAL_HWMON_NAME_LENGTH]; - struct device *device; - struct list_head node; - struct thermal_hwmon_temp tz_temp; -}; - static LIST_HEAD(thermal_hwmon_list); static DEFINE_MUTEX(thermal_hwmon_list_lock); @@ -91,6 +88,45 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) return sysfs_emit(buf, "%d\n", temperature); } + +static struct thermal_hwmon_device * +thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz) +{ + struct thermal_hwmon_device *hwmon; + char type[THERMAL_NAME_LENGTH]; + + mutex_lock(&thermal_hwmon_list_lock); + list_for_each_entry(hwmon, &thermal_hwmon_list, node) { + strscpy(type, tz->type); + strreplace(type, '-', '_'); + if (!strcmp(hwmon->type, type)) { + mutex_unlock(&thermal_hwmon_list_lock); + return hwmon; + } + } + mutex_unlock(&thermal_hwmon_list_lock); + + return NULL; +} + +/* Find the temperature input matching a given thermal zone */ +static struct thermal_hwmon_temp * +thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon, + const struct thermal_zone_device *tz) +{ + struct thermal_hwmon_temp *temp; + + mutex_lock(&thermal_hwmon_list_lock); + list_for_each_entry(temp, &hwmon->tz_list, hwmon_node) + if (temp->tz == tz) { + mutex_unlock(&thermal_hwmon_list_lock); + return temp; + } + mutex_unlock(&thermal_hwmon_list_lock); + + return NULL; +} + static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz) { int temp; @@ -101,39 +137,54 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; struct thermal_hwmon_temp *temp; + int new_hwmon_device = 1; int result; + hwmon = thermal_hwmon_lookup_by_type(tz); + if (hwmon) { + new_hwmon_device = 0; + goto register_sys_interface; + } + hwmon = kzalloc_obj(*hwmon); if (!hwmon) return -ENOMEM; - /* - * Append the thermal zone ID preceded by an underline character to the - * type to disambiguate the sensors command output. - */ - scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id); - strreplace(hwmon->name, '-', '_'); + INIT_LIST_HEAD(&hwmon->tz_list); + strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH); + strreplace(hwmon->type, '-', '_'); hwmon->device = hwmon_device_register_for_thermal(&tz->device, - hwmon->name, hwmon); + hwmon->type, hwmon); if (IS_ERR(hwmon->device)) { result = PTR_ERR(hwmon->device); goto free_mem; } - temp = &hwmon->tz_temp; + register_sys_interface: + temp = kzalloc_obj(*temp); + if (!temp) { + result = -ENOMEM; + goto unregister_name; + } temp->tz = tz; + hwmon->count++; - temp->temp_input.attr.attr.name = "temp1_input"; + snprintf(temp->temp_input.name, sizeof(temp->temp_input.name), + "temp%d_input", hwmon->count); + temp->temp_input.attr.attr.name = temp->temp_input.name; temp->temp_input.attr.attr.mode = 0444; temp->temp_input.attr.show = temp_input_show; sysfs_attr_init(&temp->temp_input.attr.attr); result = device_create_file(hwmon->device, &temp->temp_input.attr); if (result) - goto unregister_name; + goto free_temp_mem; if (thermal_zone_crit_temp_valid(tz)) { - temp->temp_crit.attr.attr.name = "temp1_crit"; + snprintf(temp->temp_crit.name, + sizeof(temp->temp_crit.name), + "temp%d_crit", hwmon->count); + temp->temp_crit.attr.attr.name = temp->temp_crit.name; temp->temp_crit.attr.attr.mode = 0444; temp->temp_crit.attr.show = temp_crit_show; sysfs_attr_init(&temp->temp_crit.attr.attr); @@ -145,17 +196,21 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) temp->temp_crit_present = true; } - /* The list is needed for hwmon lookup during removal. */ mutex_lock(&thermal_hwmon_list_lock); - list_add_tail(&hwmon->node, &thermal_hwmon_list); + if (new_hwmon_device) + list_add_tail(&hwmon->node, &thermal_hwmon_list); + list_add_tail(&temp->hwmon_node, &hwmon->tz_list); mutex_unlock(&thermal_hwmon_list_lock); return 0; unregister_input: device_remove_file(hwmon->device, &temp->temp_input.attr); + free_temp_mem: + kfree(temp); unregister_name: - hwmon_device_unregister(hwmon->device); + if (new_hwmon_device) + hwmon_device_unregister(hwmon->device); free_mem: kfree(hwmon); @@ -163,37 +218,39 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) } EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs); -static struct thermal_hwmon_device * -thermal_hwmon_lookup(const struct thermal_zone_device *tz) -{ - struct thermal_hwmon_device *hwmon; - - list_for_each_entry(hwmon, &thermal_hwmon_list, node) { - if (hwmon->tz_temp.tz == tz) - return hwmon; - } - return NULL; -} - void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; struct thermal_hwmon_temp *temp; - scoped_guard(mutex, &thermal_hwmon_list_lock) { - hwmon = thermal_hwmon_lookup(tz); - if (!hwmon) - return; - - list_del(&hwmon->node); + hwmon = thermal_hwmon_lookup_by_type(tz); + if (unlikely(!hwmon)) { + /* Should never happen... */ + dev_dbg(&tz->device, "hwmon device lookup failed!\n"); + return; } - temp = &hwmon->tz_temp; + temp = thermal_hwmon_lookup_temp(hwmon, tz); + if (unlikely(!temp)) { + /* Should never happen... */ + dev_dbg(&tz->device, "temperature input lookup failed!\n"); + return; + } device_remove_file(hwmon->device, &temp->temp_input.attr); if (temp->temp_crit_present) device_remove_file(hwmon->device, &temp->temp_crit.attr); + mutex_lock(&thermal_hwmon_list_lock); + list_del(&temp->hwmon_node); + kfree(temp); + if (!list_empty(&hwmon->tz_list)) { + mutex_unlock(&thermal_hwmon_list_lock); + return; + } + list_del(&hwmon->node); + mutex_unlock(&thermal_hwmon_list_lock); + hwmon_device_unregister(hwmon->device); kfree(hwmon); } -- cgit v1.2.3 From 7f03a417fc75fbe31a06199d8b609b8f313b5301 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 20 Jul 2026 19:19:41 +0930 Subject: btrfs: disable large folios for systems with highmem [BUG] There is a bug report that on 32bit systems (i686), btrfs crashes when trying to do zstd compression: BUG: unable to handle page fault for address: fffbc000 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page CPU: 0 UID: 0 PID: 61 Comm: kworker/u8:5 Tainted: G N 7.2.0-rc3-P3 #2 PREEMPTLAZY Hardware name: LENOVO 2007F2G/2007F2G, BIOS 79ETE7WW (2.27 ) 03/21/2011 Workqueue: btrfs-delalloc btrfs_work_helper EIP: ZSTD_compressStream2+0x221/0x5fc Call Trace: ZSTD_compressStream+0xd/0x48 zstd_compress_stream+0x8/0x10 zstd_compress_bio+0x20a/0x564 btrfs_compress_bio+0x94/0xc0 compress_file_range+0x20a/0x380 btrfs_work_helper+0xc1/0x1b4 process_scheduled_works+0x15f/0x204 worker_thread+0x10c/0x178 kthread+0xe1/0xe8 ret_from_fork+0x1d/0x14c ret_from_fork_asm+0x12/0x18 entry_INT80_32+0xf0/0xf0 CR2: 00000000fffbc000 ---[ end trace 0000000000000000 ]--- [CAUSE] Inside zstd_compress_bio(), we assume the whole page cache folio can be mapped in one go. However that assumption is not true on systems with CONFIG_HIGHMEM, the pages of the large folio can be in HIGHMEM, which needs to be mapped before access. Meanwhile zstd_compress_bio() only map the page of a large folio where the start filepos is, the remaining pages are not mapped, and accessing the remaining pages will trigger the above crash. [FIX] Do not enable large folios when the kernel has CONFIG_HIGHMEM enabled. This is the same handling for bs > ps support. Link: https://github.com/kdave/btrfs-progs/issues/1146 Reported-by: Erhard Furtner Fixes: 9bce95edb1b4 ("btrfs: move large data folios out of experimental features") Reviewed-by: Jeff Layton Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 36332df9a0f1..87385b97c81b 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3468,7 +3468,15 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device fs_info->sectorsize = sectorsize; fs_info->sectorsize_bits = ilog2(sectorsize); fs_info->block_min_order = ilog2(round_up(sectorsize, PAGE_SIZE) >> PAGE_SHIFT); - fs_info->block_max_order = calc_block_max_order(fs_info->sectorsize_bits); + /* + * For HIGHMEM, a large folio cannot be mapped in one go, breaking a lot + * of basic assumptions for btrfs IOs. + * Disable large folios for such 32-bit systems. + */ + if (IS_ENABLED(CONFIG_HIGHMEM)) + fs_info->block_max_order = fs_info->block_min_order; + else + fs_info->block_max_order = calc_block_max_order(fs_info->sectorsize_bits); fs_info->csums_per_leaf = BTRFS_MAX_ITEM_SIZE(fs_info) / fs_info->csum_size; fs_info->stripesize = stripesize; fs_info->fs_devices->fs_info = fs_info; -- cgit v1.2.3 From 0fa78ef637deb5dbe341582f88553a4bce496de0 Mon Sep 17 00:00:00 2001 From: David Lee Date: Wed, 15 Jul 2026 09:05:14 +0000 Subject: btrfs: lzo: reject inline extents without valid headers [BUG] For a crafted btrfs image, the following KASAN can be triggered when reading an inline lzo compressed file extent: BUG: KASAN: slab-out-of-bounds in lzo_decompress+0x57d/0x700 Read of size 4 at addr ffff888006f2e644 by task btrfs_lzo_inlin/77 Call Trace: dump_stack_lvl+0x5b/0x70 print_report+0xd1/0x610 kasan_report+0xe0/0x110 __asan_report_load_n_noabort+0x13/0x20 lzo_decompress+0x57d/0x700 btrfs_decompress+0x140/0x1c0 uncompress_inline+0x147/0x1b0 btrfs_get_extent+0xb23/0x10a0 btrfs_do_readpage.constprop.0+0x538/0x1ac0 btrfs_readahead+0x32f/0x5f0 read_pages+0x16f/0x850 page_cache_ra_unbounded+0x296/0x490 do_page_cache_ra+0xd9/0x130 page_cache_sync_ra+0x3ee/0x6f0 filemap_get_pages+0x306/0x15c0 filemap_read+0x329/0xd00 btrfs_file_read_iter+0x1f8/0x2b0 vfs_read+0x4ef/0x720 ksys_read+0xf8/0x1d0 __x64_sys_read+0x71/0xb0 x64_sys_call+0x1ab0/0x1b70 do_syscall_64+0x61/0x470 entry_SYSCALL_64_after_hwframe+0x4b/0x53 [CAUSE] For an inline lzo compressed file extent, there should always be one lzo header, recording the total length of the compressed data, followed by one segment header, recording the compressed lzo payload. But if a crafted inline lzo compressed file extent contains only an lzo header, without the segment header or payload, lzo_decompress() will still try to read the segment header, causing a read beyond the item boundary. Furthermore if the inline lzo compressed file extent is the first item of the leaf, it will be at the extent buffer boundary. The above out-of-boundary read will go beyond the extent buffer boundary, triggering the above KASAN report. [FIX] Validate the total length of the inlined lzo compressed file extent, to make sure there is at least one LZO header and one segment header, and a non-zero payload. Fixes: a6fa6fae40ec ("btrfs: Add lzo compression support") Assisted-by: Codex:gpt-5.5 Signed-off-by: David Lee [ Rework the commit message to remove slop ] Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/lzo.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 1531adb117d1..2f0996692da0 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -552,9 +552,10 @@ 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 * 2 || + 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); + srclen, LZO_LEN * 2, max_segment_len + LZO_LEN * 2); return -EUCLEAN; } -- cgit v1.2.3 From d2a4e4e626b2f4670b69b430c357f03f53eb6632 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Mon, 27 Jul 2026 14:53:52 +0300 Subject: btrfs: fix memory leak in btrfs_do_encoded_write() Local fuzzing of 6.12.94 has found the following memory leak: Unreferenced object 0xffff888018050a80 (size 64): comm "syz.0.17", pid 10297, jiffies 4294953601 hex dump (first 32 bytes): 00 10 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ 10 0a 05 18 80 88 ff ff 10 0a 05 18 80 88 ff ff ................ backtrace (crc a8a6fc29): kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline] slab_post_alloc_hook mm/slub.c:4152 [inline] slab_alloc_node mm/slub.c:4197 [inline] __kmalloc_cache_noprof+0x168/0x2c0 mm/slub.c:4358 kmalloc_noprof include/linux/slab.h:878 [inline] extent_changeset_alloc fs/btrfs/extent_io.h:207 [inline] qgroup_reserve_data+0x1c5/0x7d0 fs/btrfs/qgroup.c:4305 btrfs_qgroup_reserve_data+0x2e/0xb0 fs/btrfs/qgroup.c:4355 btrfs_do_encoded_write+0x92e/0x1040 fs/btrfs/inode.c:9746 btrfs_encoded_write fs/btrfs/file.c:1482 [inline] btrfs_do_write_iter+0x280/0x610 fs/btrfs/file.c:1507 btrfs_ioctl_encoded_write+0x3d6/0x490 fs/btrfs/ioctl.c:4738 btrfs_ioctl+0x6f9/0xc90 fs/btrfs/ioctl.c:-1 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:906 [inline] __se_sys_ioctl+0xf9/0x170 fs/ioctl.c:892 do_syscall_x64 arch/x86/entry/common.c:47 [inline] do_syscall_64+0xbe/0x1a0 arch/x86/entry/common.c:78 entry_SYSCALL_64_after_hwframe+0x77/0x7f Unreferenced object 0xffff888018050a00 (size 64): comm "syz.0.17", pid 10297, jiffies 4294953601 hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 ff 0f 00 00 00 00 00 00 ................ 90 0a 05 18 80 88 ff ff 90 0a 05 18 80 88 ff ff ................ backtrace (crc cb5c9580): kmemleak_alloc_recursive include/linux/kmemleak.h:42 [inline] slab_post_alloc_hook mm/slub.c:4152 [inline] slab_alloc_node mm/slub.c:4197 [inline] __kmalloc_cache_noprof+0x168/0x2c0 mm/slub.c:4358 kmalloc_noprof include/linux/slab.h:878 [inline] kzalloc_noprof include/linux/slab.h:1014 [inline] ulist_prealloc+0x9c/0x110 fs/btrfs/ulist.c:114 extent_changeset_prealloc fs/btrfs/extent_io.h:217 [inline] __set_extent_bit+0x16b/0x1a70 fs/btrfs/extent-io-tree.c:1086 set_record_extent_bits+0x50/0x90 fs/btrfs/extent-io-tree.c:1821 qgroup_reserve_data+0x274/0x7d0 fs/btrfs/qgroup.c:4312 btrfs_qgroup_reserve_data+0x2e/0xb0 fs/btrfs/qgroup.c:4355 btrfs_do_encoded_write+0x92e/0x1040 fs/btrfs/inode.c:9746 btrfs_encoded_write fs/btrfs/file.c:1482 [inline] btrfs_do_write_iter+0x280/0x610 fs/btrfs/file.c:1507 btrfs_ioctl_encoded_write+0x3d6/0x490 fs/btrfs/ioctl.c:4738 btrfs_ioctl+0x6f9/0xc90 fs/btrfs/ioctl.c:-1 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:906 [inline] __se_sys_ioctl+0xf9/0x170 fs/ioctl.c:892 do_syscall_x64 arch/x86/entry/common.c:47 [inline] do_syscall_64+0xbe/0x1a0 arch/x86/entry/common.c:78 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fix this by freeing an extent changeset before returning from btrfs_do_encoded_write(). Fixes: 7c0c7269f7b5 ("btrfs: add BTRFS_IOC_ENCODED_WRITE") Reviewed-by: Filipe Manana Signed-off-by: Dmitry Antipov Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 04ea10b61bbb..a443b8e6824d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9954,6 +9954,7 @@ out_cb: if (cb) cleanup_compressed_bio(cb); out: + extent_changeset_free(data_reserved); if (ret >= 0) iocb->ki_pos += encoded->len; return ret; -- cgit v1.2.3 From 4c375ac546ea667e619ab77d34cca6edcab7c448 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 30 Jul 2026 16:37:39 +0930 Subject: btrfs: disable bs > ps support if no transparent hugepage support Btrfs relies on mapping_set_folio_order_range() to set the minimal folio order for all its data inodes, but that function will be no-op if transparent hugepage is not enabled. Guard the bs > ps support behind CONFIG_TRANSPARENT_HUGEPAGE, just like all other filesystems. Fixes: 98077f7f2180 ("btrfs: enable experimental bs > ps support") Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/Kconfig | 3 ++- fs/btrfs/fs.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 9de04c37e11a..4b10d78ed99b 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -106,7 +106,8 @@ config BTRFS_EXPERIMENTAL - extent tree v2 - complex rework of extent tracking - - block size > page size support + - block size > page size support - needs transparent huge page and + non-HIGHMEM system - huge folios for data - folios can be as large as 2MiB now diff --git a/fs/btrfs/fs.c b/fs/btrfs/fs.c index 14d83565cdee..dcf12979af33 100644 --- a/fs/btrfs/fs.c +++ b/fs/btrfs/fs.c @@ -166,9 +166,17 @@ bool __attribute_const__ btrfs_supported_blocksize(u32 blocksize) * * Considering HIGHMEM is such a pain to deal with and it's going * to be deprecated eventually, just reject HIGHMEM && bs > ps cases. + * + * Finally, for bs > ps cases, we need to set the minimal folio order, + * which requires transparent hugepage. */ - if (IS_ENABLED(CONFIG_HIGHMEM) && blocksize > PAGE_SIZE) - return false; + if (blocksize > PAGE_SIZE) { + if (IS_ENABLED(CONFIG_HIGHMEM)) + return false; + + if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) + return false; + } return true; #endif return false; -- cgit v1.2.3 From 38417f5fc8e3323218c19bd7e419eec4fb0697bd Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Thu, 30 Jul 2026 20:39:20 +0800 Subject: ASoC: amd: yc: Add DMI quirk for HP Victus Laptop 16-e1xxx Add DMI quirk to enable ACP6x sound card for HP Victus by HP Laptop 16-e1xxx, which fixes microphone not working issue. Without this quirk, the DMIC on acp6x device is not properly enabled, causing the microphone to not work. Link: https://bugzilla.kernel.org/show_bug.cgi?id=218926 Link: https://github.com/CuerdOS/linux-kernel-nhs/commit/b29ba27a0ed672664071b4b345e63b62a419d31d Signed-off-by: Zhang Heng Reported-by: CuerdOS Dev Team Signed-off-by: CuerdOS Dev Team Link: https://patch.msgid.link/20260730123920.104525-1-zhangheng@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index de5c895e9fbd..b62dd77b588d 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -724,6 +724,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8E35"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Laptop 16-e1xxx"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From 0ef349734a93227b45f65fc50a3311d1cc5f03e9 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 31 Jul 2026 10:14:49 +0930 Subject: btrfs: initialize inode mapping flags for cached inodes [BUG] When running generic/795 with 8K block size, 4K page size, the test always fails, triggering some ASSERT()s related to folio size: 795 (241074): drop_caches: 3 assertion failed: IS_ALIGNED(start, blocksize) && IS_ALIGNED(end + 1, blocksize), in extent_io.c:1404 (blocksize=8192 root=262 ino=258 start=16826368 end=16830463 mapping min order=0) ------------[ cut here ]------------ kernel BUG at extent_io.c:1404! Oops: invalid opcode: 0000 [#1] SMP CPU: 8 UID: 0 PID: 241105 Comm: fsstress Tainted: G OE 7.2.0-rc5-custom+ #442 PREEMPT(full) f4bfb352566f3949f29c233ce6f735050a03b245 Tainted: [O]=OOT_MODULE, [E]=UNSIGNED_MODULE Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS unknown 02/02/2022 RIP: 0010:assert_folio_range.cold+0x3d/0x3f [btrfs] Call Trace: btrfs_read_folio+0x9e/0x170 [btrfs 4cd1dd93b341b8ef766643f9512f4a86259567a3] prepare_one_folio.constprop.0+0x104/0x2a0 [btrfs 4cd1dd93b341b8ef766643f9512f4a86259567a3] btrfs_buffered_write+0x285/0xa50 [btrfs 4cd1dd93b341b8ef766643f9512f4a86259567a3] btrfs_do_write_iter+0x1aa/0x210 [btrfs 4cd1dd93b341b8ef766643f9512f4a86259567a3] iter_file_splice_write+0x31a/0x540 direct_splice_actor+0x53/0x170 splice_direct_to_actor+0xe9/0x240 do_splice_direct+0x76/0xb0 vfs_copy_file_range+0x1fd/0x630 __x64_sys_copy_file_range+0xf9/0x220 do_syscall_64+0xe1/0x790 entry_SYSCALL_64_after_hwframe+0x4b/0x53 ---[ end trace 0000000000000000 ]--- The ASSERT() itself is added by a later patch. The crash is triggered with that new debug patch, and without this fix. [CAUSE] In the above case, the start 16826368 is properly 8K aligned, but the end (16830463 + 1) is not 8K aligned. Furthermore the mapping's minimal folio order is 0, not the expected 1 for 8K block size with 4K page size. So this means some inodes do not have btrfs_set_inode_mapping_order() called on it. The missing btrfs_set_inode_mapping_order() call happens for cached inodes, through the following events: - btrfs_create_new_inode() called for inode X Which properly sets minimal folio order for the VFS inode. - btrfs_update_inode() called for inode X Which calls btrfs_delayed_update_inode() to create a delayed_node into root->delayed_nodes xarray. - Drop cache/memory pressure, evicting in-memory inode X Which evicted the inode X, but delayed_node is still in root->delayed_nodes for future reuse. - btrfs_iget() for inode X called again btrfs_iget() |- btrfs_iget_locked() | |- iget5_locked_rcu() | Which creates a new vfs_inode for btrfs, whose mapping still | has the minimal order as 0. | |- btrfs_read_locked_inode() |- btrfs_fill_inode() | |- btrfs_get_delayed_node() | Which found out the previous node, and use that delayed | node to initialize the new inode. | |- filled = true; |- if (filled) goto cache_index; Which skips the btrfs_update_inode_mapping_flags() and btrfs_set_inode_mapping_order() calls. So the inode still has minimal folio order set as 0, not the required 1. Thus later page cache read will get a folio whose size is smaller than block size, as the mapping has its minimal folio order set as 0 not 1, then trigger the ASSERT(). [FIX] Move the btrfs_update_inode_mapping_flags() and btrfs_set_inode_mapping_order() calls under cache_index label, so that the mapping flags and minimal folio order is always set no matter if we have a cached inode. Assisted-by: LLM (analysis) Fixes: ecde48a1a6b3 ("btrfs: expose per-inode stable writes flag") Fixes: cc38d178ff33 ("btrfs: enable large data folio support under CONFIG_BTRFS_EXPERIMENTAL") Reviewed-by: Filipe Manana Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a443b8e6824d..afea512c2219 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3938,10 +3938,11 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path btrfs_inode_split_flags(btrfs_inode_flags(leaf, inode_item), &inode->flags, &inode->ro_flags); + +cache_index: btrfs_update_inode_mapping_flags(inode); btrfs_set_inode_mapping_order(inode); -cache_index: /* * If we were modified in the current generation and evicted from memory * and then re-read we need to do a full sync since we don't have any -- cgit v1.2.3 From 17661c67b206612cb3ba65d5ae726cd2015d0a53 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 14:23:43 +0300 Subject: ASoC: SOF: ipc4-pcm: Continue the pipeline trigger in case of IPC timeout Ignore IPC errors for pipeline state change if the firmware state is crashed or the IPC has timed out. If the firmware has crashed the kernel still needs to go through the state changes to reset its internal to be able to correctly work the next time the DSP is booted up. The case with IPC timeout is a bit more problematic, but it has been rootcaused to be the result of system scheduling blockage and the firmware did actually received and handled the message, but the reply handling got blocked by issues outside of the SOF stack. So far the best way to handle this is to continue with setting the state. Fixes: c40aad7c81e5 ("ASoC: SOF: ipc4-pcm: Workaround for crashed firmware on system suspend") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20260730112343.26687-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index fc3ead77e5ea..5929ecf6642e 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -528,7 +528,19 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list); if (ret < 0) { spcm_err(spcm, substream->stream, "failed to pause all pipelines\n"); - goto free; + /* + * workaround: if the firmware is crashed or the IPC timed out + * while setting the pipeline state we must ignore the error + * code and proceed to set adjust the local pipeline states. + * + * If the firmware is crashed we will not send IPC messages + * and we are going to see errors printed, but the state of the + * widgets will be correct for the next boot. + */ + if (sdev->fw_state != SOF_FW_CRASHED && ret != -ETIMEDOUT) + goto free; + + ret = 0; } /* update PAUSED state for all pipelines just triggered */ @@ -560,14 +572,15 @@ skip_pause_transition: "failed to set final state %d for all pipelines\n", state); /* - * workaround: if the firmware is crashed while setting the - * pipelines to reset state we must ignore the error code and - * reset it to 0. - * Since the firmware is crashed we will not send IPC messages + * workaround: if the firmware is crashed or the IPC timed out + * while setting the pipeline state we must ignore the error + * code and proceed to set adjust the local pipeline states. + * + * If the firmware is crashed we will not send IPC messages * and we are going to see errors printed, but the state of the * widgets will be correct for the next boot. */ - if (sdev->fw_state != SOF_FW_CRASHED || state != SOF_IPC4_PIPE_RESET) + if (sdev->fw_state != SOF_FW_CRASHED && ret != -ETIMEDOUT) goto free; ret = 0; -- cgit v1.2.3 From 28254722a459938d97150d3b0712b81e06d0645e Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Thu, 30 Jul 2026 22:15:07 +0000 Subject: selinux: bpf: check SBLABEL_MNT before isec init selinux_inode_init_security() marks the isec as initialized before checking if mount labeling is supported (SBLABEL_MNT). This was fine until commit 9722955b5430 ("bpf: Add simple xattr support to bpffs"), where genfscon bpffs mounts fail the SBLABEL_MNT check as expected (no xattrs) and yet leave the isec->initialized. This breaks subsequent calls to inode_doinit_with_dentry(). Do the SBLABEL_MNT check before the inode security is initialized. Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/akWdcp6P0FkNDzBk@google.com/ Fixes: 9722955b5430 ("bpf: Add simple xattr support to bpffs") Acked-by: Stephen Smalley Signed-off-by: Carlos Llamas Signed-off-by: Paul Moore --- security/selinux/hooks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8d6945edae7a..18dd28b2bb13 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2974,6 +2974,10 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, sbsec = selinux_superblock(dir->i_sb); + if (!selinux_initialized() || + !(sbsec->flags & SBLABEL_MNT)) + return -EOPNOTSUPP; + newsid = crsec->create_sid; newsclass = inode_mode_to_security_class(inode->i_mode); rc = selinux_determine_inode_label(crsec, dir, qstr, newsclass, &newsid); @@ -2988,10 +2992,6 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, isec->initialized = LABEL_INITIALIZED; } - if (!selinux_initialized() || - !(sbsec->flags & SBLABEL_MNT)) - return -EOPNOTSUPP; - xattr = lsm_get_xattr_slot(xattrs, xattr_count); if (xattr) { rc = security_sid_to_context_force(newsid, -- cgit v1.2.3 From e053b624f5d36669756990743346157be9f68c34 Mon Sep 17 00:00:00 2001 From: Achilles Gaikwad Date: Tue, 7 Jul 2026 11:23:05 -0400 Subject: NFSv4.2: fix nfs4_listxattr size accounting A call to listxattr() with a buffer size of 0 returns the actual size of the buffer needed for a subsequent call. On an NFSv4.2 mount this triggers the following oops: [ 399.768687] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 399.768705] RIP: 0010:_copy_from_pages+0x44/0xe0 [ 399.768722] Call Trace: [ 399.768723] nfs4_xattr_alloc_entry+0x1bf/0x1e0 [ 399.768730] nfs4_xattr_cache_set_list+0x43/0x1f0 [ 399.768731] nfs4_listxattr+0x21f/0x250 [ 399.768733] vfs_listxattr+0x55/0xa0 [ 399.768736] listxattr+0x23/0x160 [ 399.768737] path_listxattrat+0xba/0x1e0 [ 399.768739] do_syscall_64+0xe2/0x680 security_inode_listsecurity() (via the xattr_list_one() helper) now decrements the remaining size even when the buffer pointer is NULL, so in the size-query case, 'left' underflows to a huge size_t value. As a result, nfs4_listxattr_nfs4_user() treats the NULL buffer as a real one, leading to a NULL pointer dereference in _copy_from_pages(). security_inode_listsecurity() does not return the number of bytes it added to the list, so the code derived it as 'size - error - left'. That is also wrong in the size-query case: the generic_listxattr() contribution is only subtracted from 'left' when a buffer is present. Thus, the query result comes up short by exactly that contribution (e.g., "system.nfs4_acl" on a mount with ACL support), and a caller that allocates the returned size gets -ERANGE on the subsequent call. Declare 'left' as ssize_t, use a scratch copy to measure security hook consumption, and only decrement 'left' if a buffer is present. Fixes: f71ece9712b7 ("security,fs,nfs,net: update security_inode_listsecurity() interface") Suggested-by: Paul Moore Signed-off-by: Achilles Gaikwad Reviewed-by: Paul Moore Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7d98e9a98580..5709c6fea85b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -10596,7 +10596,8 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size) { ssize_t error, error2, error3; - size_t left = size; + ssize_t left = size; + ssize_t left2; error = generic_listxattr(dentry, list, left); if (error < 0) @@ -10606,10 +10607,13 @@ static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size) left -= error; } - error2 = security_inode_listsecurity(d_inode(dentry), &list, &left); + left2 = left; + error2 = security_inode_listsecurity(d_inode(dentry), &list, &left2); if (error2 < 0) return error2; - error2 = size - error - left; + error2 = left - left2; + if (list) + left -= error2; error3 = nfs4_listxattr_nfs4_user(d_inode(dentry), list, left); if (error3 < 0) -- cgit v1.2.3 From 66694b5f90f3876fccb87bbd02b453cdc33b3ae4 Mon Sep 17 00:00:00 2001 From: Robby Cai Date: Fri, 24 Jul 2026 18:34:40 +0800 Subject: regulator: fp9931: Fix VPOS/VNEG voltage selector table The VPOSNEG_table[] mapping does not match the FP9931 datasheet. The datasheet defines the VPOS/VNEG voltage mapping as: 00h-04h -> 7.04V (-7.04V) 05h -> 7.26V (-7.26V) 06h -> 7.49V (-7.49V) ... 28h-3Fh -> 15.06V (-15.06V) However, VPOSNEG_table[] has two issues: 1. Selector 0x00~0x04 should all map to 7.04V (5 entries), but the table has 6 entries of 7.04V, causing all subsequent entries to be shifted by one position. 2. Selectors 0x29~0x3F should all clamp to 15.06V (23 entries), but the table has only 41 entries. Any selector value above 0x28 would result in an out-of-bounds table access. Fix both issues by removing the duplicate 7.04V entry and appending the missing 23 clamped 15.06V entries, bringing the table to the correct size of 64 entries (0x00~0x3F). Fixes: 12d821bd13d4 ("regulator: Add FP9931/JD9930 driver") Signed-off-by: Robby Cai Link: https://patch.msgid.link/20260724103441.800522-2-robby.cai@oss.nxp.com Signed-off-by: Mark Brown --- drivers/regulator/fp9931.c | 54 +++++++++++----------------------------------- 1 file changed, 12 insertions(+), 42 deletions(-) diff --git a/drivers/regulator/fp9931.c b/drivers/regulator/fp9931.c index 002b41f53eff..ff743a8b0dfe 100644 --- a/drivers/regulator/fp9931.c +++ b/drivers/regulator/fp9931.c @@ -37,48 +37,18 @@ struct fp9931_data { }; static const unsigned int VPOSNEG_table[] = { - 7040000, - 7040000, - 7040000, - 7040000, - 7040000, - 7040000, - 7260000, - 7490000, - 7710000, - 7930000, - 8150000, - 8380000, - 8600000, - 8820000, - 9040000, - 9270000, - 9490000, - 9710000, - 9940000, - 10160000, - 10380000, - 10600000, - 10830000, - 11050000, - 11270000, - 11490000, - 11720000, - 11940000, - 12160000, - 12380000, - 12610000, - 12830000, - 13050000, - 13280000, - 13500000, - 13720000, - 13940000, - 14170000, - 14390000, - 14610000, - 14830000, - 15060000, + 7040000, 7040000, 7040000, 7040000, 7040000, /* 00h-04h */ + 7260000, 7490000, 7710000, 7930000, 8150000, 8380000, /* 05h-0Ah */ + 8600000, 8820000, 9040000, 9270000, 9490000, 9710000, /* 0Bh-10h */ + 9940000, 10160000, 10380000, 10600000, 10830000, 11050000, /* 11h-16h */ + 11270000, 11490000, 11720000, 11940000, 12160000, 12380000, /* 17h-1Ch */ + 12610000, 12830000, 13050000, 13280000, 13500000, 13720000, /* 1Dh-22h */ + 13940000, 14170000, 14390000, 14610000, 14830000, 15060000, /* 23h-28h */ + /* 29h-3Fh: clamped to 15.06V per datasheet */ + 15060000, 15060000, 15060000, 15060000, 15060000, 15060000, + 15060000, 15060000, 15060000, 15060000, 15060000, 15060000, + 15060000, 15060000, 15060000, 15060000, 15060000, 15060000, + 15060000, 15060000, 15060000, 15060000, 15060000, }; static const struct hwmon_channel_info *fp9931_info[] = { -- cgit v1.2.3 From 8d51e0fd3e698919d2adeff71936377f0c0d4aa0 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Thu, 23 Jul 2026 00:42:56 -0700 Subject: accel/amxdna: Fix page-insertion errors in amdxdna_insert_pages() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two error paths in amdxdna_insert_pages() called vma->vm_ops->close(vma) before returning an error code to the caller. This is incorrect: amdxdna_gem_obj_mmap() registers an HMM interval notifier before calling amdxdna_insert_pages(), and on a hard error it jumps to hmm_unreg to undo that registration. Calling vm_ops->close() manually — which drops the shmem pages_pin_count and the GEM object reference that backs the VMA — before the mmap syscall has even returned causes those resources to be released while the VMA is still alive. The kernel VMA teardown will call vm_ops->close() a second time when the process later unmaps the range, producing a reference count underflow. Replace both hard-error returns with a deferred-fault approach that keeps the VMA alive and retries page insertion through the HMM range-fault path. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260723074256.2435143-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_gem.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 4628a2787265..aec69d5f463e 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -436,6 +436,23 @@ static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj) amdxdna_gem_destroy_obj(abo); } +static void amdxdna_mark_mapp_invalid(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_write(&xdna->notifier_lock); + abo->mem.map_invalid = true; + list_for_each_entry(mapp, &abo->mem.umap_list, node) { + if (compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) { + mapp->invalid = true; + break; + } + } + up_write(&xdna->notifier_lock); +} + static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, struct vm_area_struct *vma) { @@ -457,8 +474,7 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, &num_pages); if (ret) { XDNA_ERR(xdna, "Failed insert pages %d", ret); - vma->vm_ops->close(vma); - return ret; + amdxdna_mark_mapp_invalid(abo, vma); } return 0; @@ -478,9 +494,9 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, fault_ret = handle_mm_fault(vma, vma->vm_start + offset, FAULT_FLAG_WRITE, NULL); if (fault_ret & VM_FAULT_ERROR) { - vma->vm_ops->close(vma); XDNA_ERR(xdna, "Fault in page failed"); - return -EFAULT; + amdxdna_mark_mapp_invalid(abo, vma); + break; } offset += PAGE_SIZE; -- cgit v1.2.3 From a15970d916b39acc7c60a0a99c27a6e378690aa9 Mon Sep 17 00:00:00 2001 From: Eduard Zingerman Date: Wed, 29 Jul 2026 15:18:27 +0000 Subject: bpf: Simplify sanitize_err() signature The sanitize_err() function is called when: - ptr += scalar - scalar += ptr - scalar += scalar ALU operations are processed. This commit drops offset and pointer registers parameters from its signature to simplify the follow-up changes for 'scalar += ptr' case. regs[src].type is safe to access, as it is not mutated by the callers. Signed-off-by: Yiyang Chen Acked-by: Shung-Hsi Yu Link: https://patch.msgid.link/20260729-c3-035-public-bpf-v4-v4-1-8ee297e2346b@mails.tsinghua.edu.cn Signed-off-by: Eduard Zingerman --- kernel/bpf/verifier.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7aa47342dc65..9792d6622ffd 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13557,23 +13557,21 @@ static void sanitize_mark_insn_seen(struct bpf_verifier_env *env) env->insn_aux_data[env->insn_idx].seen = env->pass_cnt; } -static int sanitize_err(struct bpf_verifier_env *env, - const struct bpf_insn *insn, int reason, - const struct bpf_reg_state *off_reg, - const struct bpf_reg_state *dst_reg) +static int sanitize_err(struct bpf_verifier_env *env, const struct bpf_insn *insn, int reason) { static const char *err = "pointer arithmetic with it prohibited for !root"; const char *op = BPF_OP(insn->code) == BPF_ADD ? "add" : "sub"; u32 dst = insn->dst_reg, src = insn->src_reg; + struct bpf_reg_state *regs = cur_regs(env); switch (reason) { case REASON_BOUNDS: verbose(env, "R%d has unknown scalar with mixed signed bounds, %s\n", - off_reg == dst_reg ? dst : src, err); + regs[src].type == SCALAR_VALUE ? src : dst, err); break; case REASON_TYPE: verbose(env, "R%d has pointer with unsupported alu operation, %s\n", - off_reg == dst_reg ? src : dst, err); + regs[src].type == SCALAR_VALUE ? dst : src, err); break; case REASON_PATHS: verbose(env, "R%d tried to %s from different maps, paths or scalars, %s\n", @@ -13762,7 +13760,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg, &info, false); if (ret < 0) - return sanitize_err(env, insn, ret, off_reg, dst_reg); + return sanitize_err(env, insn, ret); } switch (opcode) { @@ -13855,7 +13853,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, return -EFAULT; } if (ret < 0) - return sanitize_err(env, insn, ret, off_reg, dst_reg); + return sanitize_err(env, insn, ret); } return 0; @@ -14607,7 +14605,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, if (sanitize_needed(opcode)) { ret = sanitize_val_alu(env, insn); if (ret < 0) - return sanitize_err(env, insn, ret, NULL, NULL); + return sanitize_err(env, insn, ret); } /* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops. -- cgit v1.2.3 From a4c6f804b44c5c790269b25e0e61cf4e9f117c86 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Wed, 29 Jul 2026 15:18:28 +0000 Subject: bpf: Preserve pointer state for commuted arithmetic When scalar += pointer is handled in adjust_ptr_min_max_vals(), the destination register inherits the pointer state from the source pointer. Copying only selected fields is fragile because pointer provenance is tracked by several bpf_reg_state fields. Use the caller's temporary offset register to preserve the scalar operand while replacing the destination with the full pointer state. This preserves the frame number for PTR_TO_STACK registers and keeps parent identity fields consistent. Fixes: f4d7e40a5b71 ("bpf: introduce function calls (verification)") Signed-off-by: Yiyang Chen Tested-by: Daniel Wade Acked-by: Shung-Hsi Yu Link: https://patch.msgid.link/20260729-c3-035-public-bpf-v4-v4-2-8ee297e2346b@mails.tsinghua.edu.cn Signed-off-by: Eduard Zingerman --- kernel/bpf/verifier.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9792d6622ffd..cdb61fab8435 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13743,11 +13743,12 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, return -EACCES; } - /* In case of 'scalar += pointer', dst_reg inherits pointer type and id. - * The id may be overwritten later if we create a new variable offset. + /* For 'scalar += pointer', dst_reg inherits the complete pointer + * register state. Individual fields may be adjusted later by pointer + * arithmetic. Callers guarantee that below does not overwrite off_reg. */ - dst_reg->type = ptr_reg->type; - dst_reg->id = ptr_reg->id; + if (dst_reg != ptr_reg) + *dst_reg = *ptr_reg; if (!check_reg_sane_offset_scalar(env, off_reg, ptr_reg->type) || !check_reg_sane_offset_ptr(env, ptr_reg, ptr_reg->type)) @@ -13790,7 +13791,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, } break; case BPF_SUB: - if (dst_reg == off_reg) { + if (dst_reg != ptr_reg) { /* scalar -= pointer. Creates an unknown scalar */ verbose(env, "R%d tried to subtract pointer from scalar\n", dst); @@ -14808,8 +14809,8 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, err = mark_chain_precision(env, insn->dst_reg); if (err) return err; - return adjust_ptr_min_max_vals(env, insn, - src_reg, dst_reg); + off_reg = *dst_reg; + return adjust_ptr_min_max_vals(env, insn, src_reg, &off_reg); } } else if (ptr_reg) { /* pointer += scalar */ -- cgit v1.2.3 From cdf19b1b3c01791de074ce282089131026f52261 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Wed, 29 Jul 2026 15:18:29 +0000 Subject: bpf: Propagate untrusted pointer state in commuted arithmetic The untrusted PTR_TO_MEM early return skips pointer offset tracking because accesses go through probe-read handling. Moving it after full pointer-state propagation ensures scalar += untrusted_pointer leaves the destination as PTR_TO_MEM instead of an unrelated scalar. Fixes: f2362a57aeff ("bpf: allow void* cast using bpf_rdonly_cast()") Signed-off-by: Yiyang Chen Tested-by: Daniel Wade Link: https://patch.msgid.link/20260729-c3-035-public-bpf-v4-v4-3-8ee297e2346b@mails.tsinghua.edu.cn Signed-off-by: Eduard Zingerman --- kernel/bpf/verifier.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index cdb61fab8435..fdc5fbb1f78c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13707,13 +13707,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, return -EACCES; } - /* - * Accesses to untrusted PTR_TO_MEM are done through probe - * instructions, hence no need to track offsets. - */ - if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) - return 0; - switch (base_type(ptr_reg->type)) { case PTR_TO_CTX: case PTR_TO_MAP_VALUE: @@ -13750,6 +13743,13 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, if (dst_reg != ptr_reg) *dst_reg = *ptr_reg; + /* + * Accesses to untrusted PTR_TO_MEM are done through probe + * instructions, hence no need to track offsets. + */ + if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) + return 0; + if (!check_reg_sane_offset_scalar(env, off_reg, ptr_reg->type) || !check_reg_sane_offset_ptr(env, ptr_reg, ptr_reg->type)) return -EINVAL; -- cgit v1.2.3 From 21596761ff370f05460ad0f9078786082bbfa87d Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Wed, 29 Jul 2026 15:18:30 +0000 Subject: selftests/bpf: Cover commuted pointer state propagation Add verifier coverage for the three cases affected by preserving the full pointer state across scalar += pointer: stack frame number inheritance, readonly-untrusted memory access, and dynptr data-slice invalidation. Signed-off-by: Yiyang Chen Tested-by: Daniel Wade Acked-by: Eduard Zingerman Link: https://patch.msgid.link/20260729-c3-035-public-bpf-v4-v4-4-8ee297e2346b@mails.tsinghua.edu.cn Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/progs/dynptr_fail.c | 30 ++++++++++++++++ .../selftests/bpf/progs/mem_rdonly_untrusted.c | 17 +++++++++ .../selftests/bpf/progs/verifier_basic_stack.c | 41 ++++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c index 344fb2aa0813..29c6361d8820 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_fail.c +++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c @@ -1635,6 +1635,36 @@ static int callback(__u32 index, void *data) return 0; } +/* A commuted add should preserve the parent id of a dynptr data slice. */ +SEC("?raw_tp") +__failure __msg("invalid mem access 'scalar'") +int dynptr_slice_commuted_invalidate(void *ctx) +{ + struct bpf_dynptr ptr; + __u32 *slice, *derived; + + bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(__u32), 0, &ptr); + + slice = bpf_dynptr_data(&ptr, 0, sizeof(__u32)); + if (!slice) + goto done; + + asm volatile ("%[dst] = 0;" + "%[dst] += %[src];" + "%[src] = 0;" + : [dst]"=&r"(derived), [src]"+r"(slice) + : + : "memory"); + + bpf_ringbuf_discard_dynptr(&ptr, 0); + val = *derived; + return 0; + +done: + bpf_ringbuf_discard_dynptr(&ptr, 0); + return 0; +} + /* If the dynptr is written into in a callback function, its data * slices should be invalidated as well. */ diff --git a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c index 5b4453747c23..f166fff8f217 100644 --- a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c +++ b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c @@ -226,4 +226,21 @@ int null_check(void *ctx) return 0; } +SEC("socket") +__success +__retval(1) +int ldx_is_ok_commuted_addr(void *ctx) +{ + int v, *p, *derived; + + v = 1; + p = bpf_rdonly_cast(&v, 0); + asm volatile ("%[dst] = 0;" + "%[dst] += %[src];" + : [dst]"=&r"(derived) + : [src]"r"(p) + : "memory"); + return *derived; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_basic_stack.c b/tools/testing/selftests/bpf/progs/verifier_basic_stack.c index fb62e09f2114..d3df7a9f1d8c 100644 --- a/tools/testing/selftests/bpf/progs/verifier_basic_stack.c +++ b/tools/testing/selftests/bpf/progs/verifier_basic_stack.c @@ -97,4 +97,45 @@ __naked void misaligned_read_from_stack(void) " ::: __clobber_all); } +SEC("socket") +__description("stack pointer arithmetic preserves frame number") +__failure __msg("R7 invalid mem access 'scalar'") +__naked void stack_ptr_arith_preserves_frameno(void) +{ + asm volatile ("\ + r3 = 0; \ + *(u64 *)(r10 - 8) = r3; \ + r1 = %[map_hash_8b] ll; \ + r2 = r10; \ + r2 += -8; \ + call %[bpf_map_lookup_elem]; \ + if r0 != 0 goto +2; \ + r0 = 0; \ + exit; \ + r1 = r0; \ + r2 = 0; \ + r3 = 0; \ + call stack_ptr_arith_preserves_frameno_subprog;\ + r0 = 0; \ + exit; \ + ": + : __imm(bpf_map_lookup_elem), + __imm_addr(map_hash_8b) + : __clobber_all); +} + +static __used __naked void stack_ptr_arith_preserves_frameno_subprog(void) +{ + asm volatile ("\ + *(u64 *)(r10 - 8) = r1; \ + r6 = -8; \ + r6 += r10; \ + *(u64 *)(r6 + 0) = r2; \ + r7 = *(u64 *)(r10 - 8); \ + *(u64 *)(r7 + 0) = r3; \ + r0 = 0; \ + exit; \ + "::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; -- cgit v1.2.3 From 5cb5c2f4a8373eb4c8fdbfdc857bb28f28f71fe0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Wed, 22 Jul 2026 23:56:46 +0000 Subject: mshv: Use kfree_rcu in mshv_portid_free mshv_portid_free() uses synchronize_rcu() followed by kfree() to reclaim port table entries. This blocks the caller until a full RCU grace period elapses, which is unnecessary since the same module already uses the non-blocking kfree_rcu() pattern in mshv_port_table_fini(). Replace with kfree_rcu() to avoid the blocking wait and keep the reclamation strategy consistent across the file. Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_portid_table.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c index 6f59b3e37624..0d632507ed21 100644 --- a/drivers/hv/mshv_portid_table.c +++ b/drivers/hv/mshv_portid_table.c @@ -62,8 +62,7 @@ mshv_portid_free(int port_id) WARN_ON(!info); idr_unlock(&port_table_idr); - synchronize_rcu(); - kfree(info); + kfree_rcu(info, portbl_rcu); } int -- cgit v1.2.3 From 0762262ac3e70f65b3bb843fe892f8bac1562d08 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:15 +0000 Subject: mshv: Fix race in mshv_irqfd_deassign mshv_irqfd_deactivate() and the hlist traversal of pt_irqfds_list require pt->pt_irqfds_lock to be held, but mshv_irqfd_deassign() omits it. This races with the EPOLLHUP path in mshv_irqfd_wakeup(), which does take the lock before calling mshv_irqfd_deactivate(). Additionally, mshv_irqfd_deactivate() uses hlist_del() which poisons the node pointers rather than resetting them. Since mshv_irqfd_is_active() relies on hlist_unhashed() (checks pprev == NULL), a poisoned node still appears active. If a concurrent path calls mshv_irqfd_deactivate() again on the same irqfd, the guard fails to prevent a double hlist_del() on poisoned pointers. Fix both issues: - Add the missing spin_lock_irq/spin_unlock_irq around the list traversal in mshv_irqfd_deassign(), matching mshv_irqfd_release(). - Use hlist_del_init() instead of hlist_del() so the node is properly marked as unhashed after removal, making the is_active guard reliable. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_eventfd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 90959f639dc3..5995a62aff8d 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -284,7 +284,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd) if (!mshv_irqfd_is_active(irqfd)) return; - hlist_del(&irqfd->irqfd_hnode); + hlist_del_init(&irqfd->irqfd_hnode); queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown); } @@ -541,13 +541,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt, if (IS_ERR(eventfd)) return PTR_ERR(eventfd); + spin_lock_irq(&pt->pt_irqfds_lock); hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list, irqfd_hnode) { if (irqfd->irqfd_eventfd_ctx == eventfd && irqfd->irqfd_irqnum == args->gsi) - mshv_irqfd_deactivate(irqfd); } + spin_unlock_irq(&pt->pt_irqfds_lock); eventfd_ctx_put(eventfd); -- cgit v1.2.3 From 0289a67cd70bf9d3807e289f4efd643e16a6c6b4 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:43 +0000 Subject: mshv: Fix level-triggered check on uninitialized data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In mshv_irqfd_assign(), the level-triggered validation for resample irqfds checks irqfd_lapic_irq.lapic_control.level_triggered before mshv_irqfd_update() has populated the field. Since the irqfd struct is zero-allocated, level_triggered is always 0 at that point, causing the check to always reject resample irqfds with -EINVAL. This makes level-triggered interrupt resampling — used to avoid interrupt storms with assigned devices — completely non-functional. Move the check after the mshv_irqfd_update() call, which resolves the IRQ routing entry and populates irqfd_lapic_irq with the actual trigger mode. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_eventfd.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 5995a62aff8d..047e5bd43238 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -473,6 +473,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); spin_lock_irq(&pt->pt_irqfds_lock); + ret = 0; + hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { + if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) + continue; + /* This fd is used for another irq already. */ + ret = -EBUSY; + spin_unlock_irq(&pt->pt_irqfds_lock); + goto fail; + } + + idx = srcu_read_lock(&pt->pt_irq_srcu); + mshv_irqfd_update(pt, irqfd); + #if IS_ENABLED(CONFIG_X86) if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { @@ -481,22 +494,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, * Otherwise return with failure */ spin_unlock_irq(&pt->pt_irqfds_lock); + srcu_read_unlock(&pt->pt_irq_srcu, idx); ret = -EINVAL; goto fail; } #endif - ret = 0; - hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { - if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) - continue; - /* This fd is used for another irq already. */ - ret = -EBUSY; - spin_unlock_irq(&pt->pt_irqfds_lock); - goto fail; - } - idx = srcu_read_lock(&pt->pt_irq_srcu); - mshv_irqfd_update(pt, irqfd); hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list); spin_unlock_irq(&pt->pt_irqfds_lock); -- cgit v1.2.3 From f546be6a19d24d02be576d8617cb26c7acb61594 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:44:37 +0000 Subject: mshv: Fix missing error code on VP allocation failure In mshv_partition_ioctl_create_vp(), when kzalloc for the VP struct fails, the code jumps to the cleanup path without setting ret. At that point ret is 0 from the preceding successful mshv_vp_stats_map() call, so the function returns success to userspace despite having failed to create the VP. No fd is installed and no VP is registered in pt_vp_array, but userspace has no way to know the operation failed. Set ret to -ENOMEM before jumping to the cleanup path. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 146726cc4e9b..644f9b10cbba 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1117,8 +1117,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, goto unmap_ghcb_page; vp = kzalloc_obj(*vp); - if (!vp) + if (!vp) { + ret = -ENOMEM; goto unmap_stats_pages; + } vp->vp_partition = mshv_partition_get(partition); if (!vp->vp_partition) { -- cgit v1.2.3 From b098dc869219c15dc49bf9cf63fb5fc1481d3373 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 23 Jul 2026 00:28:11 +0000 Subject: mshv: Order pt_vp_array publish against irqfd assertion path mshv_partition_ioctl_create_vp() initialises a VP struct (allocations, mutex_init, init_waitqueue_head, page mappings) and then publishes the pointer into partition->pt_vp_array. Several ISR paths read this array locklessly: the intercept ISR, the two scheduler ISRs, and mshv_try_assert_irq_fast() on the irqfd fast path. Of these, only mshv_try_assert_irq_fast() can structurally race the publish. It runs from an eventfd waker without holding pt_mutex, and MSHV_IRQFD does not require the target lapic_apic_id (== vp_index) to refer to an existing VP at registration time. A user can therefore register an irqfd targeting a yet-to-be-created VP, then trigger mshv_try_assert_irq_fast() concurrently with MSHV_CREATE_VP for the same index. On weakly-ordered architectures the reader can observe a non-NULL pointer in pt_vp_array before the initialising stores to the VP struct become visible, leading to use of partially-initialised fields (e.g. vp_register_page). The other ISR readers cannot reach this race: the hypervisor will not generate intercept or scheduler messages for a VP that has never been told to run, and the user can only call MSHV_RUN_VP on the VP fd returned by MSHV_CREATE_VP, which by construction is returned after the publish. Leave those readers as plain loads. Use smp_store_release() in mshv_partition_ioctl_create_vp() to publish the pointer, and pair it with smp_load_acquire() in mshv_try_assert_irq_fast(). On x86 these compile to plain accesses under TSO; on ARM64 they emit one-instruction acquire/release barriers, acceptable on this fast path. The destroy-side path (destroy_partition() clearing pt_vp_array[i] to NULL after kfree(vp)) has a separate ordering and lifetime concern that is out of scope here. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_eventfd.c | 9 ++++++++- drivers/hv/mshv_root_main.c | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 047e5bd43238..06aef99c8298 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd) return -EOPNOTSUPP; #endif - vp = partition->pt_vp_array[irq->lapic_apic_id]; + /* + * Pairs with smp_store_release() in mshv_partition_ioctl_create_vp(). + * MSHV_IRQFD does not require the target lapic_apic_id to refer to an + * existing VP, so this read can race a concurrent VP creation; the + * acquire ensures that a non-NULL pointer implies the VP's + * initialising stores are visible. + */ + vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]); if (!vp->vp_register_page) return -EOPNOTSUPP; diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 644f9b10cbba..8a15448e2ace 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1157,7 +1157,13 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, /* already exclusive with the partition mutex for all ioctls */ partition->pt_vp_count++; - partition->pt_vp_array[args.vp_index] = vp; + /* + * Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which + * can run concurrently from an irqfd waker without holding pt_mutex. + * The release ensures the VP's initialising stores are visible to any + * reader that observes a non-NULL pointer in pt_vp_array. + */ + smp_store_release(&partition->pt_vp_array[args.vp_index], vp); goto out; -- cgit v1.2.3 From 92d0593128023cf93ae61b7728dcc3062f8d514f Mon Sep 17 00:00:00 2001 From: Hardik Garg Date: Fri, 17 Jul 2026 00:18:37 +0000 Subject: Drivers: hv: vmbus: add VTL2 redirect connection ID VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID, while protocol version 5.0 and newer normally use VMBUS_MESSAGE_CONNECTION_ID_4. For a VTL2 kernel using VMBus protocol 5.0 or newer, the host may expect INITIATE_CONTACT on either the redirect connection ID or VMBUS_MESSAGE_CONNECTION_ID_4. There is no capability indication that identifies which ID is active, so the driver must determine it at runtime. During VMBus negotiation, the redirect ID is tried first because it is used by VTL2 configurations with VMBus redirection enabled. If the redirect ID is unavailable, the host rejects it synchronously with HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to the standard ID. Return a distinct error for an invalid Initiate Contact connection ID so this fallback does not mask other post-message failures or protocol-version rejections. Preserve the existing connection ID selection for older protocol versions or when running below VTL2. Signed-off-by: Hardik Garg Reviewed-by: Tianyu Lan Reviewed-by: Saurabh Sengar Reviewed-by: Naman Jain Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/connection.c | 47 ++++++++++++++++++++++++++++------------------- drivers/hv/hyperv_vmbus.h | 2 ++ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index b5b322ce16df..0fd50d4cb573 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -72,7 +72,8 @@ module_param(max_version, uint, S_IRUGO); MODULE_PARM_DESC(max_version, "Maximal VMBus protocol version which can be negotiated"); -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo, + u32 version, u32 connection_id) { int ret = 0; struct vmbus_channel_initiate_contact *msg; @@ -87,20 +88,20 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) msg->vmbus_version_requested = version; /* - * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must - * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message, - * and for subsequent messages, we must use the Message Connection ID - * field in the host-returned Version Response Message. And, with - * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we - * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for - * compatibility. + * For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the + * caller-supplied connection_id for the Initiate Contact message so + * the caller can implement the required retry scheme. For subsequent + * messages, use the Message Connection ID field in the host-returned + * Version Response message. With VERSION_WIN10_V5 and higher, we don't + * use msg->interrupt_page, but tell the host explicitly that we still + * use VMBUS_MESSAGE_SINT(2) for compatibility. * * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1). */ if (version >= VERSION_WIN10_V5) { msg->msg_sint = VMBUS_MESSAGE_SINT; msg->msg_vtl = ms_hyperv.vtl; - vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4; + vmbus_connection.msg_conn_id = connection_id; } else { msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID; @@ -165,6 +166,22 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) return ret; } +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) +{ + int ret; + + /* Try the redirect ID first for VTL2 with VMBus protocol 5.0+. */ + if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl == 2) { + ret = vmbus_try_connection_id(msginfo, version, + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT); + if (ret != -ENXIO) + return ret; + } + + return vmbus_try_connection_id(msginfo, version, + VMBUS_MESSAGE_CONNECTION_ID_4); +} + /* * vmbus_connect - Sends a connect request on the partition service connection */ @@ -457,18 +474,10 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) switch (ret) { case HV_STATUS_INVALID_CONNECTION_ID: - /* - * See vmbus_negotiate_version(): VMBus protocol 5.0 - * and higher require that we must use - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate - * Contact message, but on old hosts that only - * support VMBus protocol 4.0 or lower, here we get - * HV_STATUS_INVALID_CONNECTION_ID and we should - * return an error immediately without retrying. - */ + /* Allow INITIATE_CONTACT to try another connection ID. */ hdr = buffer; if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT) - return -EINVAL; + return -ENXIO; /* * We could get this if we send messages too * frequently. diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index eb8bdd8bb1f5..33923621a5a3 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -110,6 +110,8 @@ struct hv_input_post_message { enum { VMBUS_MESSAGE_CONNECTION_ID = 1, VMBUS_MESSAGE_CONNECTION_ID_4 = 4, + /* VTL2 redirect connection ID for INITIATE_CONTACT. */ + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074, VMBUS_MESSAGE_PORT_ID = 1, VMBUS_EVENT_CONNECTION_ID = 2, VMBUS_EVENT_PORT_ID = 2, -- cgit v1.2.3 From 72e3b0311aa90568a4b42a64445d1d3e1dc5a34d Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:44:32 +0000 Subject: mshv: Publish VP to pt_vp_array before installing the file descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mshv_partition_ioctl_create_vp() called anon_inode_getfd() before publishing the new VP into partition->pt_vp_array. anon_inode_getfd() includes fd_install(), so the fd was live in current->files before the publish ran. A concurrent MSHV_RUN_VP ioctl on that fd does not serialise against the in-progress MSHV_CREATE_VP — it takes vp->vp_mutex, not the partition mutex. Once the VP starts running and traps, mshv_intercept_isr() can look up partition->pt_vp_array[vp_index] and observe NULL, silently dropping the intercept message. Split the fd creation: reserve an fd with get_unused_fd_flags(), create the file with anon_inode_getfile(), publish the VP via smp_store_release(), and finally call fd_install() as the userspace-visibility commit point. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 8a15448e2ace..cc2cfce2aefd 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1072,6 +1072,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, struct mshv_vp *vp; struct page *intercept_msg_page, *register_page, *ghcb_page; struct hv_stats_page *stats_pages[2]; + struct file *file; + int fd; long ret; if (copy_from_user(&args, arg, sizeof(args))) @@ -1146,14 +1148,18 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, if (ret) goto put_partition; - /* - * Keep anon_inode_getfd last: it installs fd in the file struct and - * thus makes the state accessible in user space. - */ - ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp, - O_RDWR | O_CLOEXEC); - if (ret < 0) + fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC); + if (fd < 0) { + ret = fd; goto remove_debugfs_vp; + } + + file = anon_inode_getfile("mshv_vp", &mshv_vp_fops, vp, + O_RDWR | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto put_unused_vp_fd; + } /* already exclusive with the partition mutex for all ioctls */ partition->pt_vp_count++; @@ -1165,8 +1171,17 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, */ smp_store_release(&partition->pt_vp_array[args.vp_index], vp); + /* + * fd_install() is the userspace-visibility commit point. Must be the + * last operation that can fail or be observed. + */ + fd_install(fd, file); + ret = fd; + goto out; +put_unused_vp_fd: + put_unused_fd(fd); remove_debugfs_vp: mshv_debugfs_vp_remove(vp); put_partition: -- cgit v1.2.3 From 22dfdc17ceadd2783a609947af621a7f697b7765 Mon Sep 17 00:00:00 2001 From: Yi Xie Date: Thu, 9 Jul 2026 10:19:47 +0800 Subject: mshv_vtl: bounds-check cpu index in vtl mmap fault handler cpu is taken from pgoff & 0xffff. cpu_online() does not reject cpu >= nr_cpu_ids, and per_cpu_ptr() can then walk off __per_cpu_offset. Signed-off-by: Yi Xie Reviewed-by: Naman Jain Signed-off-by: Wei Liu --- drivers/hv/mshv_vtl_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index 5ba1efb3b4e7..6e3c11c68171 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -802,7 +802,7 @@ static vm_fault_t mshv_vtl_fault(struct vm_fault *vmf) int cpu = vmf->pgoff & MSHV_PG_OFF_CPU_MASK; int real_off = vmf->pgoff >> MSHV_REAL_OFF_SHIFT; - if (!cpu_online(cpu)) + if (cpu >= nr_cpu_ids || !cpu_online(cpu)) return VM_FAULT_SIGBUS; /* * CPU Hotplug is not supported in VTL2 in OpenHCL, where this kernel driver exists. -- cgit v1.2.3 From 976245094925bab9bc39366b2e9ab44ffcde61d0 Mon Sep 17 00:00:00 2001 From: Mahanta Jambigi Date: Wed, 29 Jul 2026 15:01:53 +0200 Subject: net/smc: fix qentry overwrite for CONFIRM_LINK and ADD_LINK_CONT in smc_llc_event_handler() The SMC_LLC_CONFIRM_LINK / SMC_LLC_ADD_LINK_CONT branch in smc_llc_event_handler() stores an incoming qentry into the local LLC flow without first checking whether a qentry is already pending. If a malicious or buggy peer sends a second CONFIRM_LINK or ADD_LINK_CONT request while a flow is active and flow->qentry is already set, smc_llc_flow_qentry_set() overwrites the pointer without freeing the previous allocation, leaking one kmalloc-96 object per spurious message. The sibling SMC_LLC_DELETE_LINK branch already has the correct !flow->qentry guard. Apply the same guard to the CONFIRM_LINK/ADD_LINK_CONT branch so that a duplicate message when qentry is already occupied falls through to break and is freed by the kfree(qentry) at the out: label, rather than silently leaking the existing allocation. The response direction (smc_llc_rx_response()) is unaffected: it already guards with flow->qentry at the equivalent site and drops duplicate responses correctly. Fixes: 0fb0b02bd6fd ("net/smc: adapt SMC client code to use the LLC flow") Signed-off-by: Mahanta Jambigi Reviewed-by: Hidayath Khan Reviewed-by: Sidraya Jayagond Reviewed-by: Dust Li Link: https://patch.msgid.link/20260729130153.970800-1-mjambigi@linux.ibm.com Signed-off-by: Jakub Kicinski --- net/smc/smc_llc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c index 954b2ff1815c..aa6d83af55ed 100644 --- a/net/smc/smc_llc.c +++ b/net/smc/smc_llc.c @@ -1927,7 +1927,8 @@ static void smc_llc_event_handler(struct smc_llc_qentry *qentry) return; case SMC_LLC_CONFIRM_LINK: case SMC_LLC_ADD_LINK_CONT: - if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { + if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE && + !lgr->llc_flow_lcl.qentry) { /* a flow is waiting for this message */ smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); wake_up(&lgr->llc_msg_waiter); -- cgit v1.2.3 From 63488dba65ef91373ef616575b32eb0eb21459f4 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Wed, 29 Jul 2026 22:19:41 +0900 Subject: net: bridge: mrp: fix uninitialised bytes on the wire br_mrp_alloc_test_skb() builds MRP test frames on an skb from dev_alloc_skb(), which does not clear the linear data area. On the MRA ring-role branch the sub-option TLV header is appended with sub_tlv = skb_put(skb, sizeof(*sub_tlv)); sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR; so sub_tlv->length is never written, and the two trailing alignment bytes are appended with a bare skb_put() that does not clear them either. The neighbouring oui and sub_opt regions are explicitly zeroed, so three uninitialised bytes are left in every MRA MRP_Test frame that goes out. Put the sub-option TLV header and the alignment padding in a single skb_put_zero(), which clears both. The AUTO_MGR sub-TLV carries no payload, so the zeroed length field is already the value it should have. Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") Suggested-by: Nikolay Aleksandrov Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260729131941.10254-1-baul.lee@xbow.com Signed-off-by: Jakub Kicinski --- net/bridge/br_mrp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index 179d2470b724..ef16d0703924 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -224,11 +224,9 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, sub_opt = skb_put(skb, sizeof(*sub_opt)); memset(sub_opt, 0x0, sizeof(*sub_opt)); - sub_tlv = skb_put(skb, sizeof(*sub_tlv)); - sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR; - /* 32 bit alligment shall be ensured therefore add 2 bytes */ - skb_put(skb, MRP_OPT_PADDING); + sub_tlv = skb_put_zero(skb, sizeof(*sub_tlv) + MRP_OPT_PADDING); + sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR; } br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0); -- cgit v1.2.3 From 260b20d9b78bf002f89088fb62d60e8dee98f6f8 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 31 Jul 2026 23:16:46 +0900 Subject: ring-buffer: Fix subbuf_ids memory leak in rb_allocate_cpu_buffer() error path In rb_allocate_cpu_buffer(), cpu_buffer->subbuf_ids is allocated using kcalloc() when buffer->remote is non-NULL. If a subsequent page allocation fails (e.g., ring_buffer_desc_page() returns NULL or rb_allocate_pages() fails), execution jumps to fail_free_reader. While __free(kfree) automatically frees the outer cpu_buffer structure at scope exit, kfree(cpu_buffer) does not recursively free nested heap pointers such as cpu_buffer->subbuf_ids, resulting in a memory leak. Fix this by explicitly freeing cpu_buffer->subbuf_ids in the fail_free_reader error unwinding path when cpu_buffer->remote is set. Link: https://patch.msgid.link/178550740672.380917.6067449683620196150.stgit@devnote2 Fixes: 2e67fabd8b77 ("ring-buffer: Introduce ring-buffer remotes") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Reviewed-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 78d3875a47a5..8e2485bb3aa8 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2599,6 +2599,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) return_ptr(cpu_buffer); fail_free_reader: + kfree(cpu_buffer->subbuf_ids); free_buffer_page(cpu_buffer->reader_page); return NULL; -- cgit v1.2.3 From 1ba381759e45d5d0442452cfa5c42e836191a568 Mon Sep 17 00:00:00 2001 From: Dawid Wróbel Date: Thu, 30 Jul 2026 12:58:12 +0200 Subject: ASoC: codecs: lpass-tx-macro: Fix enum kcontrol accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "DEC0 MODE" to "DEC7 MODE" controls are enumerated, but tx_macro_dec_mode_get() and tx_macro_dec_mode_put() access their value through ucontrol->value.integer.value[0] (a long) instead of ucontrol->value.enumerated.item[0] (an unsigned int). This same pattern was fixed in the sibling drivers by commit bcfe5f76cc40 ("ASoC: codecs: rx-macro: fix accessing array out of bounds for enum type") and commit 0ea5eff7c606 ("ASoC: codecs: va-macro: fix accessing array out of bounds for enum type"), but tx-macro was missed. On 64-bit kernels built with CONFIG_SND_CTL_DEBUG, the elem value sanity check catches the 4 bytes written past the enumerated item and every read of these controls fails with -EINVAL: snd-sm8250 sound: control 2:0:0:DEC0 MODE:0: access overflow Fixes: c39667ddcfc5 ("ASoC: codecs: lpass-tx-macro: add support for lpass tx macro") Assisted-by: Claude:claude-fable-5 Cc: stable@vger.kernel.org Signed-off-by: Dawid Wróbel Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730-worktree-lpass-tx-macro-enum-fix-v2-1-6d091c736116@dawidwrobel.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index f7d168f557dd..0cbf50647ff5 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1075,7 +1075,7 @@ static int tx_macro_dec_mode_get(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int path = e->shift_l; - ucontrol->value.integer.value[0] = tx->dec_mode[path]; + ucontrol->value.enumerated.item[0] = tx->dec_mode[path]; return 0; } @@ -1084,7 +1084,7 @@ static int tx_macro_dec_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int path = e->shift_l; struct tx_macro *tx = snd_soc_component_get_drvdata(component); -- cgit v1.2.3 From 56f24311fd5607588a47e44675195a9efb200f29 Mon Sep 17 00:00:00 2001 From: Dawid Wróbel Date: Thu, 30 Jul 2026 12:58:13 +0200 Subject: ASoC: codecs: lpass-wsa-macro: Fix enum kcontrol accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EAR SPKR PA Gain" and the four "WSA RX* Mux" controls are enumerated, but their get and put callbacks access the value through ucontrol->value.integer.value[0] (a long) instead of ucontrol->value.enumerated.item[0] (an unsigned int). This same pattern was fixed in the sibling drivers by commit bcfe5f76cc40 ("ASoC: codecs: rx-macro: fix accessing array out of bounds for enum type") and commit 0ea5eff7c606 ("ASoC: codecs: va-macro: fix accessing array out of bounds for enum type"), but wsa-macro was missed. On 64-bit kernels with CONFIG_SND_CTL_DEBUG this trips the elem value sanity check and every read of these controls fails with -EINVAL. Fixes: 809bcbcecebf ("ASoC: codecs: lpass-wsa-macro: Add support to WSA Macro") Fixes: 2c4066e5d428 ("ASoC: codecs: lpass-wsa-macro: add dapm widgets and route") Assisted-by: Claude:claude-fable-5 Cc: stable@vger.kernel.org Signed-off-by: Dawid Wróbel Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260730-worktree-lpass-tx-macro-enum-fix-v2-2-6d091c736116@dawidwrobel.com Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-wsa-macro.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 5ad0448af649..af521e0988db 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2064,7 +2064,7 @@ static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); - ucontrol->value.integer.value[0] = wsa->ear_spkr_gain; + ucontrol->value.enumerated.item[0] = wsa->ear_spkr_gain; return 0; } @@ -2075,7 +2075,7 @@ static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); - wsa->ear_spkr_gain = ucontrol->value.integer.value[0]; + wsa->ear_spkr_gain = ucontrol->value.enumerated.item[0]; return 0; } @@ -2088,7 +2088,7 @@ static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol, snd_soc_dapm_to_component(widget->dapm); struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); - ucontrol->value.integer.value[0] = + ucontrol->value.enumerated.item[0] = wsa->rx_port_value[widget->shift]; return 0; } @@ -2101,7 +2101,7 @@ static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol, snd_soc_dapm_to_component(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; - u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 rx_port_value = ucontrol->value.enumerated.item[0]; u32 bit_input; u32 aif_rst; unsigned int dai_id; -- cgit v1.2.3 From 653d7ddf6cba867777a3d14c4f83ace008c5ad13 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Tue, 28 Jul 2026 01:23:29 +0800 Subject: inet: frags: publish queues before arming timer inet_frag_create() arms the fragment queue timer before inserting the queue into the fqdir rhashtable. If the namespace fragment timeout is zero or negative, the timer can run before the queue is published. The timer callback then marks the queue complete, tries to remove a node that is not in the hash table yet, and drops the anticipated hash reference. Creation can subsequently publish the completed queue without restoring that reference, leaving a stale hash node after the caller drops the remaining reference. Publish the queue first and arm the timer while holding the queue lock. This makes timer expiry wait until the queue is visible in the hash table, so inet_frag_kill() can remove the node and balance the hash reference. Fixes: 648700f76b03 ("inet: frags: use rhashtables for reassembly units") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Link: https://patch.msgid.link/bf66785e7c0c139d7a1900e2f01faeeab344b960.1784948849.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/ipv4/inet_fragment.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 848c0f0c2ed9..fc0cb993959f 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -393,8 +393,8 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir, *prev = ERR_PTR(-ENOMEM); return NULL; } - mod_timer(&q->timer, jiffies + fqdir->timeout); + spin_lock_bh(&q->lock); *prev = rhashtable_lookup_get_insert_key(&fqdir->rhashtable, &q->key, &q->node, f->rhash_params); if (*prev) { @@ -402,13 +402,13 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir, * we need to cancel what inet_frag_alloc() * anticipated. */ - int refs = 1; - q->flags |= INET_FRAG_COMPLETE; - inet_frag_kill(q, &refs); - inet_frag_putn(q, refs); + spin_unlock_bh(&q->lock); + inet_frag_putn(q, 2); return NULL; } + mod_timer(&q->timer, jiffies + fqdir->timeout); + spin_unlock_bh(&q->lock); return q; } -- cgit v1.2.3 From 47d7f7051253bdc02b1d245d87e38f16d31a74df Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 29 Jul 2026 05:44:11 -0400 Subject: net/sched: cls_route: fix fastmap use-after-free on filter The route4 classifier maintains a 16-slot fastmap cache that stores raw struct route4_filter pointers indexed by (id, iif). The reader (route4_classify) populates this cache via route4_set_fastmap() for every classified packet that hits a filter. The writer (route4_delete, route4_change) clears the cache via route4_reset_fastmap() before RCU-deferred kfree of the filter. This creates a UAF race: 1. Reader walks the RCU-protected bucket chain, finds filter f 2. Writer unlinks f, calls route4_reset_fastmap(), then tcf_queue_work() 3. Reader calls route4_set_fastmap() and writes f into the cache *after* the writer's reset, caching a pointer about to be freed 4. After the RCU grace period, kfree(f) executes 5. Next classified packet on the same (id, iif) tuple hits the stale fastmap entry and reads f->res from freed memory Reproduced with an mdelay(100) accelerator in route4_set_fastmap() and a concurrent add/delete stress test (provided by both zdi and Santosh). Both triggered KASAN slab-use-after-free reports in the route4 fastmap paths. Fix: Introduce a per-filter boolean dying flag to suppress stale fastmap republishing by in-flight readers. Fixes: 1109c00547fc ("net: sched: RCU cls_route") Reported-by: zdi-disclosures@trendmicro.com Reported-by: Santosh Kalluri Suggested-by: Paolo Abeni Tested-by: Victor Nogueira Tested-by: Santosh Kalluri Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260729094411.46257-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski --- net/sched/cls_route.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index bd6f945bd388..eded7aacd3f7 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -52,6 +52,7 @@ struct route4_filter { struct tcf_result res; struct tcf_exts exts; u32 handle; + bool dying; struct route4_bucket *bkt; struct tcf_proto *tp; struct rcu_work rwork; @@ -66,9 +67,11 @@ static inline int route4_fastmap_hash(u32 id, int iif) static DEFINE_SPINLOCK(fastmap_lock); static void -route4_reset_fastmap(struct route4_head *head) +route4_reset_fastmap(struct route4_head *head, struct route4_filter *f) { spin_lock_bh(&fastmap_lock); + if (f) + f->dying = true; memset(head->fastmap, 0, sizeof(head->fastmap)); spin_unlock_bh(&fastmap_lock); } @@ -81,9 +84,11 @@ route4_set_fastmap(struct route4_head *head, u32 id, int iif, /* fastmap updates must look atomic to aling id, iff, filter */ spin_lock_bh(&fastmap_lock); - head->fastmap[h].id = id; - head->fastmap[h].iif = iif; - head->fastmap[h].filter = f; + if (f == ROUTE4_FAILURE || !f->dying) { + head->fastmap[h].id = id; + head->fastmap[h].iif = iif; + head->fastmap[h].filter = f; + } spin_unlock_bh(&fastmap_lock); } @@ -297,6 +302,13 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held, next = rtnl_dereference(f->next); RCU_INIT_POINTER(b->ht[h2], next); tcf_unbind_filter(tp, &f->res); + /* Mark the filter dying under fastmap_lock so + * any in-flight reader that still holds it + * will skip the republish in route4_set_fastmap(). + */ + spin_lock_bh(&fastmap_lock); + f->dying = true; + spin_unlock_bh(&fastmap_lock); if (tcf_exts_get_net(&f->exts)) route4_queue_work(f); else @@ -307,6 +319,11 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held, kfree_rcu(b, rcu); } } + + /* All filters are unlinked and marked dying, so no in-flight + * reader can republish a stale entry after this reset. + */ + route4_reset_fastmap(head, NULL); kfree_rcu(head, rcu); } @@ -334,11 +351,11 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last, /* unlink it */ RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); - /* Remove any fastmap lookups that might ref filter - * notice we unlink'd the filter so we can't get it - * back in the fastmap. + /* Clear any fastmap entries that may ref this filter and + * mark it dying so in-flight readers can't republish it + * after the reset. */ - route4_reset_fastmap(head); + route4_reset_fastmap(head, f); /* Delete it */ tcf_unbind_filter(tp, &f->res); @@ -558,7 +575,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, } } - route4_reset_fastmap(head); + route4_reset_fastmap(head, fold); *arg = f; if (fold) { tcf_unbind_filter(tp, &fold->res); -- cgit v1.2.3 From f307a7dc32097c11413178fca437a10d20890bc2 Mon Sep 17 00:00:00 2001 From: Jiawen Liu <1298662399@qq.com> Date: Tue, 28 Jul 2026 12:17:10 +0400 Subject: net: hisilicon: hix5hd2_gmac: remove redundant NAPI delete hix5hd2_dev_remove() calls netif_napi_del() before unregister_netdev(). This is not needed because free_netdev() deletes all NAPI instances attached to the net_device. Remove the redundant call and let the networking core tear down the NAPI instance during unregister_netdev(). The probe error path still keeps its explicit netif_napi_del(), because the device has not been registered there. Fixes: 57c5bc9ad7d7 ("net: hisilicon: add hix5hd2 mac driver") Signed-off-by: Jiawen Liu <1298662399@qq.com> Link: https://patch.msgid.link/tencent_5FFD37A252B4FEA6A80AD25B17C8E904F005@qq.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hix5hd2_gmac.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index e3e7f2270560..02282dc86faf 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -1282,7 +1282,6 @@ static void hix5hd2_dev_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct hix5hd2_priv *priv = netdev_priv(ndev); - netif_napi_del(&priv->napi); unregister_netdev(ndev); mdiobus_unregister(priv->bus); mdiobus_free(priv->bus); -- cgit v1.2.3 From 1c4dac9bf1d2ac31da63b794bdec697777cbd0fd Mon Sep 17 00:00:00 2001 From: Or Har-Toov Date: Wed, 29 Jul 2026 11:06:00 +0300 Subject: devlink: fix net namespace reference leak in reload devlink_nl_reload_doit() calls devlink_netns_get(), which returns a net with a held reference. When the requested namespace differs from the current one and the reload action is not DRIVER_REINIT, the function returns -EOPNOTSUPP without releasing the reference. Add the missing put_net() on this error path. Fixes: 2edd92570441 ("devlink: don't allow to change net namespace for FW_ACTIVATE reload action") Signed-off-by: Or Har-Toov Reviewed-by: Jiri Pirko Signed-off-by: Tariq Toukan Reviewed-by: Antoine Tenart Link: https://patch.msgid.link/20260729080600.2427721-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- net/devlink/dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/devlink/dev.c b/net/devlink/dev.c index 57b2b8f03543..fd5633fa88ec 100644 --- a/net/devlink/dev.c +++ b/net/devlink/dev.c @@ -578,6 +578,7 @@ int devlink_nl_reload_doit(struct sk_buff *skb, struct genl_info *info) action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) { NL_SET_ERR_MSG_MOD(info->extack, "Changing namespace is only supported for reinit action"); + put_net(dest_net); return -EOPNOTSUPP; } } -- cgit v1.2.3 From 43e970d961ad3cc0c23e89db255c7fa4a353eff9 Mon Sep 17 00:00:00 2001 From: Chris Mi Date: Wed, 29 Jul 2026 10:16:22 +0300 Subject: net/mlx5: SF, Handle function changed event When host is powered off, firmware does not send vhca_state event for every probed host SF on the DPU because it may have deployed thousands of SFs to the host. Instead it sends a function changed event. Currently, only VFs handle this event. This commit extends support to SFs. When DPU user deactivates[1] SFs, mlx5 expects vhca_state event and leaves the SF in dangling state[2]. When DPU user deletes[3] SFs, mlx5 also expects vhca_state event and destroys the SF resources[4]. Fix it by changing SF to the right state and freeing SF resources when the function changed event is received. When this event is received, driver checks all SF states. - If state is in_use, change it to active. - If state is teardown_request, change it to allocated. And SF hardware table entry is freed if it is pending for delete. [1] # devlink port function set en3f0c1pf0sf0 state inactive [2] # devlink port function set en3f0c1pf0sf0 state active Error: mlx5_core: SF is inactivated but it is still attached. kernel answers: Device or resource busy [3] # devlink port show pci/0000:03:00.0/229376: type eth netdev en3f0c1pf0sf0 \ flavour pcisf controller 1 pfnum 0 sfnum 0 splittable false function: hw_addr 00:00:00:00:00:00 state active opstate attached \ roce enable trust off max_uc_macs 4096 max_io_eqs 8 # devlink port del en3f0c1pf0sf0 [4] # devlink port add pci/0000:03:00.0 flavour pcisf pfnum 0 sfnum 0 \ controller 1 Error: mlx5_core: SF already exist. Choose different sfnum. kernel answers: File exists Fixes: 6a3273217469 ("net/mlx5: SF, Port function state change support") Signed-off-by: Chris Mi Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260729071622.2423270-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 10 +++++--- .../net/ethernet/mellanox/mlx5/core/sf/devlink.c | 29 ++++++++++++++++++++++ .../mellanox/mlx5/core/sf/diag/sf_tracepoint.h | 8 ++++++ .../net/ethernet/mellanox/mlx5/core/sf/hw_table.c | 22 ++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h | 12 +++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 907ee83a722d..247e5d85ec36 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3986,7 +3986,7 @@ static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) mutex_destroy(&esw->fdb_table.offloads.vports.lock); } -static void esw_vfs_changed_event_handler(struct mlx5_eswitch *esw) +static void esw_changed_event_handler(struct mlx5_eswitch *esw) { struct mlx5_esw_pf_info host_pf_info; u16 new_num_vfs; @@ -3999,6 +3999,11 @@ static void esw_vfs_changed_event_handler(struct mlx5_eswitch *esw) host_pf_info = mlx5_esw_get_host_pf_info(esw->dev, out); new_num_vfs = host_pf_info.num_of_vfs; + if (host_pf_info.pf_disabled) { + mlx5_sf_table_esw_changed_event_handler(esw->dev); + mlx5_sf_hw_table_esw_changed_event_handler(esw->dev); + } + if (new_num_vfs == esw->esw_funcs.num_vfs || host_pf_info.pf_disabled) goto free; @@ -4091,8 +4096,7 @@ int mlx5_esw_funcs_changed_handler(struct notifier_block *nb, esw_funcs = mlx5_nb_cof(nb, struct mlx5_esw_functions, nb); esw = container_of(esw_funcs, struct mlx5_eswitch, esw_funcs); - ret = mlx5_esw_add_work(esw, esw_vfs_changed_event_handler, - GFP_ATOMIC); + ret = mlx5_esw_add_work(esw, esw_changed_event_handler, GFP_ATOMIC); if (ret) return NOTIFY_DONE; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c index b6cecbcc392d..4564d460416b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c @@ -561,3 +561,32 @@ bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev) return xa_empty(&table->function_ids); } + +void mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_table *table = dev->priv.sf_table; + unsigned long index; + struct mlx5_sf *sf; + + trace_mlx5_sf_host_pf_disabled(dev); + + if (!table) + return; + + mutex_lock(&table->sf_state_lock); + xa_for_each(&table->function_ids, index, sf) { + if (!sf->controller) + continue; + + if (sf->hw_state == MLX5_VHCA_STATE_IN_USE) + sf->hw_state = MLX5_VHCA_STATE_ACTIVE; + else if (sf->hw_state == MLX5_VHCA_STATE_TEARDOWN_REQUEST) + sf->hw_state = MLX5_VHCA_STATE_ALLOCATED; + else + continue; + trace_mlx5_sf_update_state(table->dev, sf->port_index, + sf->controller, sf->hw_fn_id, + sf->hw_state); + } + mutex_unlock(&table->sf_state_lock); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h index 302ce00da5a9..9dc88a7e30ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h @@ -11,6 +11,14 @@ #include #include "sf/vhca_event.h" +TRACE_EVENT(mlx5_sf_host_pf_disabled, + TP_PROTO(const struct mlx5_core_dev *dev), + TP_ARGS(dev), + TP_STRUCT__entry(__string(devname, dev_name(dev->device))), + TP_fast_assign(__assign_str(devname);), + TP_printk("(%s)\n", __get_str(devname)) +); + TRACE_EVENT(mlx5_sf_add, TP_PROTO(const struct mlx5_core_dev *dev, unsigned int port_index, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c index 0bc9146a3598..95a8b1e64ba4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c @@ -459,3 +459,25 @@ bool mlx5_sf_hw_table_supported(const struct mlx5_core_dev *dev) { return !!dev->priv.sf_hw_table; } + +void mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_hw_table *table; + struct mlx5_sf_hwc_table *hwc; + int i; + + table = dev->priv.sf_hw_table; + if (!table) + return; + + mutex_lock(&table->table_lock); + hwc = &table->hwc[MLX5_SF_HWC_EXT_HOST]; + for (i = 0; i < hwc->max_fn; i++) { + struct mlx5_sf_hw *sf_hw; + + sf_hw = &hwc->sfs[i]; + if (sf_hw->allocated && sf_hw->pending_delete) + mlx5_sf_hw_table_hwc_sf_free(dev, hwc, i); + } + mutex_unlock(&table->table_lock); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h index d8a934a0e968..ed784682b9b1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h @@ -15,12 +15,14 @@ void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev); int mlx5_sf_hw_notifier_init(struct mlx5_core_dev *dev); void mlx5_sf_hw_notifier_cleanup(struct mlx5_core_dev *dev); void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev); +void mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev); int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev); int mlx5_sf_table_init(struct mlx5_core_dev *dev); void mlx5_sf_notifiers_cleanup(struct mlx5_core_dev *dev); void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev); bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev); +void mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev); int mlx5_devlink_sf_port_new(struct devlink *devlink, const struct devlink_port_new_attrs *add_attr, @@ -60,6 +62,11 @@ static inline void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev) { } +static inline void +mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev) +{ +} + static inline int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev) { return 0; @@ -83,6 +90,11 @@ static inline bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev) return true; } +static inline void +mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev) +{ +} + #endif #endif -- cgit v1.2.3 From af39eb111ce6b5eba9c08513b62c4868eb7e7fd5 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 29 Jul 2026 11:04:02 +0300 Subject: net/mlx5: fw_tracer, return NULL on create error Tracer creation can fail by returning either NULL or ERR_PTR. The return value is stored without a check on the device, and users treat ERR_PTR and NULL the same way. This also causes a crash in the core dump logic, which is missing the ERR_PTR check and ends up dereferencing it, as shown in the trace below. Switch tracer creation to return NULL on failure only, so callers only need a single NULL check. Internal error: Oops: 0000000096000006 [#1] SMP Modules linked in: mlx5_ib ib_uverbs ib_core ipv6 mlx5_core CPU: 1 UID: 0 PID: 12 Comm: kworker/u16:0 Not tainted 6.19.7 #1 PREEMPT(none) Workqueue: mlx5_health0001:01:00.0 mlx5_fw_reporter_err_work [mlx5_core] pstate: a3400009 (NzCv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--) pc : mlx5_fw_tracer_trigger_core_dump_general+0x58/0xe0 [mlx5_core] lr : mlx5_fw_tracer_trigger_core_dump_general+0x40/0xe0 [mlx5_core] sp : ffff800081cf3c40 x29: ffff800081cf3c90 x28: 0000000000000000 x27: 0000000000000000 x26: ffff000080018828 x25: 0000000000000000 x24: ffff000080304a05 x23: ffff800081cf3d80 x22: ffff0000847e01a0 x21: 0000000000000000 x20: ffff0000847e01a0 x19: ffffffffffffffa1 x18: ffff80008310bbf0 x17: ffff800080119650 x16: ffff80008010df54 x15: ffff80008010d4ac x14: ffff800079c202e4 x13: ffff80008002fe60 x12: ffff800080119650 x11: ffff80008010df54 x10: ffff80008010d4ac x9 : ffff800079c203d8 x8 : ffff800081cf3c88 x7 : 0000000000000000 x6 : 0000000000000000 x5 : 0000000000000000 x4 : 0000000000000008 x3 : 0000000000000030 x2 : 0000000000000008 x1 : 0000000000000000 x0 : 00000000c5c4000e Call trace: mlx5_fw_tracer_trigger_core_dump_general+0x58/0xe0 [mlx5_core] (P) mlx5_fw_reporter_dump+0x30/0x2e0 [mlx5_core] devlink_health_do_dump+0x9c/0x160 devlink_health_report+0x1c0/0x288 mlx5_fw_reporter_err_work+0xac/0xc0 [mlx5_core] process_one_work+0x15c/0x3d8 worker_thread+0x18c/0x320 kthread+0x148/0x228 ret_from_fork+0x10/0x20 Code: b9400000 5ac00800 7a401800 540003ca (3940a260) ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Oops: Fatal exception SMP: stopping secondary CPUs Kernel Offset: disabled CPU features: 0x000000,00078031,75fce5a1,35fffe67 Memory Limit: none ---[ end Kernel panic - not syncing: Oops: Fatal exception ]--- Fixes: fd1483fe1f9f ("net/mlx5: Add support for FW reporter dump") Signed-off-by: Michael Guralnik Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260729080402.2427184-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c index adcc73e2a5b3..1493d8106c1a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c @@ -1025,13 +1025,11 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev) tracer = kvzalloc_obj(*tracer); if (!tracer) - return ERR_PTR(-ENOMEM); + return NULL; tracer->work_queue = create_singlethread_workqueue("mlx5_fw_tracer"); - if (!tracer->work_queue) { - err = -ENOMEM; + if (!tracer->work_queue) goto free_tracer; - } tracer->dev = dev; @@ -1073,7 +1071,7 @@ destroy_workqueue: destroy_workqueue(tracer->work_queue); free_tracer: kvfree(tracer); - return ERR_PTR(err); + return NULL; } static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data); @@ -1084,7 +1082,7 @@ int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer) struct mlx5_core_dev *dev; int err; - if (IS_ERR_OR_NULL(tracer)) + if (!tracer) return 0; if (!tracer->str_db.loaded) @@ -1134,7 +1132,7 @@ err_cancel_work: /* Stop tracer + Cleanup HW resources */ void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer) { - if (IS_ERR_OR_NULL(tracer)) + if (!tracer) return; mutex_lock(&tracer->state_lock); @@ -1163,7 +1161,7 @@ unlock: /* Free software resources (Buffers, etc ..) */ void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer) { - if (IS_ERR_OR_NULL(tracer)) + if (!tracer) return; mlx5_core_dbg(tracer->dev, "FWTracer: Destroy\n"); @@ -1215,7 +1213,7 @@ int mlx5_fw_tracer_reload(struct mlx5_fw_tracer *tracer) struct mlx5_core_dev *dev; int err; - if (IS_ERR_OR_NULL(tracer)) + if (!tracer) return 0; dev = tracer->dev; -- cgit v1.2.3 From 4a19f7ab5972ef608b31ae921419bc3e04b3f8ad Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Fri, 31 Jul 2026 11:59:55 -0700 Subject: accel/amdxdna: Fix locally exploitable BUG_ON in amdxdna_insert_pages() In amdxdna_insert_pages(), vm_flags_mod() sets VM_MIXEDMAP and clears VM_PFNMAP. If an unprivileged userspace process mmaps a non-imported GEM object and then calls madvise(MADV_DONTNEED), the PTEs will be successfully cleared because VM_MIXEDMAP allows this (unlike VM_PFNMAP). When userspace subsequently accesses the memory, drm_gem_shmem_fault() handles the page fault and attempts to map the backing shmem page via vmf_insert_pfn() which calls vmf_insert_pfn_prot(). Because the backing shmem page is normal system memory (pfn_valid(pfn) is true) and the VMA now has VM_MIXEDMAP set, won't this predictably trigger the explicit assertion BUG_ON((vma->vm_flags & VM_MIXEDMAP) && pfn_valid(pfn)) Fix by removing the vm_flags_mod() call and replacing the vm_insert_pages() pre-population with the handle_mm_fault() loop that was already used for the import (dma-buf) path. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260731185955.3449311-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_gem.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index aec69d5f463e..1c63eff0a4a8 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -467,25 +467,17 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, XDNA_ERR(xdna, "Failed shmem mmap %d", ret); return ret; } - - /* The buffer is based on memory pages. Fix the flag. */ - vm_flags_mod(vma, VM_MIXEDMAP, VM_PFNMAP); - ret = vm_insert_pages(vma, vma->vm_start, abo->base.pages, - &num_pages); + } else { + vma->vm_private_data = NULL; + vma->vm_ops = NULL; + ret = dma_buf_mmap(abo->dma_buf, vma, 0); if (ret) { - XDNA_ERR(xdna, "Failed insert pages %d", ret); - amdxdna_mark_mapp_invalid(abo, vma); + XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret); + return ret; } - return 0; - } - - vma->vm_private_data = NULL; - vma->vm_ops = NULL; - ret = dma_buf_mmap(abo->dma_buf, vma, 0); - if (ret) { - XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret); - return ret; + /* Drop the reference drm_gem_mmap_obj() acquired.*/ + drm_gem_object_put(to_gobj(abo)); } do { @@ -502,9 +494,6 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, offset += PAGE_SIZE; } while (--num_pages); - /* Drop the reference drm_gem_mmap_obj() acquired.*/ - drm_gem_object_put(to_gobj(abo)); - return 0; } -- cgit v1.2.3 From a76624733730e541e4955fdecf506af2f6b20558 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Sun, 19 Jul 2026 23:22:07 +0800 Subject: bpf, sockmap: Fix sk_redir use-after-free in send verdict sk_psock_msg_verdict() takes a socket reference for psock->sk_redir. tcp_bpf_send_verdict() copies that pointer while holding the source socket lock, but does not take a reference for the local copy before dropping the lock around tcp_bpf_sendmsg_redir(). When apply_bytes keeps the cached verdict active, another sendmsg() on the same source socket can consume the remaining bytes and release the cached reference while the first thread still holds only the raw local pointer: CPU 0 CPU 1 sk_redir = psock->sk_redir apply_bytes remains nonzero release_sock(sk) lock_sock(sk) apply_bytes reaches zero psock->sk_redir = NULL release_sock(sk) tcp_bpf_sendmsg_redir(sk_redir) sock_put(sk_redir) tcp_bpf_sendmsg_redir(sk_redir) The final sock_put() can free sk_redir before CPU 0 dereferences it. KASAN reported: BUG: KASAN: slab-use-after-free in tcp_bpf_sendmsg_redir+0xf39/0x1020 Read of size 8 at addr ffff888108537090 by task poc/87 Call Trace: tcp_bpf_sendmsg_redir+0xf39/0x1020 tcp_bpf_sendmsg+0x977/0x1a50 __sys_sendto+0x32c/0x3a0 __x64_sys_sendto+0xdb/0x1b0 Allocated by task 85: sk_prot_alloc+0x56/0x210 sk_clone+0x6f/0x14b0 inet_csk_clone_lock+0x24/0x740 tcp_create_openreq_child+0x25/0x2710 tcp_v4_syn_recv_sock+0x10a/0xe00 Freed by task 0: __kasan_slab_free+0x43/0x70 slab_free_after_rcu_debug+0xa6/0x1e0 rcu_core+0x50a/0x1850 Last potentially related work creation: __sk_destruct+0x3da/0x540 sk_psock_destroy+0x81e/0xab0 process_one_work+0x63a/0x1070 Take a temporary socket reference while the source socket lock still protects psock->sk_redir, and drop it after tcp_bpf_sendmsg_redir() returns. This keeps each unlocked use independent of cached-verdict ownership. Fixes: 604326b41a6f ("bpf, sockmap: convert to generic sk_msg interface") Signed-off-by: Chengfeng Ye Reviewed-by: John Fastabend Reviewed-by: Emil Tsalapatis Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/20260719152207.2892156-1-nicoyip.dev@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi --- net/ipv4/tcp_bpf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index a30475afb6f8..2e234d155b5e 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -469,6 +469,7 @@ more_data: case __SK_REDIRECT: redir_ingress = psock->redir_ingress; sk_redir = psock->sk_redir; + sock_hold(sk_redir); sk_msg_apply_bytes(psock, tosend); if (!psock->apply_bytes) { /* Clean up before releasing the sock lock. */ @@ -489,6 +490,7 @@ more_data: if (eval == __SK_REDIRECT) sock_put(sk_redir); + sock_put(sk_redir); lock_sock(sk); sk_mem_uncharge(sk, sent); -- cgit v1.2.3 From fdeba03fea78407a8c52faa99177c9f7f29f90eb Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Sat, 1 Aug 2026 00:09:21 +0800 Subject: bpf: Fix netns reference imbalance in conntrack kfuncs The opts argument of the BPF conntrack kfuncs can point to a shared map value. __bpf_nf_ct_lookup() and __bpf_nf_ct_alloc_entry() read opts->netns_id separately when acquiring and releasing the network namespace reference. The reference imbalance can occur as follows: CPU 0 CPU 1 read opts->netns_id (-1) skip get_net_ns_by_id() write opts->netns_id (id) read opts->netns_id (id) put_net(net) /* no matching get */ The reverse transition leaks the reference. Repeating the unmatched put can destroy a live namespace and crash later users. The kernel reported: Oops: general protection fault, probably for non-canonical address KASAN: null-ptr-deref in range [0x00000000000000e8-0x00000000000000ef] RIP: 0010:bpf_prog_test_run_xdp+0x52c/0x1700 Call Trace: __sys_bpf+0x1662/0x50c0 __x64_sys_bpf+0x73/0xb0 do_syscall_64+0xf9/0x540 entry_SYSCALL_64_after_hwframe+0x77/0x7f Kernel panic - not syncing: Fatal exception Snapshot every input field of opts with READ_ONCE() before validating or using it. The netns_id snapshot keeps the namespace get/put pair balanced, while the other snapshots keep the remaining options from changing partway through an invocation. The individual reads can still observe an inconsistent combination during a concurrent update, but each selected field value remains stable for that invocation. Fixes: aed8ee7feb44 ("net: netfilter: Deduplicate code in bpf_{xdp,skb}_ct_lookup") Fixes: d7e79c97c00c ("net: netfilter: Add kfuncs to allocate and insert CT") Signed-off-by: Chengfeng Ye Reviewed-by: Emil Tsalapatis Link: https://lore.kernel.org/bpf/20260731160921.3245840-1-nicoyip.dev@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi --- net/netfilter/nf_conntrack_bpf.c | 72 ++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c index f98d1d4b42c3..c2df7c948281 100644 --- a/net/netfilter/nf_conntrack_bpf.c +++ b/net/netfilter/nf_conntrack_bpf.c @@ -122,42 +122,54 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple, struct nf_conntrack_tuple otuple, rtuple; struct nf_conntrack_zone ct_zone; struct nf_conn *ct; + u8 ct_zone_dir = 0; + u16 ct_zone_id; + s32 netns_id; + u8 l4proto; int err; if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) return ERR_PTR(-EINVAL); + + netns_id = READ_ONCE(opts->netns_id); + l4proto = READ_ONCE(opts->l4proto); + ct_zone_id = READ_ONCE(opts->ct_zone_id); if (opts_len == NF_BPF_CT_OPTS_SZ) { - if (opts->reserved[0] || opts->reserved[1] || opts->reserved[2]) + ct_zone_dir = READ_ONCE(opts->ct_zone_dir); + if (READ_ONCE(opts->reserved[0]) || + READ_ONCE(opts->reserved[1]) || + READ_ONCE(opts->reserved[2])) return ERR_PTR(-EINVAL); } else { - if (opts->ct_zone_id) + if (ct_zone_id) return ERR_PTR(-EINVAL); } - if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS)) + if (unlikely(netns_id < BPF_F_CURRENT_NETNS)) return ERR_PTR(-EINVAL); - err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, + err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, IP_CT_DIR_ORIGINAL, &otuple); if (err < 0) return ERR_PTR(err); - err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, + err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, IP_CT_DIR_REPLY, &rtuple); if (err < 0) return ERR_PTR(err); - if (opts->netns_id >= 0) { - net = get_net_ns_by_id(net, opts->netns_id); + if (netns_id >= 0) { + net = get_net_ns_by_id(net, netns_id); if (unlikely(!net)) return ERR_PTR(-ENONET); } if (opts_len == NF_BPF_CT_OPTS_SZ) { - if (opts->ct_zone_dir == 0) - opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; - nf_ct_zone_init(&ct_zone, - opts->ct_zone_id, opts->ct_zone_dir, 0); + if (ct_zone_dir == 0) { + ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; + opts->ct_zone_dir = ct_zone_dir; + } + nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0); } else { ct_zone = nf_ct_zone_dflt; } @@ -171,7 +183,7 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple, __nf_ct_set_timeout(ct, timeout * HZ); out: - if (opts->netns_id >= 0) + if (netns_id >= 0) put_net(net); return ct; @@ -186,46 +198,58 @@ static struct nf_conn *__bpf_nf_ct_lookup(struct net *net, struct nf_conntrack_tuple tuple; struct nf_conntrack_zone ct_zone; struct nf_conn *ct; + u8 ct_zone_dir = 0; + u16 ct_zone_id; + s32 netns_id; + u8 l4proto; int err; if (!opts || !bpf_tuple) return ERR_PTR(-EINVAL); if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) return ERR_PTR(-EINVAL); + + netns_id = READ_ONCE(opts->netns_id); + l4proto = READ_ONCE(opts->l4proto); + ct_zone_id = READ_ONCE(opts->ct_zone_id); if (opts_len == NF_BPF_CT_OPTS_SZ) { - if (opts->reserved[0] || opts->reserved[1] || opts->reserved[2]) + ct_zone_dir = READ_ONCE(opts->ct_zone_dir); + if (READ_ONCE(opts->reserved[0]) || + READ_ONCE(opts->reserved[1]) || + READ_ONCE(opts->reserved[2])) return ERR_PTR(-EINVAL); } else { - if (opts->ct_zone_id) + if (ct_zone_id) return ERR_PTR(-EINVAL); } - if (unlikely(opts->l4proto != IPPROTO_TCP && opts->l4proto != IPPROTO_UDP)) + if (unlikely(l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP)) return ERR_PTR(-EPROTO); - if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS)) + if (unlikely(netns_id < BPF_F_CURRENT_NETNS)) return ERR_PTR(-EINVAL); - err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, + err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, IP_CT_DIR_ORIGINAL, &tuple); if (err < 0) return ERR_PTR(err); - if (opts->netns_id >= 0) { - net = get_net_ns_by_id(net, opts->netns_id); + if (netns_id >= 0) { + net = get_net_ns_by_id(net, netns_id); if (unlikely(!net)) return ERR_PTR(-ENONET); } if (opts_len == NF_BPF_CT_OPTS_SZ) { - if (opts->ct_zone_dir == 0) - opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; - nf_ct_zone_init(&ct_zone, - opts->ct_zone_id, opts->ct_zone_dir, 0); + if (ct_zone_dir == 0) { + ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; + opts->ct_zone_dir = ct_zone_dir; + } + nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0); } else { ct_zone = nf_ct_zone_dflt; } hash = nf_conntrack_find_get(net, &ct_zone, &tuple); - if (opts->netns_id >= 0) + if (netns_id >= 0) put_net(net); if (!hash) return ERR_PTR(-ENOENT); -- cgit v1.2.3 From 6d4514ca9cdf61fec4ec634cf50386f6f7e69748 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 21 Jul 2026 00:31:48 +0000 Subject: futex: Prevent robust futex exit race some more A robust futex unlock stores 0 over the whole futex value - wiping FUTEX_WAITERS - and wakes a single waiter. That wakeup is a one-shot notification: the protocol relies on its recipient to either acquire the futex (and eventually unlock while aware of the remaining contention) or re-arm FUTEX_WAITERS before sleeping again. If the woken waiter is killed before it can do either, the kernel must jump in and wake the next task down the line. This is a known complication of the futex protocol with a previous partial fix in commit ca16d5bee598 ("futex: Prevent robust futex exit race"). Unfortunately, that fix is insufficient. If a third task re-acquired the futex through the uncontended fast path in the meantime, the notification is lost: robust exit processing sees that it is owned by another task and does nothing, while the new owner sees no FUTEX_WAITERS when it unlocks and wakes nobody. The remaining waiters sleep forever behind a free futex: A owns the futex, B and C sleep in FUTEX_WAIT uval == A | FUTEX_WAITERS A robust unlock: store 0, FUTEX_WAKE(1) wakes B uval == 0 D fast path acquire: cmpxchg(0 -> D) uval == D, no FUTEX_WAITERS B killed before acting on the wakeup B exit walk, pending op: owner D != B -> no action D unlock: no FUTEX_WAITERS -> no wake C sleeps forever This is clearly a shortcoming in the implementation, which fails to keep the FUTEX_WAITERS bit consistent. Work around this by augmenting the robust list exit processing to also perform the extra wakeup if the futex word is owned by another thread but FUTEX_WAITERS is not set. This does not fix the problem of a non-contended take over/release and free sequence, which has been discussed for years and has been addressed by commit 3ca9595d9fb6 ("futex: Add support for unlocking robust futexes") and subsequent changes, but failed to take the problem described above into account. A more complete solution which is based on the in kernel unlock of contended robust futexes has been discussed in the context of this change and should show up in mainline sooner than later. [ tglx: Amend change log slightly and fixup coding style ] Fixes: ca16d5bee598 ("futex: Prevent robust futex exit race") Signed-off-by: Keno Fischer Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Assisted-by: ClaudeCode:claude-fable-5 tla+ Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260730194705.38981-1-keno@juliacomputing.com --- kernel/futex/core.c | 85 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/kernel/futex/core.c b/kernel/futex/core.c index 179b26e9c934..2650d1e52803 100644 --- a/kernel/futex/core.c +++ b/kernel/futex/core.c @@ -982,8 +982,11 @@ retry: return -1; /* - * Special case for regular (non PI) futexes. The unlock path in - * user space has two race scenarios: + * Special case for regular (non PI) futexes. Ordinarily, we do + * not perform any processing here unless the current thread was + * the owner of the futex (by the TID check below). + * + * However, the unlock path has three race scenarios: * * 1. The unlock path releases the user space futex value and * before it can execute the futex() syscall to wake up @@ -992,42 +995,70 @@ retry: * 2. A woken up waiter is killed before it can acquire the * futex in user space. * - * In the second case, the wake up notification could be generated - * by the unlock path in user space after setting the futex value - * to zero or by the kernel after setting the OWNER_DIED bit below. + * 3. A woken up waiter is killed in user space after another + * thread has acquired the futex, but before it can set + * FUTEX_WAITERS. + * + * Note that, if userspace uses the FUTEX_ROBUST_UNLOCK flag, we + * will not see case 1 here. + * + * In the second and third case, the wake up notification could + * be generated from any of: + * + * i. An ordinary futex wakeup after unlock (with or + * without FUTEX_ROBUST_UNLOCK) + * ii. A robust wakeup from another thread's death + * iii. A previous round through this special case + * + * As a result, the futex world will be in one of four states: + * + * A. The futex word is 0 (unlocked) + * B. The futex word is owned by another thread + * (FUTEX_WAITERS is not set) + * C. The futex word is owned by another thread + * (FUTEX_WAITERS set) + * D. The futex's owner died and OWNER_DIED is set + * (the owner part of the word is 0) * - * In both cases the TID validation below prevents a wakeup of - * potential waiters which can cause these waiters to block - * forever. + * The key issue is that the kernel usually (at least from + * sources ii. and iii. or when so requested by userspace from + * source i.) only ever wakes *one* waiter at a time. If this + * waiter dies before acquiring the futex (or setting the + * FUTEX_WAITERS bit), the kernel *must* still wake the next + * waiter down the line to uphold the futex invariants and + * avoid lost wakeups. Note we do not need to handle state C, + * as it does not matter to us whether *we* successfully set + * the bit or a third thread did so in the meantime. * - * In both cases the following conditions are met: + * Therefore, in these cases we must issue an additional + * futex_wake(). Note however that we *must not* set OWNER_DIED + * here. Our thread is *not* the owner of the futex. * - * 1) task->futex.robust_list->list_op_pending != NULL - * @pending_op == true - * 2) The owner part of user space futex value == 0 + * Thus to summarize, the conditions for needing the additional + * futex_wake() are: + * + * 1) @pending_op == true (the thread has not finished the + * mutex operation) + * 2) The futex word is in one of the states A, B or D * 3) Regular futex: @pi == false * - * If these conditions are met, it is safe to attempt waking up a - * potential waiter without touching the user space futex value and - * trying to set the OWNER_DIED bit. If the futex value is zero, - * the rest of the user space mutex state is consistent, so a woken - * waiter will just take over the uncontended futex. Setting the - * OWNER_DIED bit would create inconsistent state and malfunction - * of the user space owner died handling. Otherwise, the OWNER_DIED - * bit is already set, and the woken waiter is expected to deal with - * this. + * Note in particular that in all of the states A-D the owner + * portion of the futex word differs from our thread's TID + * (unless the actual owner has the same TID in another PID + * namespace, but we cannot currently distinguish that + * scenario), so this can be a special-case wakeup in the bail + * path of the ordinary TID check. */ owner = uval & FUTEX_TID_MASK; - if (pending_op && !pi && !owner) { - futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, NULL, 1, - FUTEX_BITSET_MATCH_ANY); + if (owner != task_pid_vnr(curr)) { + if (pending_op && !pi && (!owner || !(uval & FUTEX_WAITERS))) { + futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, NULL, 1, + FUTEX_BITSET_MATCH_ANY); + } return 0; } - if (owner != task_pid_vnr(curr)) - return 0; - /* * Ok, this dying thread is truly holding a futex * of interest. Set the OWNER_DIED bit atomically -- cgit v1.2.3 From f1a3a9946aab611dd2200c01ff122f64b033dad2 Mon Sep 17 00:00:00 2001 From: Babanpreet Singh Date: Tue, 14 Jul 2026 04:29:10 +0000 Subject: counter: microchip-tcb-capture: Fix DT channel validation mchp_tc_probe() reads the devicetree "reg" cell - a u32, per the API contract of of_property_read_u32_index() - into a signed int, so the bounds check "channel > 2" fails to reject cell values at or above 0x80000000: reinterpreted as a negative int, they compare below 2 and pass validation. A malformed devicetree can therefore drive a negative channel into the ATMEL_TC_REG() offset arithmetic, making the driver access syscon regmap offsets outside the TC block's register window, and into the "t%d_clk" clock-name formatting, where it truncates clk_name (sized for "t0_clk".."t2_clk"). Declare channel as u32, matching the API contract; the unsigned comparison then rejects everything except channels 0..2. Adjust the format specifier to %u accordingly, which also resolves the W=1 warning that exposed the gap: microchip-tcb-capture.c:520:56: warning: '%d' directive output may be truncated writing between 1 and 11 bytes into a region of size 6 [-Wformat-truncation=] note: directive argument in the range [-2147483648, 2] No behavior change for well-formed devicetrees: channels 0..2 take identical paths before and after. Fixes: 106b104137fd ("counter: Add microchip TCB capture counter") Assisted-by: Claude:claude-fable-5 [gcc W=1] Signed-off-by: Babanpreet Singh Reviewed-by: Joshua Crofts Link: https://lore.kernel.org/r/20260714042910.7-1-bbnpreetsingh@gmail.com Signed-off-by: William Breathitt Gray --- drivers/counter/microchip-tcb-capture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index 19d457ae4c3b..e53a8390756b 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -483,7 +483,7 @@ static int mchp_tc_probe(struct platform_device *pdev) char clk_name[7]; struct regmap *regmap; struct clk *clk[3]; - int channel; + u32 channel; int ret, i; counter = devm_counter_alloc(&pdev->dev, sizeof(*priv)); @@ -517,7 +517,7 @@ static int mchp_tc_probe(struct platform_device *pdev) priv->channel[i] = channel; - snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel); + snprintf(clk_name, sizeof(clk_name), "t%u_clk", channel); clk[i] = of_clk_get_by_name(np->parent, clk_name); if (IS_ERR(clk[i])) { -- cgit v1.2.3 From 2fd9b4cfcefe30cb506072f78f2cd3b6dc8a29b1 Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Tue, 28 Jul 2026 17:57:07 +0800 Subject: Docs/admin-guide/cgroup-v2: document io.latency rotational vs non-rotational behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit io.latency is documented only in terms of average latency and the avg_lat stat, which matches rotational devices. On non-rotational devices a group misses its target once enough of the IOs in the window individually exceed it, and io.stat reports missed/total rather than avg_lat/win. Describe both cases: how a miss is detected, note that the avg_lat tuning guidance is rotational-only, and update the io.stat field list (mark avg_lat/win as rotational-only, document missed/total). Acked-by: Michal Koutný Signed-off-by: Tao Cui Signed-off-by: Tejun Heo --- Documentation/admin-guide/cgroup-v2.rst | 50 ++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 14b8c571c0d1..aed195a71cbf 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -2239,9 +2239,12 @@ IO Latency ~~~~~~~~~~ This is a cgroup v2 controller for IO workload protection. You provide a group -with a latency target, and if the average latency exceeds that target the -controller will throttle any peers that have a lower latency target than the -protected workload. +with a latency target, and if the group misses its target the controller will +throttle any peers that have a lower latency target than the protected +workload. How a miss is detected depends on the device: on rotational devices +the average latency over the window must exceed the target, while on +non-rotational devices a miss is counted once enough of the IOs in the window +individually exceed the target. The limits are only applied at the peer level in the hierarchy. This means that in the diagram below, only groups A, B, and C will influence each other, and @@ -2258,10 +2261,12 @@ 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, 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. +blkcg_debug_stats enabled, observe io.stat for your workload group to get an +idea of the latency you see during normal operation. On rotational devices, +use the avg_lat value as a basis for your real setting, setting it 10-15% +higher. On non-rotational devices io.stat reports no average latency; set +the target based on your device and use the missed/total fields to verify it +is being met. How IO Latency Throttling Works ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2303,19 +2308,36 @@ IO Latency Interface Files the blkcg_debug_stats module parameter is enabled (it is disabled by default). + The reported latency fields depend on the device. Rotational devices + report avg_lat and win; non-rotational devices report missed and total + instead. missed and total are live counters for the current window and + may change between reads. + depth This is the current queue depth for the group. avg_lat - This is an exponential moving average with a decay rate of 1/exp - bound by the sampling interval. The decay rate interval can be - calculated by multiplying the win value in io.stat by the - corresponding number of samples based on the win value. + (Rotational devices only.) This is an exponential moving + average with a decay rate of 1/exp bound by the sampling + interval. The decay rate interval can be calculated by + multiplying the win value in io.stat by the corresponding number + of samples based on the win value. win - The sampling window size in milliseconds. This is the minimum - duration of time between evaluation events. Windows only elapse - with IO activity. Idle periods extend the most recent window. + (Rotational devices only.) The sampling window size in + milliseconds. This is the minimum duration of time between + evaluation events. Windows only elapse with IO activity. Idle + periods extend the most recent window. + + missed + (Non-rotational devices only.) The number of IOs in the + current window whose latency exceeded the target. A group is + considered to be missing its target once missed reaches a + certain ratio of total. + + total + (Non-rotational devices only.) The total number of IOs + accounted in the current window. IO Priority ~~~~~~~~~~~ -- cgit v1.2.3 From 4da94744707b27a3ae1197bdd7127da4505dc5b1 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Tue, 28 Jul 2026 23:09:22 -0300 Subject: drm/v3d: Serialize the scheduler timeout handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V3D exposes several independent hardware queues (BIN, RENDER, TFU and CSD) but has only a single, global reset. A timeout on any one queue therefore has to stop, reset and restart the schedulers of every other queue as well. That makes concurrent timeout handlers unsafe. `reset_lock` was never able to make them safe, as a driver-side lock can only cover the driver's &drm_sched_backend_ops.timedout_job callback. The scheduler handles the timed out job and its pending list around that callback, outside of the driver's control, so a global reset triggered by one queue can still interfere with another queue that is in the middle of handling a timeout of its own. Consequently, if a reset happens in the CSD queue while a CL-intensive application is running, the global reset stops and restarts the CL queue's scheduler while that queue is handling a timeout of its own. As drm_sched_stop() and drm_sched_start() subtract and add the credits of every job sitting on the pending list of the scheduler they are called on, and as the CL queue's handler concurrently takes its job off that same list and puts it back, the stop and the start no longer see the same set of jobs. The CL queue is left with more credits in flight than its limit: [ 327.302739] ------------[ cut here ]------------ [ 327.302744] WARNING: CPU: 2 PID: 43 at drivers/gpu/drm/scheduler/sched_main.c:102 drm_sched_run_job_work+0x238/0x4d0 [gpu_sched] [ 327.302884] CPU: 2 UID: 0 PID: 43 Comm: kworker/u16:1 Not tainted 6.18.39-v8-16k+ #3 PREEMPT [ 327.302889] Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT) [ 327.302893] Workqueue: v3d_bin drm_sched_run_job_work [gpu_sched] [ 327.302984] Call trace: [ 327.302987] drm_sched_run_job_work+0x238/0x4d0 [gpu_sched] (P) [ 327.302997] process_scheduled_works+0x180/0x3d0 [ 327.303010] worker_thread+0x268/0x3e8 [ 327.303016] kthread+0x140/0x250 [ 327.303022] ret_from_fork+0x10/0x20 [ 327.303031] ---[ end trace 0000000000000000 ]--- From that point on, the credit count of the CL queue is broken, causing a complete GPU hang and UI freeze. The DRM scheduler already provides a mechanism to serialize the timeout handlers of different schedulers: an ordered workqueue passed as drm_sched_init()'s @timeout_wq parameter. By default, each scheduler queues its timeout work on the system workqueue, which runs the handlers concurrently. Give all of the queues a shared ordered workqueue instead, as recommended by the DRM scheduler documentation for hardware that has distinct queues but resets globally. Cc: stable@vger.kernel.org # 6.15 Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260728-v3d-order-global-reset-v1-1-e47be838158d@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_drv.h | 6 ++++++ drivers/gpu/drm/v3d/v3d_sched.c | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 423bcfe331a0..8779a42c6508 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -182,6 +182,12 @@ struct v3d_dev { */ struct mutex reset_lock; + /* Ordered workqueue shared by every queue's scheduler timeout work. + * V3D reset is global to all queues, so the timeout handlers must not + * run concurrently. + */ + struct workqueue_struct *reset_wq; + /* Lock taken when creating and pushing the GPU scheduler * jobs, to keep the sched-fence seqnos in order. */ diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index c35f9f70cf8d..63dd95b828c8 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -837,6 +837,7 @@ v3d_queue_sched_init(struct v3d_dev *v3d, const struct drm_sched_backend_ops *op struct drm_sched_init_args args = { .credit_limit = 1, .timeout = msecs_to_jiffies(500), + .timeout_wq = v3d->reset_wq, .dev = v3d->drm.dev, }; @@ -851,9 +852,13 @@ v3d_sched_init(struct v3d_dev *v3d) { int ret; + v3d->reset_wq = alloc_ordered_workqueue("v3d_reset", 0); + if (!v3d->reset_wq) + return -ENOMEM; + ret = v3d_queue_sched_init(v3d, &v3d_bin_sched_ops, V3D_BIN, "v3d_bin"); if (ret) - return ret; + goto fail; ret = v3d_queue_sched_init(v3d, &v3d_render_sched_ops, V3D_RENDER, "v3d_render"); @@ -896,4 +901,6 @@ v3d_sched_fini(struct v3d_dev *v3d) if (v3d->queue[q].sched.ready) drm_sched_fini(&v3d->queue[q].sched); } + + destroy_workqueue(v3d->reset_wq); } -- cgit v1.2.3 From 3a046db33bb9f28b43a951a7a090db771dc0f8b3 Mon Sep 17 00:00:00 2001 From: Zhefu Zhang Date: Sun, 2 Aug 2026 15:36:54 -0700 Subject: Input: atkbd - skip deactivate for Xiaomi Book Pro 14's internal keyboard The internal keyboard of the Xiaomi Book Pro 14 does not work unless atkbd skips deactivating it at the end of atkbd_probe(). Using 'i8042.dumbkbd=1' also makes the keyboard work, but then the driver never writes to the keyboard at all, so the Caps Lock LED is lost. The atkbd_deactivate_fixup quirk fixes both without a boot parameter. DMI: XIAOMI Xiaomi Book Pro 14/TM2424, BIOS XMAPT4B0P0909 05/06/2026 Signed-off-by: Zhefu Zhang Reviewed-by: Andrew Zhou Link: https://patch.msgid.link/20260802031559.19701-1-a723356@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 8cb4dc6fb165..6c897282d25f 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1938,6 +1938,14 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { }, .callback = atkbd_deactivate_fixup, }, + { + /* Xiaomi Book Pro 14 (TM2424) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"), + DMI_MATCH(DMI_PRODUCT_NAME, "Xiaomi Book Pro 14"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; -- cgit v1.2.3 From 410c44b1096789d0c40fbee706520e981dba7bc1 Mon Sep 17 00:00:00 2001 From: Donglin Lyu Date: Sun, 2 Aug 2026 15:37:18 -0700 Subject: Input: atkbd - skip deactivate for HONOR ZQC-P The internal keyboard on the HONOR ZQC-P (HONOR MagicBook Pro 14 2026) does not work after boot. Using the kernel command line 'i8042.dumbkbd=1' makes the keyboard functional, but the CapsLock LED does not work. Adding the 'atkbd_deactivate_fixup' quirk fixes the keyboard and CapsLock LED natively without requiring boot parameters. DMI: HONOR ZQC-P/ZQC-P-PCB, BIOS 1.09 03/19/2026 Fixes: 9cf6e24c9fbf ("Input: atkbd - do not skip atkbd_deactivate() when skipping ATKBD_CMD_GETID") Signed-off-by: Donglin Lyu Tested-by: Ruslan Shevchenko Link: https://patch.msgid.link/20260801151115.52709-1-donglin_lyu@outlook.com Cc: stable@vger.kernel.org [dtor: keep all HONOR entries together] Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 6c897282d25f..4c82e988260e 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1919,22 +1919,30 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), - DMI_MATCH(DMI_PRODUCT_NAME, "FMB-P"), + DMI_MATCH(DMI_PRODUCT_NAME, "BCC-N"), }, .callback = atkbd_deactivate_fixup, }, { - /* Lenovo Yoga Air 14 (83QK) */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "FMB-P"), }, .callback = atkbd_deactivate_fixup, }, { + /* HONOR MagicBook Pro 14 2026 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), - DMI_MATCH(DMI_PRODUCT_NAME, "BCC-N"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZQC-P"), + }, + .callback = atkbd_deactivate_fixup, + }, + { + /* Lenovo Yoga Air 14 (83QK) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83QK"), }, .callback = atkbd_deactivate_fixup, }, -- cgit v1.2.3 From 075b74841bd0065a3bda3440873c747938e69b68 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 2 Aug 2026 16:24:24 -0700 Subject: Linux 7.2-rc6 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 060834cd0a4d..902f3f3d54b7 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc5 +EXTRAVERSION = -rc6 NAME = Baby Opossum Posse # *DOCUMENTATION* -- cgit v1.2.3 From e5fd3f514e27db1f05fbd72ba615d74941e23c51 Mon Sep 17 00:00:00 2001 From: "Jose Fernandez (Anthropic)" Date: Thu, 30 Jul 2026 22:32:47 +0000 Subject: bpf: tcp: Fix use-after-free in bpf_iter_tcp_established_batch() reqsk_queue_hash_req() publishes a TCP_NEW_SYN_RECV request_sock onto the ehash chain, drops the bucket lock, and only afterwards sets rsk_refcnt to 3. Lockless readers such as __inet_lookup_established() handle this with refcount_inc_not_zero(), but bpf_iter_tcp_established_batch() uses plain sock_hold() while holding the bucket lock, on the assumption that the lock guarantees sk_refcnt > 0. That assumption does not hold for request_sock: CPU 0 CPU 1 ----- ----- tcp_conn_request() reqsk_queue_hash_req() inet_ehash_insert(req) spin_lock(bucket) __sk_nulls_add_node_rcu(req) // rsk_refcnt == 0 spin_unlock(bucket) bpf_iter_tcp_established_batch() spin_lock(bucket) sock_hold(req) <-- addition on 0 spin_unlock(bucket) refcount_set(&req->rsk_refcnt, 3) // clobbers saturated value which surfaces as: refcount_t: addition on 0; use-after-free. WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x48/0x90, CPU#1 Call Trace: bpf_iter_tcp_established_batch+0x14e/0x170 bpf_iter_tcp_batch+0x53/0x200 bpf_iter_tcp_seq_next+0x27/0x70 bpf_seq_read+0x107/0x410 vfs_read+0xb9/0x380 The iterator's stolen reference is lost when the publishing CPU's refcount_set() overwrites the count, leaving the socket one reference short. When the last legitimate owner drops its reference the reqsk is freed while still reachable, leading to use-after-free. This reproduces in seconds with tcp_syncookies=0, a handful of threads doing connect()/close() to a local listener while others read an iter/tcp link in a tight loop. Use refcount_inc_not_zero() and skip the socket on failure. A skipped socket is still part of the bucket, so keep counting it in expected. The reallocations are sized from expected, and a request sock whose refcount gets published while the lock is held across the last realloc must already have room. A skipped socket is counted in expected but never batched, so end_sk can be short of expected on a batch that is actually complete. Decide completeness by whether the walk left any socket behind instead. The WARN after the locked realloc checks the same, replacing an end_sk == expected check that could not hold on that path since commit cdec67a489d4 ("bpf: tcp: Make sure iter->batch always contains a full bucket snapshot"). If every matching socket in a bucket is mid-init (refcount 0), end_sk stays 0. Advance to the next bucket rather than returning a batch entry that was never filled this round. Fixes: 04c7820b776f ("bpf: tcp: Bpf iter batching and lock_sock") Assisted-by: Claude:unspecified Signed-off-by: Jose Fernandez (Anthropic) Reviewed-by: Kuniyuki Iwashima Link: https://lore.kernel.org/bpf/20260730-bpf-iter-tcp-refcnt-v3-1-754b9c8a6717@linux.dev Signed-off-by: Kumar Kartikeya Dwivedi --- net/ipv4/tcp_ipv4.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b8887cdd66c5..7f413f509d7d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -3078,24 +3078,24 @@ static unsigned int bpf_iter_tcp_established_batch(struct seq_file *seq, { struct bpf_tcp_iter_state *iter = seq->private; struct hlist_nulls_node *node; - unsigned int expected = 1; - struct sock *sk; - - sock_hold(*start_sk); - iter->batch[iter->end_sk++].sk = *start_sk; + struct sock *sk = *start_sk; + unsigned int expected = 0; - sk = sk_nulls_next(*start_sk); *start_sk = NULL; sk_nulls_for_each_from(sk, node) { - if (seq_sk_match(seq, sk)) { - if (iter->end_sk < iter->max_sk) { - sock_hold(sk); - iter->batch[iter->end_sk++].sk = sk; - } else if (!*start_sk) { - /* Remember where we left off. */ - *start_sk = sk; - } - expected++; + if (!seq_sk_match(seq, sk)) + continue; + expected++; + if (iter->end_sk < iter->max_sk) { + /* reqsk_queue_hash_req() inserts with sk_refcnt == 0 + * and refcount_set()s it after the bucket lock drops. + */ + if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) + continue; + iter->batch[iter->end_sk++].sk = sk; + } else if (!*start_sk) { + /* Remember where we left off. */ + *start_sk = sk; } } @@ -3133,12 +3133,13 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) struct sock *sk; int err; +again: sk = bpf_iter_tcp_resume(seq); if (!sk) return NULL; /* Done */ expected = bpf_iter_fill_batch(seq, &sk); - if (likely(iter->end_sk == expected)) + if (likely(!sk)) goto done; /* Batch size was too small. */ @@ -3157,7 +3158,7 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) return NULL; /* Done */ expected = bpf_iter_fill_batch(seq, &sk); - if (likely(iter->end_sk == expected)) + if (likely(!sk)) goto done; /* Batch size was still too small. Hold onto the lock while we try @@ -3170,10 +3171,14 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) return ERR_PTR(err); } - expected = bpf_iter_fill_batch(seq, &sk); - WARN_ON_ONCE(iter->end_sk != expected); + bpf_iter_fill_batch(seq, &sk); + WARN_ON_ONCE(sk); done: bpf_iter_tcp_unlock_bucket(seq); + if (unlikely(!iter->end_sk)) { + ++iter->state.bucket; + goto again; + } return iter->batch[0].sk; } -- cgit v1.2.3 From 296736076b3fd078742651c719555a488624023a Mon Sep 17 00:00:00 2001 From: Richard Davies Date: Sun, 2 Aug 2026 17:53:10 -0700 Subject: Input: focaltech - fix array out-of-bounds in focaltech_process_rel_packet Make finger2 (and also finger1) unsigned, so that if the finger index in the packet is 0 then subtracting 1 creates an array index which overflows above the existing check for FOC_MAX_FINGERS, as the existing comment says it should, instead of writing to state->fingers[-1]. Fixes: 05be1d079ec0 ("Input: psmouse - support for the FocalTech PS/2 protocol extensions") Signed-off-by: Richard Davies Link: https://patch.msgid.link/20260701190932.14960-1-richard@arachsys.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/focaltech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 43f9939b7c63..d3ad4af5aa09 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -197,7 +197,7 @@ static void focaltech_process_rel_packet(struct psmouse *psmouse, { struct focaltech_data *priv = psmouse->private; struct focaltech_hw_state *state = &priv->state; - int finger1, finger2; + unsigned int finger1, finger2; state->pressed = packet[0] >> 7; finger1 = ((packet[0] >> 4) & 0x7) - 1; -- cgit v1.2.3 From c422d34a4ad953473b8cc2b232915a4beaf8c3fa Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Sat, 1 Aug 2026 07:55:15 +0000 Subject: smb/client: show compress mount option Example: 1. mount -t cifs -o compress //localhost/test /mnt 2. mount | grep cifs //localhost/test on /mnt type cifs (..., compress, ...) Signed-off-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/cifsfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 1788d93a2522..a1dacc7d8f74 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -692,6 +692,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_puts(s, ",seal"); else if (tcon->ses->server->ignore_signature) seq_puts(s, ",signloosely"); + if (cifs_sb->ctx->compress) + seq_puts(s, ",compress"); if (tcon->nocase) seq_puts(s, ",nocase"); if (tcon->nodelete) -- cgit v1.2.3 From 4986410316b1ae0e63c6ce418e4eb196723626e7 Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Sat, 1 Aug 2026 20:48:09 -0400 Subject: smb: client: Fix use-after-free in cifs_try_adding_channels() cifs_try_adding_channels() takes a temporary reference to an interface before dropping iface_lock. If cifs_ses_add_channel() fails, it drops that reference and then increments iface->weight_fulfilled. A concurrent interface list refresh can remove the list reference while channel creation is in progress. In that case, the failure-path kref_put() releases the last reference and frees iface. Updating weight_fulfilled afterward then accesses freed memory. Increment weight_fulfilled before dropping the temporary reference, keeping iface alive for the final access. Fixes: 6aac002bcfd5 ("cifs: failure to add channel on iface should bump up weight") Cc: stable@vger.kernel.org Signed-off-by: Shuangpeng Bai Signed-off-by: Steve French --- fs/smb/client/sess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c index de2012cc9cf3..7cf7dd104f7c 100644 --- a/fs/smb/client/sess.c +++ b/fs/smb/client/sess.c @@ -233,9 +233,9 @@ int cifs_try_adding_channels(struct cifs_ses *ses) cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n", &iface->sockaddr, rc); - kref_put(&iface->refcount, release_iface); /* failure to add chan should increase weight */ iface->weight_fulfilled++; + kref_put(&iface->refcount, release_iface); continue; } -- cgit v1.2.3 From 4c77b45fa23015d41d7401f684bc22ca585b41fe Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 30 Jul 2026 22:06:25 -0700 Subject: fixp-arith: convert comments to kernel-doc format Insert a hyphen ('-') in 2 places to prevent kernel-doc warnings: Warning: include/linux/fixp-arith.h:42 This comment starts with '/**', but isn't a kernel-doc comment. * __fixp_sin32() returns the sin of an angle in degrees Warning: include/linux/fixp-arith.h:66 This comment starts with '/**', but isn't a kernel-doc comment. * fixp_sin32() returns the sin of an angle in degrees Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260731050625.455556-1-rdunlap@infradead.org Signed-off-by: Dmitry Torokhov --- include/linux/fixp-arith.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h index e485fb0c1201..4d4f4a7d4749 100644 --- a/include/linux/fixp-arith.h +++ b/include/linux/fixp-arith.h @@ -40,7 +40,7 @@ static const s32 sin_table[] = { }; /** - * __fixp_sin32() returns the sin of an angle in degrees + * __fixp_sin32() - returns the sin of an angle in degrees * * @degrees: angle, in degrees, from 0 to 360. * @@ -64,7 +64,7 @@ static inline s32 __fixp_sin32(int degrees) } /** - * fixp_sin32() returns the sin of an angle in degrees + * fixp_sin32() - returns the sin of an angle in degrees * * @degrees: angle, in degrees. The angle can be positive or negative * -- cgit v1.2.3 From 8bec01c80e798eca1ae7863cf29bc6befd759db7 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Fri, 31 Jul 2026 11:35:54 +0800 Subject: ALSA: hda/tas2781: fix ACPI reference handling tas2781_read_acpi() gets a reference to the matching ACPI device and then looks up its first physical device node. After taking a reference to the physical device, it immediately drops the ACPI device reference. However, every later failure jumps to an error path that drops the ACPI device reference a second time. This unbalances the reference count and may prematurely release the ACPI device. In addition, acpi_get_first_physical_node() may return NULL. Without a check, the driver passes the NULL physical device to the property helper calls and may dereference it. Return -ENODEV when no physical device is associated with the ACPI node, and remove the duplicate acpi_dev_put() from the common error path. Fixes: bb5f86ea50ff ("ALSA: hda/tas2781: Add tas2781 hda SPI driver") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/97EA8F29DA0D9AF7+20260731033554.949564-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index 4899ea372798..271c56a79c32 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -343,6 +343,8 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda, strscpy(p->dev_name, hid, sizeof(p->dev_name)); physdev = get_device(acpi_get_first_physical_node(adev)); acpi_dev_put(adev); + if (!physdev) + return -ENODEV; property = "ti,dev-index"; ret = device_property_count_u32(physdev, property); @@ -385,7 +387,6 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda, err: dev_err(p->dev, "read acpi error, ret: %d\n", ret); put_device(physdev); - acpi_dev_put(adev); return ret; } -- cgit v1.2.3 From c973e61895db8fe4a69d8b33de92068d135cafd8 Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Fri, 31 Jul 2026 21:45:05 +0800 Subject: ALSA: usb-audio: Add QUIRK_FLAG_MIXER_GET_CUR_BROKEN for Logitech PRO X 2 LIGHTSPEED The UAC mixer of the Logitech PRO X 2 LIGHTSPEED has broken mixer GET_CUR behavior but otherwise works fine. Add a quirk table entry matching VID/PID=0x046d/0x0af7 and apply the MIXER_GET_CUR_BROKEN quirk flag to make the mixer usable again. Quirky device sample (after applying the quirk flag): usb 3-2.1: New USB device found, idVendor=046d, idProduct=0af7, bcdDevice= 1.00 usb 3-2.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 3-2.1: Product: PRO X 2 LIGHTSPEED usb 3-2.1: Manufacturer: Logitech usb 3-2.1: SerialNumber: 0000000000000000 usb 3-2.1: 2:0: broken mixer GET_CUR (-18944/0/256 => -2662) usb 3-2.1: 6:0: broken mixer GET_CUR (-18944/0/256 => 0) Fixes: 86aa1ea1f15c ("ALSA: usb-audio: Do not expose sticky mixers") Suggested-by: Brian van den Berg Reported-by: Brian van den Berg Link: https://lore.kernel.org/all/370007e6-b73b-4bfc-8410-a860781c7ad7@proton.me/ Signed-off-by: Rong Zhang Link: https://patch.msgid.link/20260731-uac-lg-pro-x-2-ls-v1-1-268eaefe66ab@rong.moe 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 d2a111ee50ab..c90c89200baf 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2256,6 +2256,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { DEVICE_FLG(0x046d, 0x0a8f, /* Logitech H390 headset */ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x046d, 0x0af7, /* Logitech PRO X 2 LIGHTSPEED */ + QUIRK_FLAG_MIXER_GET_CUR_BROKEN), DEVICE_FLG(0x0499, 0x1506, /* Yamaha THR5 */ QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */ -- cgit v1.2.3 From b2d5a81dae385333f9734910277fbf94c78bd17f Mon Sep 17 00:00:00 2001 From: Lin Jiapeng Date: Tue, 28 Jul 2026 15:19:10 +0800 Subject: xfs: fix exchange-range reflink flag clearing issue with INO1_WRITTEN When exchanging two full-file ranges, xmi_can_exchange_reflink_flags() can move the reflink inode flag from the file that currently has it to the other file, as long as exactly one side is marked. This assumes that the file contents, and therefore all shared extents, are exchanged. That assumption is not true when XFS_EXCHMAPS_INO1_WRITTEN is set. xfs_exchmaps_can_skip_mapping() can skip hole and unwritten mappings from file1, so an exchange can complete without moving every mapping that the earlier flag-swap decision accounted for. In that case the post-operation cleanup can clear the reflink flag from an inode that still owns shared written extents. Later writes then take the non-reflink write path and may update blocks that should still have been protected by CoW, which shows up as data corruption between reflink-related files. Fix this by disabling the reflink flag exchange whenever XFS_EXCHMAPS_INO1_WRITTEN is requested. The contents exchange can still proceed; the conservative outcome is that both inodes keep the reflink flag. The regular reflink flag cleanup path can drop the extra flag later once the inode no longer has shared extents. Reported-by: Lin Jiapeng (TencentOS Red Team) Fixes: 966ceafc7a43 ("xfs: create deferred log items for file mapping exchanges") Cc: stable@vger.kernel.org # v6.10 Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Lin Jiapeng Signed-off-by: Carlos Maiolino --- fs/xfs/libxfs/xfs_exchmaps.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c index dcd0bd0b13b4..3efed37cb98a 100644 --- a/fs/xfs/libxfs/xfs_exchmaps.c +++ b/fs/xfs/libxfs/xfs_exchmaps.c @@ -959,6 +959,16 @@ xmi_can_exchange_reflink_flags( { struct xfs_mount *mp = req->ip1->i_mount; + /* + * The INO1_WRITTEN optimization can skip exchanging hole and + * unwritten mappings, which means we cannot guarantee that all + * shared extents actually moved to the other file. Clearing the + * reflink flag of an inode that still holds shared extents breaks + * the CoW write path, so refuse to exchange the flags in that case. + */ + if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN) + return false; + if (hweight32(reflink_state) != 1) return false; if (req->startoff1 != 0 || req->startoff2 != 0) -- cgit v1.2.3 From fb082235c67551d526704e07a4efb82ac3a9ba48 Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Wed, 22 Jul 2026 11:17:59 +0200 Subject: xfs: fix inverted clearance of inode junk flags sashiko.dev noticed that these checks clear all the valid flags instead of invalid. This probably was never hit as it only executed on invalid flag presence. Fixes: 2d295fe65776 ("xfs: repair inode records") Signed-off-by: Andrey Albershteyn Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/inode_repair.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index b88427a4460c..8bc508336aa5 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -1797,7 +1797,7 @@ xrep_inode_flags( /* Clear junk flags */ if (sc->ip->i_diflags & ~XFS_DIFLAG_ANY) - sc->ip->i_diflags &= ~XFS_DIFLAG_ANY; + sc->ip->i_diflags &= XFS_DIFLAG_ANY; /* NEWRTBM only applies to realtime bitmaps */ if (I_INO(sc->ip) == sc->mp->m_sb.sb_rbmino) @@ -1828,7 +1828,7 @@ xrep_inode_flags( /* Clear junk flags. */ if (sc->ip->i_diflags2 & ~XFS_DIFLAG2_ANY) - sc->ip->i_diflags2 &= ~XFS_DIFLAG2_ANY; + sc->ip->i_diflags2 &= XFS_DIFLAG2_ANY; /* No reflink flag unless we support it and it's a file. */ if (!xfs_has_reflink(sc->mp) || !S_ISREG(mode)) -- cgit v1.2.3 From 97efed1091a50e4dbf31307015138b43a972d2e4 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:22:59 -0700 Subject: xfs: only check mergeability of bnobt records In the cntbt (free space by block count) btree, records are not supposed to be in startblock order. Hence the mergeability check is pointless. Remove it, since it does nothing, as LOLLM points out. Cc: stable@vger.kernel.org # v6.4 Fixes: d5784ae82778d9 ("xfs: flag free space btree records that could be merged") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/alloc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c index 48edaa2cb1e0..c666be69f164 100644 --- a/fs/xfs/scrub/alloc.c +++ b/fs/xfs/scrub/alloc.c @@ -136,7 +136,7 @@ xchk_allocbt_rec( const union xfs_btree_rec *rec) { struct xfs_alloc_rec_incore irec; - struct xchk_alloc *ca = bs->private; + struct xchk_alloc *ca = bs->private; xfs_alloc_btrec_to_irec(rec, &irec); if (xfs_alloc_check_irec(to_perag(bs->cur->bc_group), &irec) != NULL) { @@ -144,7 +144,8 @@ xchk_allocbt_rec( return 0; } - xchk_allocbt_mergeable(bs, ca, &irec); + if (bs->sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT) + xchk_allocbt_mergeable(bs, ca, &irec); xchk_allocbt_xref(bs->sc, &irec); return 0; -- cgit v1.2.3 From 5fc643fb86599e29b38e7b2c2680b4b15bf8f772 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:23:15 -0700 Subject: xfs: don't double-lock when deleting a self-referential directory LOLLM notices that the dirtree scrubber can detect a directory that refers to itself. In this case, it's not correct for the directory tree repair code to try to iolock/ilock both sc->ip and dp, because they're the same inode. Fix this by detecting that corner case and handling it appropriately. Cc: stable@vger.kernel.org # v6.10 Fixes: 3f31406aef493b ("xfs: fix corruptions in the directory tree") 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_repair.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/fs/xfs/scrub/dirtree_repair.c b/fs/xfs/scrub/dirtree_repair.c index 1c0d7ea4a5be..bbf6acf6fd40 100644 --- a/fs/xfs/scrub/dirtree_repair.c +++ b/fs/xfs/scrub/dirtree_repair.c @@ -349,6 +349,8 @@ xrep_dirtree_unlink_iolock( ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL); + if (sc->ip == dp) + return 0; if (xfs_ilock_nowait(dp, XFS_IOLOCK_EXCL)) return 0; @@ -400,8 +402,18 @@ xrep_dirtree_unlink( * directory code can handle a reservationless update. */ resblks = xfs_remove_space_res(mp, step->name_len); - error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip, - &resblks, &sc->tp, &dontcare); + if (sc->ip == dp) { +again: + error = xfs_trans_alloc_inode(dp, &M_RES(mp)->tr_remove, + resblks, 0, false, &sc->tp); + if ((error == -ENOSPC || error == -EDQUOT) && resblks > 0) { + resblks = 0; + goto again; + } + } else { + error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip, + &resblks, &sc->tp, &dontcare); + } if (error) goto out_iolock; @@ -489,9 +501,11 @@ out_trans_cancel: xchk_trans_cancel(sc); out_ilock: xfs_iunlock(sc->ip, XFS_ILOCK_EXCL); - xfs_iunlock(dp, XFS_ILOCK_EXCL); + if (dp != sc->ip) + xfs_iunlock(dp, XFS_ILOCK_EXCL); out_iolock: - xfs_iunlock(dp, XFS_IOLOCK_EXCL); + if (dp != sc->ip) + xfs_iunlock(dp, XFS_IOLOCK_EXCL); return error; } -- cgit v1.2.3 From 8af9cd79cdf6ee96ec610d707db22244fa21eb40 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:23:31 -0700 Subject: xfs: don't return EFSCORRUPTED when scrubbing corrupt parent pointers LOLLM noticed that scrub sets the CORRUPT flag when xfs_parent_from_attr thinks it's been given a corrupt parent pointer. This eliminates the potential to repair the filesystem because that error code is bubbled up the call stack. Fix this by collapsing them all to ECANCELED in xchk_parent_pptr, which doesn't have that trait. Cc: stable@vger.kernel.org # v6.10 Fixes: 0d29a20fbdba89 ("xfs: scrub parent pointers") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/parent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c index a8c4807e1d94..99b60773b715 100644 --- a/fs/xfs/scrub/parent.c +++ b/fs/xfs/scrub/parent.c @@ -485,7 +485,7 @@ xchk_parent_scan_attr( valuelen, &parent_ino, NULL); if (error) { xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0); - return error; + return -ECANCELED; } /* No self-referential parent pointers. */ -- cgit v1.2.3 From 5ee37132ea81abd36213b31a72140660c2aac54b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:23:46 -0700 Subject: xfs: don't zap the attr fork on repair when there are queued pptr updates LOLLM noticed that xrep_xattr_rebuild_tree doesn't check for queued parent pointer updates when it decides that it's going to zap the attr fork. This is obviously incorrect, so fix that. We hold the IOLOCK and the ILOCK of sc->ip at that point in time, so we can't race with any /new/ operations. Cc: stable@vger.kernel.org # v6.10 Fixes: e5d7ce0364d8ee ("xfs: replay unlocked parent pointer updates that accrue during xattr repair") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/attr_repair.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/xfs/scrub/attr_repair.c b/fs/xfs/scrub/attr_repair.c index be627ab655ad..6e6af142f1fb 100644 --- a/fs/xfs/scrub/attr_repair.c +++ b/fs/xfs/scrub/attr_repair.c @@ -1427,7 +1427,8 @@ xrep_xattr_rebuild_tree( * If we didn't find any attributes to salvage, repair the file by * zapping its attr fork. */ - if (rx->attrs_found == 0) { + if (rx->attrs_found == 0 && + (!xfs_has_parent(sc->mp) || xfarray_length(rx->pptr_recs) == 0)) { xfs_trans_ijoin(sc->tp, sc->ip, 0); error = xrep_xattr_reset_fork(sc); if (error) -- cgit v1.2.3 From b1a296fc2241f724ef8f14da6a4efa800d444dac Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:24:02 -0700 Subject: xfs: nlink scrub must take IOLOCK before determining ILOCK state In xchk_nlinks_ilock_dir, take the IOLOCK before accessing internal inode state to figure out if we need to take ILOCK shared or exclusive. That way we can't race with directory updates. LOLLM pointed out that the code was initially correct w.r.t. the IOLOCK, but then I broke it. Cc: stable@vger.kernel.org # v6.18 Fixes: f477af0cfa0487 ("xfs: fix locking in xchk_nlinks_collect_dir") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/nlinks.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/xfs/scrub/nlinks.c b/fs/xfs/scrub/nlinks.c index 355ab6de23ea..bcedb8c3e4e6 100644 --- a/fs/xfs/scrub/nlinks.c +++ b/fs/xfs/scrub/nlinks.c @@ -382,6 +382,12 @@ xchk_nlinks_ilock_dir( { uint lock_mode = XFS_ILOCK_SHARED; + /* + * Take the IOLOCK so that other threads cannot start a directory + * update while we're scanning. + */ + xfs_ilock(ip, XFS_IOLOCK_SHARED); + /* * We're going to scan the directory entries, so we must be ready to * pull the data fork mappings into memory if they aren't already. @@ -397,13 +403,8 @@ xchk_nlinks_ilock_dir( xfs_need_iread_extents(&ip->i_af)) lock_mode = XFS_ILOCK_EXCL; - /* - * Take the IOLOCK so that other threads cannot start a directory - * update while we're scanning. - */ - lock_mode |= XFS_IOLOCK_SHARED; xfs_ilock(ip, lock_mode); - return lock_mode; + return lock_mode | XFS_IOLOCK_SHARED; } /* Walk a directory to bump the observed link counts of the children. */ -- cgit v1.2.3 From 0c88e10d12de9ca7cbed1467bb1b52310101bff8 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:24:17 -0700 Subject: xfs: avoid UAF on sc->tempip in xrep_tempfile_create LOLLM noticed a potential UAF if the tempfile creation code fails after it set sc->tempip. Fix that. Cc: stable@vger.kernel.org # v6.10 Fixes: 84c14ee39dd388 ("xfs: create temporary files and directories for online repair") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/tempfile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/scrub/tempfile.c b/fs/xfs/scrub/tempfile.c index e0c630f888cf..98820003b929 100644 --- a/fs/xfs/scrub/tempfile.c +++ b/fs/xfs/scrub/tempfile.c @@ -174,6 +174,7 @@ out_release_inode: xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL); xfs_finish_inode_setup(sc->tempip); xchk_irele(sc, sc->tempip); + sc->tempip = NULL; } out_release_dquots: xfs_qm_dqrele(udqp); -- cgit v1.2.3 From 7cdafd8f10ebdf745ba6046b9fa67490c343a17f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:24:33 -0700 Subject: xfs: hoist per-bucket unlinked list check to helper In the next patch we're going to make this loop more exciting, so hoist the code to a helper function to reduce clutter in the resulting code. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader.c | 64 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 62ed5eaf08fb..cecf034ef989 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -932,6 +932,42 @@ xchk_agi_xref( /* scrub teardown will take care of sc->sa for us */ } +/* + * Walk the incore unlinked list for a particular AGI bucket to construct + * the unlinked inode bitmap for later reconstruction of the unlinked list. + * Returns 1 if we should keep checking, or 0 to stop checking. + */ +static int +xchk_iunlink_bucket( + struct xfs_scrub *sc, + unsigned int bucket, + xfs_agino_t agino) +{ + while (agino != NULLAGINO) { + struct xfs_inode *ip; + + if (agino % XFS_AGI_UNLINKED_BUCKETS != bucket) { + xchk_block_set_corrupt(sc, sc->sa.agi_bp); + return 0; + } + + ip = xfs_iunlink_lookup(sc->sa.pag, agino); + if (!ip) { + xchk_block_set_corrupt(sc, sc->sa.agi_bp); + return 0; + } + + if (!xfs_inode_on_unlinked_list(ip)) { + xchk_block_set_corrupt(sc, sc->sa.agi_bp); + return 0; + } + + agino = ip->i_next_unlinked; + } + + return 1; +} + /* * Check the unlinked buckets for links to bad inodes. We hold the AGI, so * there cannot be any threads updating unlinked list pointers in this AG. @@ -942,30 +978,14 @@ xchk_iunlink( struct xfs_agi *agi) { unsigned int i; - struct xfs_inode *ip; for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) { - xfs_agino_t agino = be32_to_cpu(agi->agi_unlinked[i]); - - while (agino != NULLAGINO) { - if (agino % XFS_AGI_UNLINKED_BUCKETS != i) { - xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return; - } - - ip = xfs_iunlink_lookup(sc->sa.pag, agino); - if (!ip) { - xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return; - } - - if (!xfs_inode_on_unlinked_list(ip)) { - xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return; - } - - agino = ip->i_next_unlinked; - } + int ret; + + ret = xchk_iunlink_bucket(sc, i, + be32_to_cpu(agi->agi_unlinked[i])); + if (ret < 1) + return; } } -- cgit v1.2.3 From 527eaaefddb6ec5c83a06c9a1559960dd6361753 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:24:48 -0700 Subject: xfs: don't livelock in scrub on a circular unlinked list LOLLM points out that online fsck can livelock if an unlinked inode list contains a loop. Use a bitmap to detect cycles. Cc: stable@vger.kernel.org # v4.15 Fixes: a12890aebb8959 ("xfs: scrub the AGI") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader.c | 46 +++++++++++++++++++++++++++++++++--------- fs/xfs/scrub/agheader_repair.c | 17 ++++++++++++++-- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index cecf034ef989..1fa66aa68e16 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -18,6 +18,8 @@ #include "xfs_inode.h" #include "scrub/scrub.h" #include "scrub/common.h" +#include "scrub/bitmap.h" +#include "scrub/agino_bitmap.h" int xchk_setup_agheader( @@ -935,7 +937,8 @@ xchk_agi_xref( /* * Walk the incore unlinked list for a particular AGI bucket to construct * the unlinked inode bitmap for later reconstruction of the unlinked list. - * Returns 1 if we should keep checking, or 0 to stop checking. + * Returns 1 if we should keep checking, 0 to stop checking, or a negative + * errno. */ static int xchk_iunlink_bucket( @@ -943,36 +946,57 @@ xchk_iunlink_bucket( unsigned int bucket, xfs_agino_t agino) { + struct xagino_bitmap seen; + int ret; + + xagino_bitmap_init(&seen); + while (agino != NULLAGINO) { struct xfs_inode *ip; + unsigned int len = 1; if (agino % XFS_AGI_UNLINKED_BUCKETS != bucket) { xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return 0; + goto bad; + } + + if (xagino_bitmap_test(&seen, agino, &len)) { + xchk_block_set_corrupt(sc, sc->sa.agi_bp); + goto bad; } ip = xfs_iunlink_lookup(sc->sa.pag, agino); if (!ip) { xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return 0; + goto bad; } if (!xfs_inode_on_unlinked_list(ip)) { xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return 0; + goto bad; } + ret = xagino_bitmap_set(&seen, agino, 1); + if (ret) + goto out_bitmap; + agino = ip->i_next_unlinked; } - - return 1; + ret = 1; + +out_bitmap: + xagino_bitmap_destroy(&seen); + return ret; +bad: + ret = 0; + goto out_bitmap; } /* * Check the unlinked buckets for links to bad inodes. We hold the AGI, so * there cannot be any threads updating unlinked list pointers in this AG. */ -STATIC void +STATIC int xchk_iunlink( struct xfs_scrub *sc, struct xfs_agi *agi) @@ -985,8 +1009,10 @@ xchk_iunlink( ret = xchk_iunlink_bucket(sc, i, be32_to_cpu(agi->agi_unlinked[i])); if (ret < 1) - return; + return ret; } + + return 0; } /* Scrub the AGI. */ @@ -1073,7 +1099,9 @@ xchk_agi( if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount)) xchk_block_set_corrupt(sc, sc->sa.agi_bp); - xchk_iunlink(sc, agi); + error = xchk_iunlink(sc, agi); + if (error) + goto out; xchk_agi_xref(sc); out: diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 2554494847ff..13074d5e319c 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1080,18 +1080,22 @@ xrep_iunlink_walk_ondisk_bucket( struct xrep_agi *ragi, unsigned int bucket) { + struct xagino_bitmap seen; struct xfs_scrub *sc = ragi->sc; struct xfs_agi *agi = sc->sa.agi_bp->b_addr; xfs_agino_t prev_agino = NULLAGINO; xfs_agino_t next_agino; int error = 0; + xagino_bitmap_init(&seen); + next_agino = be32_to_cpu(agi->agi_unlinked[bucket]); while (next_agino != NULLAGINO) { xfs_agino_t agino = next_agino; + unsigned int len = 1; if (xchk_should_terminate(ragi->sc, &error)) - return error; + goto out_bitmap; trace_xrep_iunlink_walk_ondisk_bucket(sc->sa.pag, bucket, prev_agino, agino); @@ -1099,15 +1103,24 @@ xrep_iunlink_walk_ondisk_bucket( if (bucket != agino % XFS_AGI_UNLINKED_BUCKETS) break; + if (xagino_bitmap_test(&seen, agino, &len)) + break; + next_agino = xrep_iunlink_next(sc, agino); if (!next_agino) next_agino = xrep_iunlink_reload_next(ragi, prev_agino, agino); + error = xagino_bitmap_set(&seen, agino, 1); + if (error) + goto out_bitmap; + prev_agino = agino; } - return 0; +out_bitmap: + xagino_bitmap_destroy(&seen); + return error; } /* Decide if this is an unlinked inode in this AG. */ -- cgit v1.2.3 From 1e96a00e0d3a00be6e4f368b2f18e2d345f813ce Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:25:04 -0700 Subject: xfs: don't walk off the end of a null sc->sa.agi_bp in AGI repair LOLLM noticed a longstanding bug where xrep_iunlink_walk_ondisk_bucket tries to walk ragi->sc->sa.agi_bp to rebuild the unlinked inode lists. Unfortunately, it's possible for agi_bp to be null if the buffer verifier fails, so we have to use ragi->agi_bp (which skips verifier checks) instead. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 13074d5e319c..39468b8fa986 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1082,7 +1082,7 @@ xrep_iunlink_walk_ondisk_bucket( { struct xagino_bitmap seen; struct xfs_scrub *sc = ragi->sc; - struct xfs_agi *agi = sc->sa.agi_bp->b_addr; + struct xfs_agi *agi = ragi->agi_bp->b_addr; xfs_agino_t prev_agino = NULLAGINO; xfs_agino_t next_agino; int error = 0; -- cgit v1.2.3 From 6b9cd540138a06660a843a519facc147060acbef Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:25:19 -0700 Subject: xfs: load next_agino from the correct xfarray in xrep_iunlink_relink_prev LOLLM notices that xrep_iunlink_relink_prev has the comment "set the forward pointer..." but then loads the value from the xfarray that stores pointers to the previous inode in the unlinked list. That's wrong, so fix the variable access. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 39468b8fa986..5204eab09c64 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1619,7 +1619,7 @@ xrep_iunlink_relink_prev( want_rele = true; /* Set the forward pointer since this just came off disk. */ - error = xfarray_load(ragi->iunlink_prev, agino, &next_agino); + error = xfarray_load(ragi->iunlink_next, agino, &next_agino); if (error) goto out_rele; -- cgit v1.2.3 From 5644fab990fc72406dddc91cbb8304659d77f3f1 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:25:35 -0700 Subject: xfs: pass runtime errors from xrep_iunlink_mark_ondisk_rec up to callers LOLLM points out that the only error that xrep_iunlink_mark_ondisk_rec returns is ENOMEM, but we ignore that, and can end up writing a garbage AGI based on incomplete information. We shouldn't do that, though here we must be screen out EFSCORRUPTED/EFSBASDCRC because we haven't checked the inobt yet. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 5204eab09c64..7f1955484303 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1309,7 +1309,7 @@ xrep_iunlink_mark_ondisk_rec( * iunlink_bmp. We haven't checked the inobt yet, so we don't error out if * the btree is corrupt. */ -STATIC void +STATIC int xrep_iunlink_mark_ondisk( struct xrep_agi *ragi) { @@ -1321,6 +1321,14 @@ xrep_iunlink_mark_ondisk( cur = xfs_inobt_init_cursor(sc->sa.pag, sc->tp, agi_bp); error = xfs_btree_query_all(cur, xrep_iunlink_mark_ondisk_rec, ragi); xfs_btree_del_cursor(cur, error); + + /* + * Don't proceed if we couldn't set a bit in the bitmap. All other + * errors we ignore because we haven't actually checked the inobt yet. + */ + if (error == -ENOMEM) + return -ENOMEM; + return 0; } /* @@ -1508,7 +1516,9 @@ xrep_iunlink_rebuild_buckets( * If there are ondisk inodes that are unlinked and are not been loaded * into cache, record them in iunlink_bmp. */ - xrep_iunlink_mark_ondisk(ragi); + error = xrep_iunlink_mark_ondisk(ragi); + if (error) + return error; /* * Walk each iunlink bucket to (re)construct as much of the incore list -- cgit v1.2.3 From 2daf3ed5d059dec79c123aec42eb8d28e0c016d4 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:25:50 -0700 Subject: xfs: check xfarray iteration errors when committing unlinked inode lists LOLLM noticed that we neglect to check for xfarray_iter itself returning errors when writing a new AGI. Fix that. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 7f1955484303..8514d2d7e3ce 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1675,6 +1675,8 @@ xrep_iunlink_commit( if (error) return error; } + if (error < 0) + return error; /* Fix all the back links */ idx = XFARRAY_CURSOR_INIT; @@ -1683,6 +1685,8 @@ xrep_iunlink_commit( if (error) return error; } + if (error < 0) + return error; /* Copy the staged iunlink buckets to the new AGI. */ for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) { -- cgit v1.2.3 From 68ab37650ce5195b4f4f8466444a36a78207840c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:26:06 -0700 Subject: xfs: fix allocated inodes that show up in the unlinked list If an allocated inode shows up in the unlinked list, we need to get it completely off the list. Set the corrected next/prev pointers such that the inode will not look like it should be on an unlinked list at all. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 63 +++++++++++++++++++++++++++++++++++++----- fs/xfs/scrub/trace.h | 1 + 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 8514d2d7e3ce..41de5cf87352 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -979,6 +979,13 @@ err: return error; } +/* + * Magic value that means "not unlinked" because xfarrays don't support storing + * totally zeroed elements. There can't be a cluster that starts in daddr 0 so + * there can't be an inode #1 either. + */ +#define LINKED_AGINO (0x1) + /* * Record a forwards unlinked chain pointer from agino -> next_agino in our * staging information. @@ -1362,6 +1369,35 @@ xrep_iunlink_resolve_bucket( break; } + if (VFS_I(ip)->i_nlink != 0) { + /* + * Inode is linked somewhere! Blow out both unlinked + * list pointers, advance the list, and pretend we + * didn't see this inode. Clear it from iunlink_bmp + * because it's linked. + */ + trace_xrep_iunlink_resolve_allocated(sc->sa.pag, + bucket, prev_agino, next_agino); + + error = xrep_iunlink_store_next(ragi, next_agino, + NULLAGINO); + if (error) + return error; + + error = xrep_iunlink_store_prev(ragi, next_agino, + LINKED_AGINO); + if (error) + return error; + + error = xagino_bitmap_clear(&ragi->iunlink_bmp, + next_agino, 1); + if (error) + return error; + + next_agino = ip->i_next_unlinked; + continue; + } + if (next_agino % XFS_AGI_UNLINKED_BUCKETS != bucket) { /* * Inode is in the wrong bucket. Advance the list, @@ -1540,6 +1576,24 @@ xrep_iunlink_rebuild_buckets( xrep_iunlink_add_lost_inodes, ragi); } +static inline void +set_inode_prev_unlinked( + struct xfs_inode *ip, + xfs_agino_t prev_agino) +{ + /* + * Magic value that means "not unlinked" because xfarrays don't support + * storing totally zeroed elements. + */ + if (prev_agino == LINKED_AGINO) + prev_agino = 0; + + if (ip->i_prev_unlinked != prev_agino) { + trace_xrep_iunlink_relink_prev(ip, prev_agino); + ip->i_prev_unlinked = prev_agino; + } +} + /* Update i_next_iunlinked for the inode @agino. */ STATIC int xrep_iunlink_relink_next( @@ -1573,8 +1627,7 @@ xrep_iunlink_relink_next( if (error) goto out_rele; - trace_xrep_iunlink_relink_prev(ip, prev_agino); - ip->i_prev_unlinked = prev_agino; + set_inode_prev_unlinked(ip, prev_agino); } /* Update the forward pointer. */ @@ -1641,11 +1694,7 @@ xrep_iunlink_relink_prev( ip->i_next_unlinked = next_agino; } - /* Update the backward pointer. */ - if (ip->i_prev_unlinked != prev_agino) { - trace_xrep_iunlink_relink_prev(ip, prev_agino); - ip->i_prev_unlinked = prev_agino; - } + set_inode_prev_unlinked(ip, prev_agino); out_rele: /* diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index d5d39d82749e..00fbe1b9c235 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -3542,6 +3542,7 @@ DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_uncached); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_wronglist); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_nolist); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_ok); +DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_allocated); TRACE_EVENT(xrep_iunlink_relink_next, TP_PROTO(struct xfs_inode *ip, xfs_agino_t next_agino), -- cgit v1.2.3 From 6d67c6b99f1fc07c64b97fcbc974c6f1ada7f622 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:26:21 -0700 Subject: xfs: fix another iunlink infinite loop bug in online fsck xrep_iunlink_resolve_bucket is supposed to reconstruct as much of the incore prev and next unlinked list pointers based on what it finds on disk and in memory before we move on to relinking the truly lost inodes back into the unlinked list. However, it's still vulnerable to infinite loops that come in via the next_unlinked pointers. Fix this problem by remembering which inodes we've already seen and checking new agino pointers against that. If a bit is already set, either this is a loop or the inode has nonzero link count. We'll deal with the second case in a subsequent patch. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 37 ++++++++++++++++++++++++++++--------- fs/xfs/scrub/trace.h | 1 + 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 41de5cf87352..65b9a8befce9 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1348,15 +1348,32 @@ xrep_iunlink_resolve_bucket( struct xrep_agi *ragi, unsigned int bucket) { + struct xagino_bitmap seen; struct xfs_scrub *sc = ragi->sc; struct xfs_inode *ip; xfs_agino_t prev_agino = NULLAGINO; xfs_agino_t next_agino = ragi->iunlink_heads[bucket]; int error = 0; + xagino_bitmap_init(&seen); + while (next_agino != NULLAGINO) { + unsigned int len = 1; + if (xchk_should_terminate(ragi->sc, &error)) - return error; + goto out_bitmap; + + /* Inode already seen? We're stuck in a loop */ + if (xagino_bitmap_test(&seen, next_agino, &len)) { + trace_xrep_iunlink_resolve_infinite_loop(sc->sa.pag, + bucket, prev_agino, next_agino); + next_agino = NULLAGINO; + break; + } + + error = xagino_bitmap_set(&seen, next_agino, 1); + if (error) + goto out_bitmap; /* Find the next inode in the chain. */ ip = xfs_iunlink_lookup(sc->sa.pag, next_agino); @@ -1382,17 +1399,17 @@ xrep_iunlink_resolve_bucket( error = xrep_iunlink_store_next(ragi, next_agino, NULLAGINO); if (error) - return error; + goto out_bitmap; error = xrep_iunlink_store_prev(ragi, next_agino, LINKED_AGINO); if (error) - return error; + goto out_bitmap; error = xagino_bitmap_clear(&ragi->iunlink_bmp, next_agino, 1); if (error) - return error; + goto out_bitmap; next_agino = ip->i_next_unlinked; continue; @@ -1433,20 +1450,20 @@ xrep_iunlink_resolve_bucket( */ error = xagino_bitmap_clear(&ragi->iunlink_bmp, next_agino, 1); if (error) - return error; + goto out_bitmap; /* Remember the previous inode's next pointer. */ if (prev_agino != NULLAGINO) { error = xrep_iunlink_store_next(ragi, prev_agino, next_agino); if (error) - return error; + goto out_bitmap; } /* Remember this inode's previous pointer. */ error = xrep_iunlink_store_prev(ragi, next_agino, prev_agino); if (error) - return error; + goto out_bitmap; /* Advance the list and remember this inode. */ prev_agino = next_agino; @@ -1457,10 +1474,12 @@ xrep_iunlink_resolve_bucket( if (prev_agino != NULLAGINO) { error = xrep_iunlink_store_next(ragi, prev_agino, next_agino); if (error) - return error; + goto out_bitmap; } - return 0; +out_bitmap: + xagino_bitmap_destroy(&seen); + return error; } /* Reinsert this unlinked inode into the head of the staged bucket list. */ diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 00fbe1b9c235..14aa0ec1f09e 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -3538,6 +3538,7 @@ DEFINE_EVENT(xrep_iunlink_resolve_class, name, \ TP_PROTO(const struct xfs_perag *pag, unsigned int bucket, \ xfs_agino_t prev_agino, xfs_agino_t next_agino), \ TP_ARGS(pag, bucket, prev_agino, next_agino)) +DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_infinite_loop); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_uncached); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_wronglist); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_nolist); -- cgit v1.2.3 From af146cb7ff8ff5c54162f35c238f3ff1d5ad110f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:26:37 -0700 Subject: xfs: set the prev pointer when reinserting an inode on the unlinked list If we find a rogue free inode and decide to reinsert it into the unlinked list, we need to set the prev pointer to NULLAGINO so that the incore list gets updated. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 65b9a8befce9..4f1f235349c5 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1504,6 +1504,10 @@ xrep_iunlink_add_to_bucket( if (error) return error; + error = xrep_iunlink_store_prev(ragi, agino, NULLAGINO); + if (error) + return error; + /* Remember the head inode's previous pointer. */ if (current_head != NULLAGINO) { error = xrep_iunlink_store_prev(ragi, current_head, agino); -- cgit v1.2.3 From 0052633527158b49762ab427e73924e4f8d25e6c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:26:52 -0700 Subject: xfs: don't ignore runtime errors in xrep_iunlink_reload_next LOLLM complained that this function ignores runtime errors being returned by xrep_iunlink_store_*. Rework the function signature so that we can return runtime errors to abort the repair. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 4f1f235349c5..2104512f1ee1 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1041,31 +1041,40 @@ xrep_iunlink_next( * the chain or if we should stop walking the chain due to corruption; or a * per-AG inode number. */ -STATIC xfs_agino_t +STATIC int xrep_iunlink_reload_next( struct xrep_agi *ragi, xfs_agino_t prev_agino, - xfs_agino_t agino) + xfs_agino_t agino, + xfs_agino_t *next_agino) { struct xfs_scrub *sc = ragi->sc; struct xfs_inode *ip; - xfs_agino_t ret = NULLAGINO; int error; + *next_agino = NULLAGINO; + error = xchk_iget(ragi->sc, xfs_agino_to_ino(sc->sa.pag, agino), &ip); if (error) - return ret; + return 0; trace_xrep_iunlink_reload_next(ip, prev_agino); /* If this is a linked inode, stop processing the chain. */ if (VFS_I(ip)->i_nlink != 0) { - xrep_iunlink_store_next(ragi, agino, NULLAGINO); + error = xrep_iunlink_store_next(ragi, agino, NULLAGINO); + if (error) + return error; + + error = xrep_iunlink_store_prev(ragi, agino, LINKED_AGINO); + if (error) + return error; + goto rele; } ip->i_prev_unlinked = prev_agino; - ret = ip->i_next_unlinked; + *next_agino = ip->i_next_unlinked; /* * Drop the inode reference that we just took. We hold the AGI, so @@ -1074,7 +1083,7 @@ xrep_iunlink_reload_next( */ rele: xchk_irele(sc, ip); - return ret; + return 0; } /* @@ -1114,9 +1123,12 @@ xrep_iunlink_walk_ondisk_bucket( break; next_agino = xrep_iunlink_next(sc, agino); - if (!next_agino) - next_agino = xrep_iunlink_reload_next(ragi, prev_agino, - agino); + if (!next_agino) { + error = xrep_iunlink_reload_next(ragi, prev_agino, + agino, &next_agino); + if (error) + break; + } error = xagino_bitmap_set(&seen, agino, 1); if (error) -- cgit v1.2.3 From 63320a0f70f66f311f4bccff3af0719c2119f46c Mon Sep 17 00:00:00 2001 From: Long Li Date: Mon, 27 Jul 2026 10:38:48 +0800 Subject: xfs: fix ilock leak on error in xfs_dq_get_next_id xfs_dq_get_next_id() takes the quota inode ILOCK before calling xfs_iread_extents(). If xfs_iread_extents() fails, the function returns immediately without releasing the lock, leaking the quota inode ILOCK. This can leave the quota inode locked and cause subsequent quota operations to hang. Fix this by jumping to a common unlock path on error instead of returning directly. Fixes: bda250dbaf39f ("xfs: rewrite xfs_dq_get_next_id using xfs_iext_lookup_extent") Cc: stable@vger.kernel.org # v4.12 Signed-off-by: Long Li Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_dquot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index c311f61d9554..b4f6c594808c 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -778,7 +778,7 @@ xfs_dq_get_next_id( lock_flags = xfs_ilock_data_map_shared(quotip); error = xfs_iread_extents(NULL, quotip, XFS_DATA_FORK); if (error) - return error; + goto out_unlock; if (xfs_iext_lookup_extent(quotip, "ip->i_df, start, &cur, &got)) { /* contiguous chunk, bump startoff for the id calculation */ @@ -789,6 +789,7 @@ xfs_dq_get_next_id( error = -ENOENT; } +out_unlock: xfs_iunlock(quotip, lock_flags); return error; -- cgit v1.2.3 From e2b4a856085e9bd939bde2dee0d08b1d41babde9 Mon Sep 17 00:00:00 2001 From: Long Li Date: Mon, 27 Jul 2026 10:38:49 +0800 Subject: xfs: don't swallow dquot recovery verification errors xlog_recover_dquot_commit_pass2() validates the recovered dquot with xfs_dqblk_verify() and, on failure, sets error = -EFSCORRUPTED and jumps to out_release. But out_release unconditionally returns 0, so the corruption error is discarded: the caller xlog_recover_items_pass2() sees success, log recovery proceeds as if the dquot were valid, and the corrupt quota buffer can be written back to disk. Fixes: 9c235dfc3d3f ("xfs: dquot recovery does not validate the recovered dquot") Cc: stable@vger.kernel.org # v6.8 Signed-off-by: Long Li Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_dquot_item_recover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index fe419b28de22..63bc9ab7d947 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -173,7 +173,7 @@ xlog_recover_dquot_commit_pass2( out_release: xfs_buf_relse(bp); - return 0; + return error; } const struct xlog_recover_item_ops xlog_dquot_item_ops = { -- cgit v1.2.3 From 63de19199342e2598373cbb99186fa93e9116603 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 29 Jul 2026 15:02:48 +0200 Subject: xfs: add a separate bio_set for spliting GC writes Allocating the new bio for a split from the same pool as the original one can deadlock under memory pressure as the origin bio could be the last one from the mempool. Add a separate pool for splitting GC write bios to avoid this. Fixes: 080d01c41d44 ("xfs: implement zoned garbage collection") Signed-off-by: Christoph Hellwig Reviewed-by: Damien Le Moal Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_zone_gc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index f76a09130852..e4f70e024632 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -130,6 +130,9 @@ struct xfs_zone_gc_data { /* bioset used to allocate the gc_bios */ struct bio_set bio_set; + /* bioset used when writes need to be split to hardware limits */ + struct bio_set split_bio_set; + /* * Scratchpad to buffer GC data, organized as a ring buffer over * discontiguous folios. scratch_head is where the buffer is filled, @@ -221,6 +224,9 @@ xfs_zone_gc_data_alloc( if (bioset_init(&data->bio_set, 16, offsetof(struct xfs_gc_bio, bio), BIOSET_NEED_BVECS)) goto out_free_recs; + if (bioset_init(&data->split_bio_set, 16, + offsetof(struct xfs_gc_bio, bio), 0)) + goto out_exit_bio_set; for (i = 0; i < XFS_GC_NR_BUFS; i++) { data->scratch_folios[i] = folio_alloc(GFP_KERNEL, get_order(XFS_GC_BUF_SIZE)); @@ -238,6 +244,8 @@ xfs_zone_gc_data_alloc( out_free_scratch: while (--i >= 0) folio_put(data->scratch_folios[i]); + bioset_exit(&data->split_bio_set); +out_exit_bio_set: bioset_exit(&data->bio_set); out_free_recs: kfree(data->iter.recs); @@ -254,6 +262,7 @@ xfs_zone_gc_data_free( for (i = 0; i < XFS_GC_NR_BUFS; i++) folio_put(data->scratch_folios[i]); + bioset_exit(&data->split_bio_set); bioset_exit(&data->bio_set); kfree(data->iter.recs); kfree(data); @@ -810,7 +819,8 @@ xfs_zone_gc_split_write( data->mp->m_sb.sb_blocksize) >> SECTOR_SHIFT; split_len = split_sectors << SECTOR_SHIFT; - split = bio_split(&chunk->bio, split_sectors, GFP_NOFS, &data->bio_set); + split = bio_split(&chunk->bio, split_sectors, GFP_NOFS, + &data->split_bio_set); split_chunk = container_of(split, struct xfs_gc_bio, bio); split_chunk->data = data; ihold(VFS_I(chunk->ip)); -- cgit v1.2.3 From ec6978e6bf68fb6f51edd4f700f9a554dc0da894 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 29 Jul 2026 15:02:49 +0200 Subject: xfs: add a comment to describe xfs_gc_bio.victim_rtg All other fields have comments describing them, add one for this field as well. Signed-off-by: Christoph Hellwig Reviewed-by: Damien Le Moal Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_zone_gc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index e4f70e024632..7ab8f2218c6a 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -103,6 +103,7 @@ struct xfs_gc_bio { /* Open Zone being written to */ struct xfs_open_zone *oz; + /* Realtime group currently being reclaimed */ struct xfs_rtgroup *victim_rtg; /* Bio used for reads and writes, including the bvec used by it */ -- cgit v1.2.3 From eb6b2cc1fc8ad566d746d128a559989ff0bba5cc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 29 Jul 2026 15:00:58 +0200 Subject: xfs: check v5 superblock features early When working on a new features that reuses the existing pad in the superblock, I noticed that mounting such a file system on an old kernel logs a rather confusing warning: XFS (vdc): Metadir superblock padding fields must be zero. This is because we only validate the various feature fields in v5 superblocks after the common superblock validation helper is called. Fix this by calling the feature validation first. Fixes: eca383fcd63b ("xfs: refactor superblock verifiers") Cc: # v4.19 Signed-off-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/libxfs/xfs_sb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c index 47322adb7690..75f2a021ee6d 100644 --- a/fs/xfs/libxfs/xfs_sb.c +++ b/fs/xfs/libxfs/xfs_sb.c @@ -1118,10 +1118,10 @@ xfs_sb_read_verify( * because _verify_common checks the on-disk values. */ __xfs_sb_from_disk(&sb, dsb, false); - error = xfs_validate_sb_common(mp, bp, &sb); + error = xfs_validate_sb_read(mp, &sb); if (error) goto out_error; - error = xfs_validate_sb_read(mp, &sb); + error = xfs_validate_sb_common(mp, bp, &sb); out_error: if (error == -EFSCORRUPTED || error == -EFSBADCRC) -- cgit v1.2.3 From 496e0f706b8ad7831bf7189ff9deb8701d2ebc2b Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:28 +0200 Subject: KVM: s390: Fix unlikely NULL gmap dereference When creating a new vCPU, kvm_vm_ioctl_create_vcpu() will call kvm_arch_vcpu_postcreate() after the file descriptor for the new vCPU has been created. The new file descriptor has not been returned yet, but a malicious userspace program could try to guess it. If a malicious userspace program manages to start the newly created vCPU before kvm_arch_vcpu_postcreate() is called, __vcpu_run() will try to dereference vcpu->arch.gmap and trigger a NULL pointer dereference. Fix this by adding a new field to struct kvm_vcpu_arch to keep track of the initialization status of the vCPU. Refuse to run a vCPU that is not fully initialized. Fixes: dafd032a15f8 ("KVM: s390: move vcpu specific initalization to a later point") Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Reviewed-by: Steffen Eiden Reviewed-by: Janosch Frank Reviewed-by: Christian Borntraeger Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-2-imbrenda@linux.ibm.com> --- arch/s390/include/asm/kvm_host.h | 1 + arch/s390/kvm/kvm-s390.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index c172f9b212d1..b4182ca4435f 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -440,6 +440,7 @@ struct kvm_vcpu_arch { bool skey_enabled; /* Indicator if the access registers have been loaded from guest */ bool acrs_loaded; + bool initialized; struct kvm_s390_pv_vcpu pv; union diag318_info diag318_info; struct kvm_s390_mmu_cache *mc; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 150b5dd2170e..f86b4b0b356f 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3613,6 +3613,9 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) if (test_kvm_facility(vcpu->kvm, 74) || vcpu->kvm->arch.user_instr0 || vcpu->kvm->arch.user_operexec) vcpu->arch.sie_block->ictl |= ICTL_OPEREXC; + + /* Pairs with smp_load_acquire() in kvm_arch_vcpu_ioctl_run() and kvm_arch_vcpu_ioctl() */ + smp_store_release(&vcpu->arch.initialized, true); } static bool kvm_has_pckmo_subfunc(struct kvm *kvm, unsigned long nr) @@ -5039,6 +5042,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) kvm_run->kvm_dirty_regs & ~KVM_SYNC_S390_VALID_FIELDS) return -EINVAL; + /* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */ + if (!smp_load_acquire(&vcpu->arch.initialized)) + return -EINVAL; + vcpu_load(vcpu); if (guestdbg_exit_pending(vcpu)) { @@ -5523,6 +5530,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp, long r; u16 rc, rrc; + /* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */ + if (!smp_load_acquire(&vcpu->arch.initialized)) + return -EINVAL; + vcpu_load(vcpu); switch (ioctl) { -- cgit v1.2.3 From 1423415471274abda87024967d7fe2206ceee0ea Mon Sep 17 00:00:00 2001 From: Ryan Wilbur Date: Thu, 30 Jul 2026 16:39:20 -0300 Subject: serial: 8250_of: clear stuck empty-FIFO RX-timeout on LPC32xx The NXP LPC32xx UART (PORT_LPC3220) can latch an RX character-timeout interrupt while the RX FIFO is empty: IIR reports UART_IIR_RX_TIMEOUT (0x0c) but LSR.DR is clear. A character timeout is only cleared by reading RHR, but serial8250_rx_chars() reads RHR only when LSR.DR is set, so nothing ever clears the condition. The interrupt is level-triggered and re-fires immediately, so on a single-core ARM926 the resulting interrupt storm livelocks the CPU. It is reproducible when userspace repeatedly opens the front-panel port (ttyS1): serial8250_do_set_termios() re-enables interrupts on unlock and the handler then spins forever with iir=0xcc lsr=0x60 ier=0x05, tripping the soft-lockup detector in serial8250_handle_irq_locked(). LPC32xx has no dedicated 8250 glue driver, it's driven by the generic 8250_of. Add a hardware specific handle_irq for PORT_LPC3220, wired up in of_platform_serial_setup() the same way fsl8250_handle_irq is installed. The handler follows dw8250_handle_irq(): on an RX timeout with an empty FIFO (LSR.DR and LSR.BI clear) it does one throwaway RHR read to clear the condition, then calls serial8250_handle_irq_locked(). No real received data is ever discarded, and it is a no-op on healthy UARTs which never report a timeout with DR clear. This is the same class of bug already worked around in other 8250 drivers; see commit 424d79183af0 ("serial: 8250_dw: Avoid "too much work" from bogus rx timeout interrupt") which reports the identical iir=0xcc/lsr=0x60. See also UART_RX_TIMEOUT_QUIRK in 8250_omap, and the note in 8250_bcm7271. Cc: stable Assisted-by: Claude:Opus4.8 Signed-off-by: Ryan Wilbur Link: https://patch.msgid.link/20260730193920.28954-1-rwilbur633@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 81644d40b09a..160f4c71de06 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -81,6 +81,40 @@ static int of_platform_serial_clk_notifier_cb(struct notifier_block *nb, unsigne return NOTIFY_DONE; } +static int lpc32xx_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir; + u16 status; + + guard(serial8250_rpm)(up); + + iir = serial_port_in(port, UART_IIR); + if (iir & UART_IIR_NO_INT) + return 0; + + guard(uart_port_lock_check_sysrq_irqsave)(port); + + /* + * The LPC32xx UART can assert an RX character-timeout interrupt while + * the RX FIFO is empty: IIR reports UART_IIR_RX_TIMEOUT but LSR.DR is + * clear. The timeout is only cleared by reading RHR, but the core RX + * path skips that read when the FIFO is empty, so the level-triggered + * IRQ re-fires forever and livelocks this single-core SoC. Do one + * throwaway RHR read to clear it; a healthy UART never reports a + * timeout with DR/BI clear, so no received data is ever discarded. + */ + if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) { + status = serial_lsr_in(up); + if (!(status & (UART_LSR_DR | UART_LSR_BI))) + serial_port_in(port, UART_RX); + } + + serial8250_handle_irq_locked(port, iir); + + return 1; +} + /* * Fill a struct uart_port for a given device node */ @@ -173,6 +207,9 @@ static int of_platform_serial_setup(struct platform_device *ofdev, case PORT_NPCM: ret = npcm_setup(port); break; + case PORT_LPC3220: + port->handle_irq = lpc32xx_handle_irq; + break; default: /* Nothing to do */ ret = 0; @@ -369,6 +406,7 @@ static struct platform_driver of_platform_serial_driver = { module_platform_driver(of_platform_serial_driver); +MODULE_IMPORT_NS("SERIAL_8250"); MODULE_AUTHOR("Arnd Bergmann "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices"); -- cgit v1.2.3 From e25d47a526939ad44b75f778b8a7500562b84fc1 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Fri, 31 Jul 2026 09:56:16 +0200 Subject: vt: stabilize tty reference in kbd_keycode with tty_port_tty_get kbd_keycode() reads vc->port.tty without acquiring a tty reference, racing against con_shutdown() which clears port.tty under a different lock. Use tty_port_tty_get()/tty_kref_put() to hold a proper reference for the duration the tty pointer is needed. Assisted-by: AISLE:Snapshot Signed-off-by: Joshua Rogers Cc: stable Link: https://patch.msgid.link/20260731-tty-vt-stuff-v1-1-be99b9da8e30@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 763a3f1b7be0..c41d850b29c6 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1437,7 +1437,7 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw) struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down }; int rc; - tty = vc->port.tty; + tty = tty_port_tty_get(&vc->port); if (tty && (!tty->driver_data)) { /* No driver data? Strange. Okay we fix it then. */ @@ -1497,9 +1497,12 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw) * characters get aren't echoed locally. This makes key repeat * usable with slow applications and under heavy loads. */ + tty_kref_put(tty); return; } + tty_kref_put(tty); + param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate; param.ledstate = kbd->ledflagstate; key_map = key_maps[shift_final]; -- cgit v1.2.3 From a7ad0034453ba4c353f9b8f810ee2569de33d283 Mon Sep 17 00:00:00 2001 From: Joshua Rogers Date: Fri, 31 Jul 2026 09:56:17 +0200 Subject: vt: add permission check for KDSKBMETA ioctl KDSKBMETA modifies keyboard meta mode but lacks the !perm check that all other keyboard setter ioctls in vt_k_ioctl() enforce, allowing a process to change meta mode on a non-controlling console without authorization. Assisted-by: AISLE:Snapshot Cc: stable Signed-off-by: Joshua Rogers Link: https://patch.msgid.link/20260731-tty-vt-stuff-v1-2-be99b9da8e30@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt_ioctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 28993a3d0acb..deb3b3f93461 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -406,6 +406,8 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd, /* this could be folded into KDSKBMODE, but for compatibility reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */ case KDSKBMETA: + if (!perm) + return -EPERM; return vt_do_kdskbmeta(console, arg); case KDGKBMETA: -- cgit v1.2.3 From dcb2f7576ce460eb4f6b9048b7c266c8da5848a8 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 31 Jul 2026 08:59:13 +0000 Subject: serial: amba-pl011: fix indefinite RS485 post-send delay The RS485 stop hrtimer is used both to drain the transmitter and to wait out delay_rts_after_send. The callback cannot tell the two apart, so it restarts the post-send delay on every expiry and the timer never stops. Add a WAIT_AFTER_SEND_DELAY state so its expiry ends the stop sequence instead of restarting the delay. Fixes: 2c1fd53af21b ("serial: amba-pl011: Fix RTS handling in RS485 mode") Cc: stable Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260731085915.326775-2-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8ed91e1da22b..d88c1242e6d8 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -309,6 +309,7 @@ enum pl011_rs485_tx_state { WAIT_AFTER_RTS, SEND, WAIT_AFTER_SEND, + WAIT_AFTER_SEND_DELAY, }; /* @@ -1350,6 +1351,7 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap) return; } if (port->rs485.delay_rts_after_send > 0) { + uap->rs485_tx_state = WAIT_AFTER_SEND_DELAY; hrtimer_start(&uap->trigger_stop_tx, ms_to_ktime(port->rs485.delay_rts_after_send), HRTIMER_MODE_REL); @@ -1415,7 +1417,8 @@ static void pl011_rs485_tx_start(struct uart_amba_port *uap) uap->rs485_tx_state = SEND; return; } - if (uap->rs485_tx_state == WAIT_AFTER_SEND) { + if (uap->rs485_tx_state == WAIT_AFTER_SEND || + uap->rs485_tx_state == WAIT_AFTER_SEND_DELAY) { hrtimer_try_to_cancel(&uap->trigger_stop_tx); uap->rs485_tx_state = SEND; return; @@ -1482,7 +1485,8 @@ static enum hrtimer_restart pl011_trigger_stop_tx(struct hrtimer *t) unsigned long flags; uart_port_lock_irqsave(&uap->port, &flags); - if (uap->rs485_tx_state == WAIT_AFTER_SEND) + if (uap->rs485_tx_state == WAIT_AFTER_SEND || + uap->rs485_tx_state == WAIT_AFTER_SEND_DELAY) pl011_rs485_tx_stop(uap); uart_port_unlock_irqrestore(&uap->port, flags); -- cgit v1.2.3 From 36672c8d7d14e9c43287528455d2c97b526ea6ad Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 31 Jul 2026 08:59:14 +0000 Subject: serial: amba-pl011: cancel RS485 hrtimers after freeing IRQ The RS485 trigger hrtimers are embedded in the devm-managed port and can fire after it is freed. The IRQ handler can arm a timer, so free the IRQ first and then cancel both timers. Complete the RS485 stop without arming a timer, and cancel the timers in remove() for the suspend-then-unbind path, where shutdown is not called. This issue was found by an in-house static analysis tool. Fixes: 2c1fd53af21b ("serial: amba-pl011: Fix RTS handling in RS485 mode") Cc: stable Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260731085915.326775-3-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 52 +++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index d88c1242e6d8..5232042fdd92 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1334,11 +1334,30 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) #define pl011_dma_flush_buffer NULL #endif -static void pl011_rs485_tx_stop(struct uart_amba_port *uap) +static void pl011_rs485_tx_stop_now(struct uart_amba_port *uap) { struct uart_port *port = &uap->port; u32 cr; + cr = pl011_read(uap, REG_CR); + + if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + cr &= ~UART011_CR_RTS; + else + cr |= UART011_CR_RTS; + + /* Disable the transmitter and reenable the transceiver */ + cr &= ~UART011_CR_TXE; + cr |= UART011_CR_RXE; + pl011_write(cr, uap, REG_CR); + + uap->rs485_tx_state = OFF; +} + +static void pl011_rs485_tx_stop(struct uart_amba_port *uap) +{ + struct uart_port *port = &uap->port; + if (uap->rs485_tx_state == SEND) uap->rs485_tx_state = WAIT_AFTER_SEND; @@ -1362,19 +1381,7 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap) hrtimer_try_to_cancel(&uap->trigger_start_tx); } - cr = pl011_read(uap, REG_CR); - - if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - cr &= ~UART011_CR_RTS; - else - cr |= UART011_CR_RTS; - - /* Disable the transmitter and reenable the transceiver */ - cr &= ~UART011_CR_TXE; - cr |= UART011_CR_RXE; - pl011_write(cr, uap, REG_CR); - - uap->rs485_tx_state = OFF; + pl011_rs485_tx_stop_now(uap); } static void pl011_stop_tx(struct uart_port *port) @@ -2084,11 +2091,20 @@ static void pl011_shutdown(struct uart_port *port) pl011_dma_shutdown(uap); - if ((port->rs485.flags & SER_RS485_ENABLED && uap->rs485_tx_state != OFF)) - pl011_rs485_tx_stop(uap); - free_irq(uap->port.irq, uap); + /* + * free_irq() drains the UART interrupt handler, which can arm either + * timer. Cancel the timers afterwards to drain their callbacks too. + */ + hrtimer_cancel(&uap->trigger_start_tx); + hrtimer_cancel(&uap->trigger_stop_tx); + + uart_port_lock_irq(port); + if (uap->rs485_tx_state != OFF) + pl011_rs485_tx_stop_now(uap); + uart_port_unlock_irq(port); + pl011_disable_uart(uap); /* @@ -3067,6 +3083,8 @@ static void pl011_remove(struct amba_device *dev) struct uart_amba_port *uap = amba_get_drvdata(dev); uart_remove_one_port(&amba_reg, &uap->port); + hrtimer_cancel(&uap->trigger_start_tx); + hrtimer_cancel(&uap->trigger_stop_tx); pl011_unregister_port(uap); } -- cgit v1.2.3 From 440915499231e9db1c361aa45bb702e8fd3b4a32 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 31 Jul 2026 08:59:15 +0000 Subject: serial: amba-pl011: synchronize DMA teardown dmaengine_terminate_all() does not wait for a running callback, so the TX callback can still touch the TX buffer after it is freed. The RX poll timer reads the RX buffers without the port lock. Switch to dmaengine_terminate_sync() and delete the RX timer before freeing the buffers. Fixes: ead76f329f77 ("ARM: 6763/1: pl011: add optional RX DMA to PL011 v2") Cc: stable Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Link: https://patch.msgid.link/20260731085915.326775-4-fanwu01@zju.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 5232042fdd92..9abaeecd05fc 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1247,7 +1247,7 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) if (uap->using_tx_dma) { /* In theory, this should already be done by pl011_dma_flush_buffer */ - dmaengine_terminate_all(uap->dmatx.chan); + dmaengine_terminate_sync(uap->dmatx.chan); if (uap->dmatx.queued) { dma_unmap_single(uap->dmatx.chan->device->dev, uap->dmatx.dma, uap->dmatx.len, @@ -1260,12 +1260,12 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) } if (uap->using_rx_dma) { - dmaengine_terminate_all(uap->dmarx.chan); + if (uap->dmarx.poll_rate) + timer_delete_sync(&uap->dmarx.timer); + dmaengine_terminate_sync(uap->dmarx.chan); /* Clean up the RX DMA */ pl011_dmabuf_free(uap->dmarx.chan, &uap->dmarx.dbuf_a, DMA_FROM_DEVICE); pl011_dmabuf_free(uap->dmarx.chan, &uap->dmarx.dbuf_b, DMA_FROM_DEVICE); - if (uap->dmarx.poll_rate) - timer_delete_sync(&uap->dmarx.timer); uap->using_rx_dma = false; } } -- cgit v1.2.3 From f8e370058e9e1396fea1a8d11906fa92ec9bad88 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:29 +0200 Subject: KVM: s390: Do not free SCA if it was not allocated If VM creation fails early in kvm_arch_init_vm(), the cleanup code tries to free up the SCA, even though the address is 0. Due to using free_pages_exact(), only the first page is skipped, accidentally freeing pages 1, 2, and 3. Fix by checking whether the pointer is NULL before attempting to free the SCA in sca_dispose(). Fixes: e72753ed1267 ("KVM: s390: Use ESCA instead of BSCA at VM init") Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-3-imbrenda@linux.ibm.com> Reviewed-by: Christian Borntraeger Reviewed-by: Janosch Frank --- arch/s390/kvm/kvm-s390.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index f86b4b0b356f..1b3290a5ad1a 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3247,7 +3247,8 @@ static void kvm_s390_crypto_init(struct kvm *kvm) static void sca_dispose(struct kvm *kvm) { - free_pages_exact(kvm->arch.sca, sizeof(*kvm->arch.sca)); + if (kvm->arch.sca) + free_pages_exact(kvm->arch.sca, sizeof(*kvm->arch.sca)); kvm->arch.sca = NULL; } -- cgit v1.2.3 From b050f741fd0d636f2daab72b74aeccea97abdb5f Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:30 +0200 Subject: KVM: s390: Fix kvm_s390_vcpu_unsetup_cmma() In some cases kvm_s390_vcpu_unsetup_cmma() can be called with a 0 cbrlo; in such cases, if running with V != R, free_page() will attempt to free physical page 0. Fix by freeing cbrlo only if it's non-zero. Fixes: b31605c12f4e ("KVM: s390: make cmma usage conditionally") Fixes: 29b40f105ec8 ("KVM: s390: protvirt: Add initial vm and cpu lifecycle handling") Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-4-imbrenda@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 1b3290a5ad1a..9be27db0a21e 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3678,7 +3678,8 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu) void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu) { - free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo)); + if (vcpu->arch.sie_block->cbrlo) + free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo)); vcpu->arch.sie_block->cbrlo = 0; } -- cgit v1.2.3 From a0496b40a4ab346052aaa59a5163c10224dda01f Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:31 +0200 Subject: KVM: s390: Fix overclearing ESCA in case of error If an attempt is made to create a vCPU with an already existing ID, the duplicated vCPU will be destroyed. When destroying a vCPU, its ESCA entry will be cleared. In the above scenario, the spurious duplicate vCPU is destroyed, but the ESCA entry corresponding to the original vCPU is cleared. Fix by skipping clearing the ESCA entry if the vCPU creation was not successful, i.e. if the vcpu->arch.initialized is still zero. Reviewed-by: Janosch Frank Signed-off-by: Claudio Imbrenda Fixes: abf4a71ed95f ("KVM: s390: Unlink vcpu on destroy - v2") [ Added Fixes tag while picking -- Claudio ] Message-ID: <20260803124040.126471-5-imbrenda@linux.ibm.com> --- arch/s390/kvm/interrupt.c | 2 +- arch/s390/kvm/kvm-s390.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 7514d9e2403c..bb4e72466043 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -86,7 +86,7 @@ static void sca_clear_ext_call(struct kvm_vcpu *vcpu) struct esca_block *sca = vcpu->kvm->arch.sca; union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl; - if (!kvm_s390_use_sca_entries()) + if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized) return; kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND); diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 9be27db0a21e..5b2727d7dfd1 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3462,7 +3462,7 @@ static void sca_del_vcpu(struct kvm_vcpu *vcpu) { struct esca_block *sca = vcpu->kvm->arch.sca; - if (!kvm_s390_use_sca_entries()) + if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized) return; clear_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn); -- cgit v1.2.3 From d301ade41831e746783bdb697fae3afba47841ba Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:32 +0200 Subject: KVM: s390: ucontrol: Fix sca_clear_ext_call() When cleaning up a UCONTROL VM, sca_clear_ext_call() will touch memory outside of the allocated ESCA block, and UCONTROL VMs don't even use ESCA. Fix by not touching ESCA for UCONTROL VMs, and fence the KVM_S390_INTERRUPT ioctl altogether. Add extra checks in sca_ext_call_pending() and sca_inject_ext_call() to make sure UCONTROL VMs won't touch ESCA. Fencing does not cause regressions with userspace, since UCONTROL VMs never used KVM_S390_INTERRUPT ioctls. Fixes: 7d43bafcff17 ("KVM: s390: Make provisions for ESCA utilization") Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-6-imbrenda@linux.ibm.com> --- arch/s390/kvm/interrupt.c | 19 ++++++++++++++----- arch/s390/kvm/kvm-s390.c | 5 +++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index bb4e72466043..8f24bcd1a6d3 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -45,13 +45,16 @@ static struct kvm_s390_gib *gib; static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id) { struct esca_block *sca = vcpu->kvm->arch.sca; - union esca_sigp_ctrl sigp_ctrl = sca->cpu[vcpu->vcpu_id].sigp_ctrl; + union esca_sigp_ctrl sigp_ctrl; if (!kvm_s390_test_cpuflags(vcpu, CPUSTAT_ECALL_PEND)) return 0; + if (kvm_is_ucontrol(vcpu->kvm)) + return 0; BUG_ON(!kvm_s390_use_sca_entries()); + sigp_ctrl = sca->cpu[vcpu->vcpu_id].sigp_ctrl; if (src_id) *src_id = sigp_ctrl.scn; @@ -60,13 +63,16 @@ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id) static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id) { - struct esca_block *sca = vcpu->kvm->arch.sca; - union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl; union esca_sigp_ctrl old_val, new_val = {.scn = src_id, .c = 1}; + struct esca_block *sca = vcpu->kvm->arch.sca; + union esca_sigp_ctrl *sigp_ctrl; int expect, rc; BUG_ON(!kvm_s390_use_sca_entries()); + if (kvm_is_ucontrol(vcpu->kvm)) + return -EINVAL; + sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl; old_val = READ_ONCE(*sigp_ctrl); old_val.c = 0; @@ -84,10 +90,13 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id) static void sca_clear_ext_call(struct kvm_vcpu *vcpu) { struct esca_block *sca = vcpu->kvm->arch.sca; - union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl; + union esca_sigp_ctrl *sigp_ctrl; - if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized) + if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized || kvm_is_ucontrol(vcpu->kvm)) return; + + /* Initialize after the above check, to prevent going out of bounds */ + sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl; kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND); WRITE_ONCE(sigp_ctrl->value, 0); diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 5b2727d7dfd1..21574f57be72 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2934,6 +2934,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) case KVM_S390_INTERRUPT: { struct kvm_s390_interrupt s390int; + r = -EINVAL; + if (kvm_is_ucontrol(kvm)) + break; r = -EFAULT; if (copy_from_user(&s390int, argp, sizeof(s390int))) break; @@ -5456,6 +5459,8 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl, struct kvm_s390_interrupt s390int; struct kvm_s390_irq s390irq = {}; + if (kvm_is_ucontrol(vcpu->kvm)) + return -EINVAL; if (copy_from_user(&s390int, argp, sizeof(s390int))) return -EFAULT; if (s390int_to_s390irq(&s390int, &s390irq)) -- cgit v1.2.3 From d699986f11bf854bddecd6b1bfdf53e05ef945d6 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:33 +0200 Subject: KVM: s390: Fix leaking of PGM_ADDRESSING to userspace If kvm_s390_set_cmma_bits() is asked to set CMMA values outside of a memslot, PGM_ADDRESSING (5) is returned, instead of a negative error value. Same issue with kvm_s390_{g,s}et_skeys(), kvm_s390_keyop(), and dat_reset_reference_bit(). Fix by returning -EFAULT whenever the return value would be > 0, which is consistent with the behaviour before the gmap rewrite. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-7-imbrenda@linux.ibm.com> --- arch/s390/kvm/dat.c | 16 ++++++++++------ arch/s390/kvm/dat.h | 2 +- arch/s390/kvm/kvm-s390.c | 16 ++++++++-------- arch/s390/kvm/priv.c | 5 +++-- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c index ed4259d17629..171b61959908 100644 --- a/arch/s390/kvm/dat.c +++ b/arch/s390/kvm/dat.c @@ -755,13 +755,15 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf return rc; } -int dat_reset_reference_bit(union asce asce, gfn_t gfn) +int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey) { union pgste pgste, old; union crste *crstep; union pte *ptep; int rc; + skey->skey = 0; + rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep); if (rc) return rc; @@ -771,21 +773,23 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn) if (!crste.h.fc || !crste.s.fc1.pr) return 0; - return page_reset_referenced(large_crste_to_phys(*crstep, gfn)); + skey->skey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) << 1; + return 0; } old = pgste_get_lock(ptep); pgste = old; if (!ptep->h.i) { - rc = page_reset_referenced(pte_origin(*ptep)); - pgste.hr = rc >> 1; + skey->skey = page_reset_referenced(pte_origin(*ptep)) << 1; + pgste.hr = skey->r; } - rc |= (pgste.gr << 1) | pgste.gc; + skey->r |= pgste.gr; + skey->c |= pgste.gc; pgste.gr = 0; dat_update_ptep_sd(old, pgste, ptep); pgste_set_unlock(ptep, pgste); - return rc; + return 0; } static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) diff --git a/arch/s390/kvm/dat.h b/arch/s390/kvm/dat.h index fad605305e05..141ee7b9f019 100644 --- a/arch/s390/kvm/dat.h +++ b/arch/s390/kvm/dat.h @@ -537,7 +537,7 @@ int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gf union skey skey, bool nq); int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn, union skey skey, union skey *oldkey, bool nq, bool mr, bool mc); -int dat_reset_reference_bit(union asce asce, gfn_t gfn); +int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey); long dat_reset_skeys(union asce asce, gfn_t start); unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param); diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 21574f57be72..e162efaa35b8 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -571,7 +571,7 @@ static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op switch (op) { case KVM_S390_KEYOP_SSKE: r = dat_cond_set_storage_key(mc, asce, gfn, skey, &skey, 0, 0, 0); - if (r >= 0) + if (r == 0 || r == 1) return skey.skey; break; case KVM_S390_KEYOP_ISKE: @@ -580,14 +580,14 @@ static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op return skey.skey; break; case KVM_S390_KEYOP_RRBE: - r = dat_reset_reference_bit(asce, gfn); - if (r > 0) - return r << 1; + r = dat_reset_reference_bit(asce, gfn, &skey); + if (!r) + return skey.skey; break; default: return -EINVAL; } - return r; + return r > 0 ? -EFAULT : r; } /* Section: device related */ @@ -2214,7 +2214,7 @@ static int kvm_s390_get_skeys(struct kvm *kvm, struct kvm_s390_skeys *args) } kvfree(keys); - return r; + return r <= 0 ? r : -EFAULT; } static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args) @@ -2276,7 +2276,7 @@ static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args) kvm_s390_free_mmu_cache(mc); out: kvfree(keys); - return r; + return r <= 0 ? r : -EFAULT; } /* @@ -2386,7 +2386,7 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm, set_bit(GMAP_FLAG_USES_CMM, &kvm->arch.gmap->flags); - return r; + return r <= 0 ? r : -EFAULT; } /** diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index ad0ddc433a73..ea5a99537346 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -289,6 +289,7 @@ static int handle_iske(struct kvm_vcpu *vcpu) static int handle_rrbe(struct kvm_vcpu *vcpu) { unsigned long gaddr; + union skey skey; int reg1, reg2; int rc; @@ -307,12 +308,12 @@ static int handle_rrbe(struct kvm_vcpu *vcpu) gaddr = kvm_s390_logical_to_effective(vcpu, gaddr); gaddr = kvm_s390_real_to_abs(vcpu, gaddr); scoped_guard(read_lock, &vcpu->kvm->mmu_lock) - rc = dat_reset_reference_bit(vcpu->arch.gmap->asce, gpa_to_gfn(gaddr)); + rc = dat_reset_reference_bit(vcpu->arch.gmap->asce, gpa_to_gfn(gaddr), &skey); if (rc > 0) return kvm_s390_inject_program_int(vcpu, rc); if (rc < 0) return rc; - kvm_s390_set_psw_cc(vcpu, rc); + kvm_s390_set_psw_cc(vcpu, (skey.skey >> 1) & 3); return 0; } -- cgit v1.2.3 From 4db7207052874f13dec1e9be384cca279c6693e8 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:34 +0200 Subject: KVM: s390: Fix race in __do_essa() An unlikely race between __do_essa() and kvm_s390_vm_start_migration(), kvm_s390_vm_stop_migration(), or dat_get_cmma() was possible. Fix by locking kvm->slots_arch_lock. Since this is not a hot path, the overhead of an additional mutex is negligible. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-8-imbrenda@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 20 ++++++++++---------- arch/s390/kvm/priv.c | 5 +++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index e162efaa35b8..e5c5e9f61cb2 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -1219,8 +1219,8 @@ static void kvm_s390_sync_request_broadcast(struct kvm *kvm, int req) /* * Must be called with kvm->srcu held to avoid races on memslots, and with - * kvm->slots_lock to avoid races with ourselves, kvm_s390_vm_stop_migration(), - * and kvm_s390_get_cmma_bits(). + * kvm->slots_arch_lock to avoid races with ourselves, + * kvm_s390_vm_stop_migration(), and kvm_s390_get_cmma_bits(). */ static int kvm_s390_vm_start_migration(struct kvm *kvm) { @@ -1265,7 +1265,7 @@ static int kvm_s390_vm_start_migration(struct kvm *kvm) } /* - * Must be called with kvm->slots_lock to avoid races with ourselves, + * Must be called with kvm->slots_arch_lock to avoid races with ourselves, * kvm_s390_vm_start_migration() and kvm_s390_get_cmma_bits(). */ static int kvm_s390_vm_stop_migration(struct kvm *kvm) @@ -1300,7 +1300,9 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm, { int res = -ENXIO; - mutex_lock(&kvm->slots_lock); + guard(srcu)(&kvm->srcu); + guard(mutex)(&kvm->slots_arch_lock); + switch (attr->attr) { case KVM_S390_VM_MIGRATION_START: res = kvm_s390_vm_start_migration(kvm); @@ -1311,7 +1313,6 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm, default: break; } - mutex_unlock(&kvm->slots_lock); return res; } @@ -3001,9 +3002,8 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) r = -EFAULT; if (copy_from_user(&args, argp, sizeof(args))) break; - mutex_lock(&kvm->slots_lock); - r = kvm_s390_get_cmma_bits(kvm, &args); - mutex_unlock(&kvm->slots_lock); + scoped_guard(mutex, &kvm->slots_arch_lock) + r = kvm_s390_get_cmma_bits(kvm, &args); if (!r) { r = copy_to_user(argp, &args, sizeof(args)); if (r) @@ -3017,9 +3017,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) r = -EFAULT; if (copy_from_user(&args, argp, sizeof(args))) break; - mutex_lock(&kvm->slots_lock); + mutex_lock(&kvm->slots_arch_lock); r = kvm_s390_set_cmma_bits(kvm, &args); - mutex_unlock(&kvm->slots_lock); + mutex_unlock(&kvm->slots_arch_lock); break; } case KVM_S390_PV_COMMAND: { diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index ea5a99537346..b1ba24c346ef 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -1261,8 +1261,9 @@ static int handle_essa(struct kvm_vcpu *vcpu) /* Retry the ESSA instruction */ kvm_s390_retry_instr(vcpu); } else { - scoped_guard(read_lock, &vcpu->kvm->mmu_lock) - i = __do_essa(vcpu, orc); + scoped_guard(mutex, &vcpu->kvm->slots_arch_lock) + scoped_guard(read_lock, &vcpu->kvm->mmu_lock) + i = __do_essa(vcpu, orc); if (i < 0) return i; /* Account for the possible extra cbrl entry */ -- cgit v1.2.3 From ec215346270512db60478e45e8dff2de950225aa Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:35 +0200 Subject: KVM: s390: cmma: Fix dirty tracking when removing memslot When a memslot is removed, all ptes that mapped the slot are cleared or even deallocated. If this happens while the system is in migration mode, and if cmma-dirty pages are removed, the cmma-dirty counter will not reflect reality. Fix by appropriately decrementing the cmma-dirty counter when removing a memslot. Opportunistically improve kvm_arch_commit_memory_region() to use __free() for the struct kvm_s390_mmu_cache. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-9-imbrenda@linux.ibm.com> --- arch/s390/kvm/dat.c | 7 ++++++- arch/s390/kvm/kvm-s390.c | 25 +++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c index 171b61959908..3f2d6e8902d7 100644 --- a/arch/s390/kvm/dat.c +++ b/arch/s390/kvm/dat.c @@ -850,6 +850,7 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal struct slot_priv *p = walk->priv; union crste dummy = { .val = p->token }; union pte new_pte, pte = READ_ONCE(*ptep); + union pgste pgste; new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par); @@ -857,7 +858,11 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal if (pte.val == new_pte.val) return 0; - dat_ptep_xchg(ptep, new_pte, gfn, walk->asce, false); + pgste = pgste_get_lock(ptep); + pgste = __dat_ptep_xchg(ptep, pgste, new_pte, gfn, walk->asce, false); + pgste.cmma_d = 0; + pgste_set_unlock(ptep, pgste); + return 0; } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index e5c5e9f61cb2..ba811f0673d1 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -5812,14 +5812,30 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, return 0; } +static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) +{ + union pgste pgste; + + pgste = pgste_get_lock(ptep); + if (pgste.cmma_d) { + pgste.cmma_d = 0; + atomic64_dec(walk->priv); + } + pgste_set_unlock(ptep, pgste); + return 0; +} + void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_memory_slot *old, const struct kvm_memory_slot *new, enum kvm_mr_change change) { - struct kvm_s390_mmu_cache *mc = NULL; + const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, }; + struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL; int rc = 0; + guard(mutex)(&kvm->slots_arch_lock); + if (change == KVM_MR_FLAGS_ONLY) return; @@ -5830,6 +5846,12 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, } scoped_guard(write_lock, &kvm->mmu_lock) { + if (kvm->arch.migration_mode && kvm->arch.use_cmma && old) { + _dat_walk_gfn_range(old->base_gfn, old->base_gfn + old->npages, + kvm->arch.gmap->asce, &ops, DAT_WALK_IGN_HOLES, + &kvm->arch.cmma_dirty_pages); + } + switch (change) { case KVM_MR_DELETE: rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages); @@ -5851,7 +5873,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, out: if (rc) pr_warn("failed to commit memory region\n"); - kvm_s390_free_mmu_cache(mc); return; } -- cgit v1.2.3 From dab62d218754e00a22aff45d2b0116c5ee30cbd2 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:36 +0200 Subject: KVM: s390: ucontrol: Add missing locking around gmap_remove_child() gmap_remove_child() needs to be called while holding the children_lock of the parent gmap. This was not the case in the error handling path of kvm_arch_vcpu_create() for UCONTROL guests. Fix by adding the missing lock. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Reviewed-by: Steffen Eiden Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-10-imbrenda@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index ba811f0673d1..2741ca323ede 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3875,7 +3875,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) out_ucontrol_uninit: if (kvm_is_ucontrol(vcpu->kvm)) { + spin_lock(&vcpu->kvm->arch.gmap->children_lock); gmap_remove_child(vcpu->arch.gmap); + spin_unlock(&vcpu->kvm->arch.gmap->children_lock); vcpu->arch.gmap = gmap_put(vcpu->arch.gmap); } out_free_sie_block: -- cgit v1.2.3 From e4d678900a1ae66112812fe4d2aff16044ca37f2 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:37 +0200 Subject: KVM: s390: Free the mmu cache when kvm_arch_vcpu_create() fails The mmu cache is the first thing that is allocated in kvm_arch_vcpu_create(), but in case of failure it was not freed. Fix by freeing the mmu cache in case of failure. Refactor kvm_arch_vcpu_create() to use scope-based cleanup instead of gotos. Fixes: e38c884df921 ("KVM: s390: Switch to new gmap") Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-11-imbrenda@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 2741ca323ede..9b8a35e7dc82 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3800,21 +3800,21 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) return 0; } +DEFINE_FREE(sie_page, struct sie_page *, if (_T) free_page((unsigned long)(_T))) + int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) { - struct sie_page *sie_page; + struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL; + struct sie_page *sie_page __free(sie_page) = NULL; int rc; BUILD_BUG_ON(sizeof(struct sie_page) != 4096); - vcpu->arch.mc = kvm_s390_new_mmu_cache(); - if (!vcpu->arch.mc) + mc = kvm_s390_new_mmu_cache(); + if (!mc) return -ENOMEM; sie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL_ACCOUNT); - if (!sie_page) { - kvm_s390_free_mmu_cache(vcpu->arch.mc); - vcpu->arch.mc = NULL; + if (!sie_page) return -ENOMEM; - } vcpu->arch.sie_block = &sie_page->sie_block; vcpu->arch.sie_block->itdba = virt_to_phys(&sie_page->itdb); @@ -3856,10 +3856,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) vcpu->run->kvm_valid_regs |= KVM_SYNC_FPRS; if (kvm_is_ucontrol(vcpu->kvm)) { - rc = -ENOMEM; vcpu->arch.gmap = gmap_new_child(vcpu->kvm->arch.gmap, -1UL); if (!vcpu->arch.gmap) - goto out_free_sie_block; + return -ENOMEM; } VM_EVENT(vcpu->kvm, 3, "create cpu %d at 0x%p, sie block at 0x%p", @@ -3867,22 +3866,19 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) trace_kvm_s390_create_vcpu(vcpu->vcpu_id, vcpu, vcpu->arch.sie_block); rc = kvm_s390_vcpu_setup(vcpu); - if (rc) - goto out_ucontrol_uninit; + if (rc) { + if (kvm_is_ucontrol(vcpu->kvm)) { + scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock) + gmap_remove_child(vcpu->arch.gmap); + vcpu->arch.gmap = gmap_put(vcpu->arch.gmap); + } + return rc; + } + vcpu->arch.mc = no_free_ptr(mc); + sie_page = NULL; kvm_s390_update_topology_change_report(vcpu->kvm, 1); return 0; - -out_ucontrol_uninit: - if (kvm_is_ucontrol(vcpu->kvm)) { - spin_lock(&vcpu->kvm->arch.gmap->children_lock); - gmap_remove_child(vcpu->arch.gmap); - spin_unlock(&vcpu->kvm->arch.gmap->children_lock); - vcpu->arch.gmap = gmap_put(vcpu->arch.gmap); - } -out_free_sie_block: - free_page((unsigned long)(vcpu->arch.sie_block)); - return rc; } int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) -- cgit v1.2.3 From 216c5289dd66d9cde6b42cbe68ee8dec5669678a Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:38 +0200 Subject: KVM: s390: Return -EINTR if a signal is pending while faulting-in If a fatal signal is pending while trying to fault-in a page, return -EINTR instead of -EAGAIN. Also fix unpack_one() to handle -EINTR properly. Fixes: e907ae530133 ("KVM: s390: Add helper functions for fault handling") Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-12-imbrenda@linux.ibm.com> --- arch/s390/kvm/faultin.c | 6 +++--- arch/s390/kvm/pv.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/s390/kvm/faultin.c b/arch/s390/kvm/faultin.c index fee80047bd94..3cc45f7f5b2d 100644 --- a/arch/s390/kvm/faultin.c +++ b/arch/s390/kvm/faultin.c @@ -91,9 +91,9 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa /* Access outside memory, addressing exception. */ if (is_noslot_pfn(f->pfn)) return PGM_ADDRESSING; - /* Signal pending: try again. */ - if (f->pfn == KVM_PFN_ERR_SIGPENDING) - return -EAGAIN; + /* Fatal signal pending: bail out. */ + if (is_sigpending_pfn(f->pfn)) + return -EINTR; /* Check if it's read-only memory; don't try to actually handle that case. */ if (f->pfn == KVM_PFN_ERR_RO_FAULT) return -EOPNOTSUPP; diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index 1beacc841ca8..dc204b521052 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -809,7 +809,7 @@ static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak, return -EAGAIN; } - if (ret && ret != -EAGAIN) + if (ret && ret != -EAGAIN && ret != -EINTR) KVM_UV_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx with rc %x rrc %x", uvcb.gaddr, *rc, *rrc); return ret; -- cgit v1.2.3 From 9187a9186d0ac7b260b07917aabc672a80d37c3c Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:39 +0200 Subject: KVM: s390: Fix ordering when adding to SCA When adding a new vCPU to the SCA area, the validity bit in the MCN was set before the pointer to the state description, potentially allowing for a race. Fix by setting the pointer before setting the bit. Fixes: 14542a0a54c5 ("KVM: S390: Remove sca_lock") Reviewed-by: Steffen Eiden Reviewed-by: Janosch Frank Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-13-imbrenda@linux.ibm.com> --- arch/s390/kvm/kvm-s390.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 9b8a35e7dc82..518a69c55e85 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3485,8 +3485,8 @@ static void sca_add_vcpu(struct kvm_vcpu *vcpu) if (!kvm_s390_use_sca_entries()) return; + WRITE_ONCE(sca->cpu[vcpu->vcpu_id].sda, virt_to_phys(vcpu->arch.sie_block)); set_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn); - sca->cpu[vcpu->vcpu_id].sda = virt_to_phys(vcpu->arch.sie_block); } static int sca_can_add_vcpu(struct kvm *kvm, unsigned int id) -- cgit v1.2.3 From feadc5e84dcb53422a437556c35af9efd9826fd5 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Mon, 3 Aug 2026 14:40:40 +0200 Subject: KVM: s390: Fix cleanup in kvm_s390_pv_create_cpu() If creating a protected vCPU in kvm_s390_pv_create_cpu() fails, kvm_s390_pv_destroy_cpu() was called, which checks whether the vCPU has a PV handle and exits doing nothing otherwise. At that point, due to not having created the protected vCPU, the PV handle will not be set, and kvm_s390_pv_destroy_cpu() will do nothing, thus leaking the allocated memory. Fix by factoring out the code to free and reset a PV vCPU; call it from kvm_s390_pv_destroy_cpu() and kvm_s390_pv_create_cpu(). Opportunistically fix the return value of kvm_s390_pv_destroy_cpu() in case of errors: return -EIO instead if EIO. Fixes: d4074324b07a ("KVM: s390: pv: avoid double free of sida page") Reviewed-by: Steffen Eiden Reviewed-by: Janosch Frank Signed-off-by: Claudio Imbrenda Message-ID: <20260803124040.126471-14-imbrenda@linux.ibm.com> --- arch/s390/kvm/pv.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index dc204b521052..b02e0159d3cd 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -244,6 +244,24 @@ static void kvm_s390_clear_pv_state(struct kvm *kvm) kvm->arch.pv.stor_var = NULL; } +static void kvm_s390_pv_dispose_cpu(struct kvm_vcpu *vcpu, bool free_stor_base) +{ + if (free_stor_base) + free_pages(vcpu->arch.pv.stor_base, get_order(uv_info.guest_cpu_stor_len)); + free_page((unsigned long)sida_addr(vcpu->arch.sie_block)); + vcpu->arch.sie_block->pv_handle_cpu = 0; + vcpu->arch.sie_block->pv_handle_config = 0; + memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv)); + vcpu->arch.sie_block->sdf = 0; + /* + * The sidad field (for sdf == 2) is now the gbea field (for sdf == 0). + * Use the reset value of gbea to avoid leaking the kernel pointer of + * the just freed sida. + */ + vcpu->arch.sie_block->gbea = 1; + kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); +} + int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) { int cc; @@ -258,24 +276,9 @@ int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) WARN_ONCE(cc, "protvirt destroy cpu failed rc %x rrc %x", *rc, *rrc); /* Intended memory leak for something that should never happen. */ - if (!cc) - free_pages(vcpu->arch.pv.stor_base, - get_order(uv_info.guest_cpu_stor_len)); - - free_page((unsigned long)sida_addr(vcpu->arch.sie_block)); - vcpu->arch.sie_block->pv_handle_cpu = 0; - vcpu->arch.sie_block->pv_handle_config = 0; - memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv)); - vcpu->arch.sie_block->sdf = 0; - /* - * The sidad field (for sdf == 2) is now the gbea field (for sdf == 0). - * Use the reset value of gbea to avoid leaking the kernel pointer of - * the just freed sida. - */ - vcpu->arch.sie_block->gbea = 1; - kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); + kvm_s390_pv_dispose_cpu(vcpu, !cc); - return cc ? EIO : 0; + return cc ? -EIO : 0; } int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) @@ -319,9 +322,7 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) uvcb.header.rrc); if (cc) { - u16 dummy; - - kvm_s390_pv_destroy_cpu(vcpu, &dummy, &dummy); + kvm_s390_pv_dispose_cpu(vcpu, true); return -EIO; } -- cgit v1.2.3 From 50b303f3d0f7de543ee90d50879970783d06da33 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 20 Jul 2026 13:11:58 +0200 Subject: usb: cdnsp: fix incorrect endian conversions for APB timeout register readl() already returns a CPU-endian value. Passing its return value to le32_to_cpu() is therefore redundant and causes an incorrect double byte swap on big-endian systems. Similarly, writel() expects a CPU-endian value, so passing the result of cpu_to_le32() is incorrect. Remove the unnecessary conversions and operate on the MMIO register value as a CPU-endian u32. Fixes: 241e2ce88e5a ("usb: cdnsp: Fix issue with resuming from L1") Suggested-by: Arnd Bergmann Cc: stable Signed-off-by: Pawel Laszczak Acked-by: Arnd Bergmann Link: https://patch.msgid.link/20260720-endian-fix-v1-v1-1-b5681fa1ea9f@cadence.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdnsp-gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index a5275c2fb43b..7a516e509198 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -163,9 +163,9 @@ static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev) offset = cdnsp_find_next_ext_cap(base, offset, D_XEC_PRE_REGS_CAP); reg = base + offset + REG_CHICKEN_BITS_3_OFFSET; - val = le32_to_cpu(readl(reg)); + val = readl(reg); val = CHICKEN_APB_TIMEOUT_SET(val, cdns->override_apb_timeout); - writel(cpu_to_le32(val), reg); + writel(val, reg); } static void cdnsp_set_chicken_bits_2(struct cdnsp_device *pdev, u32 bit) -- cgit v1.2.3 From 6b1c8a9403a26cb0fed7a648916c74dc236da591 Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Mon, 20 Jul 2026 16:56:54 +0000 Subject: usb: gadget: f_ncm: Use unsigned int for ndp_index The variable ndp_index is declared as a signed integer, but it stores the return value of get_ncm(), which is unsigned. A malicious host can supply a large offset that overflows the signed ndp_index, making it negative. Because ndp_index is compared against unsigned bounds, this negative value bypasses sanity checks and leads to an out-of-bounds read when calculating the address of the NDP block (ntb_ptr + ndp_index). Fix this by changing ndp_index to unsigned int to ensure consistent unsigned comparisons throughout the function. Fixes: 370af734dfaf ("usb: gadget: NCM: RX function support multiple NDPs") Cc: stable Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260720165654.2224591-1-sonalipradhan@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_ncm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 64eabda2f546..bf02545b37a2 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1171,7 +1171,7 @@ static int ncm_unwrap_ntb(struct gether *port, unsigned char *ntb_ptr = skb->data; __le16 *tmp; unsigned index, index2; - int ndp_index; + unsigned int ndp_index; unsigned dg_len, dg_len2; unsigned ndp_len; unsigned block_len; -- cgit v1.2.3 From 7e22c9f79b200672f3e477421b6c9050d8cf70a5 Mon Sep 17 00:00:00 2001 From: Jiangshan Yi Date: Wed, 22 Jul 2026 18:18:10 +0800 Subject: usb: misc: usbio: check ibuf_len against rxbuf_len in bulk msg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ibuf_len is the bulk IN (receive) buffer size, but the EMSGSIZE check in usbio_bulk_msg() compares it against txbuf_len — the bulk OUT endpoint size. Both are taken independently from different endpoints in usbio_probe(), so the check is wrong when they differ. Use rxbuf_len for the IN direction. This matches the buffer that actually holds the response data. Fixes: 121a0f839dbb ("usb: misc: Add Intel USBIO bridge driver") Cc: stable Signed-off-by: Jiangshan Yi Tested-by: Antti Laakso Link: https://patch.msgid.link/20260722101810.458634-1-yijiangshan@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 3c2474dca810..fe093e7760d5 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -265,7 +265,7 @@ int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last, lockdep_assert_held(&usbio->bulk_mutex); if ((obuf_len > (usbio->txbuf_len - sizeof(*bpkt))) || - (ibuf_len > (usbio->txbuf_len - sizeof(*bpkt)))) + (ibuf_len > (usbio->rxbuf_len - sizeof(*bpkt)))) return -EMSGSIZE; if (ibuf_len) -- cgit v1.2.3 From c2f811314be351d86b6ab41e9297ae80d8da6f86 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 31 Jul 2026 10:15:20 +0000 Subject: usb: atm: cxacru: properly kill rcv_urb on error in cxacru_cm() If cxacru_cm() encounters an error while submitting or waiting for snd_urb, it aborts and returns the error without killing the already submitted rcv_urb. This leaves the rcv_urb active. When this happens during initialization (e.g., in cxacru_atm_start()), the driver may ignore the error and proceed to call cxacru_poll_status(), which invokes cxacru_cm() again. Attempting to submit the still-active rcv_urb triggers a warning in usb_submit_urb(): cxacru 1-1:1.0: send of cm 0x84 failed (-104) ATM dev 0: cxacru_atm_start: CHIP_ADSL_LINE_START returned -104 ------------[ cut here ]------------ URB ffff88812658d200 submitted while active WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb+0x79/0x18b0 drivers/usb/core/urb.c:379 ... Call Trace: cxacru_cm+0x21a/0xf10 drivers/usb/atm/cxacru.c:631 cxacru_cm_get_array drivers/usb/atm/cxacru.c:722 [inline] cxacru_poll_status+0x178/0x1110 drivers/usb/atm/cxacru.c:828 cxacru_atm_start+0x185/0x360 drivers/usb/atm/cxacru.c:814 usbatm_atm_init+0x144/0x3a0 drivers/usb/atm/usbatm.c:927 usbatm_usb_probe+0x15cb/0x1db0 drivers/usb/atm/usbatm.c:1178 cxacru_usb_probe+0x17f/0x220 drivers/usb/atm/cxacru.c:1370 ... To fix this, ensure that rcv_urb is properly killed if cxacru_cm() aborts early. We can safely call usb_kill_urb() on rcv_urb in the error path, as it is safe to call even if the URB is not active (e.g., if it failed to submit in the first place, or if it already completed). Fixes: 1b0e61465234 ("[PATCH] USB ATM: driver for the Conexant AccessRunner chipset cxacru") Cc: stable Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+c9dff578c3a41775176a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c9dff578c3a41775176a Link: https://syzkaller.appspot.com/ai_job?id=75fec6f2-c8a6-43b1-b184-4d26baba86cc Signed-off-by: Aleksandr Nogikh Link: https://patch.msgid.link/91edfa4c-a63d-400c-9f00-31f3e1f98c00@mail.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/cxacru.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index f1900c567ba4..429ac20a8999 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -700,6 +700,8 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, ret = offd; usb_dbg(instance->usbatm, "cm %#x\n", cm); fail: + if (ret < 0) + usb_kill_urb(instance->rcv_urb); mutex_unlock(&instance->cm_serialize); err: return ret; -- cgit v1.2.3 From 152f174a13618bec1f842d2deb69245cb2ace51f Mon Sep 17 00:00:00 2001 From: Nikhil Solanke Date: Wed, 29 Jul 2026 01:21:57 +0530 Subject: usb: core: Add quirk for 255-bytes initial config read Certain third-party USB game controllers exposing (or spoofing) an Xbox 360-compatible interface (VID:PID 045e:028e) fail to enumerate under Linux. The device disconnects from the bus without responding to the initial GET_DESCRIPTOR(CONFIGURATION) request, and the kernel logs 'unable to read config index 0 descriptor/start: -71'. The device then falls back to a secondary Android HID mode (with a different VID:PID), losing XInput functionality including rumble support. The failure reproduces across multiple machines, host controller types, and kernel versions including current mainline and LTS. The device enumerates correctly and remains in XInput mode under Windows. Notably, the device enumerates correctly in Android mode when the same 9-byte request is issued for that mode's configuration descriptor, confirming the firmware bug is specific to the XInput mode. usbmon traces from Linux and Wireshark/USBPcap traces from Windows are identical up to the point of failure, with no visible protocol-level difference explaining the divergence. The root cause was identified when Michal Pecio discovered via a QEMU bus-level capture that Windows does not use wLength=9 for the initial config descriptor request; it uses wLength=255. Alan Stern subsequently confirmed this with a bus analyzer on a different USB 2.0 device, and Michal verified the behavior goes back to Windows 95 OSR2.1. So, add a new quirk flag USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE which causes usb_get_configuration() to issue a 255 byte sized configuration request instead of USB_DT_CONFIG_SIZE (9) for the initial GET_DESCRIPTOR(CONFIGURATION) request, mimicking long-standing Windows behavior. This patch intentionally does not add any new VID:PID entries using this quirk. Some affected Xbox 360-compatible controllers spoof Microsoft's VID:PID, while genuine Microsoft controllers already enumerate correctly and do not require this quirk. Other affected clone devices use their own VID:PID pairs and can be added individually as they are identified. Suggested-by: Alan Stern Suggested-by: Michal Pecio Closes: https://lore.kernel.org/linux-usb/CAFgddh+JWdT4LLwMc5qjM8q_pBu-fRo2qADR5ovAKoGHWMQrRw@mail.gmail.com/ Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Acked-by: Alan Stern Signed-off-by: Nikhil Solanke Link: https://patch.msgid.link/20260728195158.65162-2-nikhilsolanke5@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/kernel-parameters.txt | 5 ++++ drivers/usb/core/config.c | 32 ++++++++++++++++++------- drivers/usb/core/quirks.c | 4 ++++ include/linux/usb/quirks.h | 3 +++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b5493a7f8f22..3d35270dddef 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -8169,6 +8169,11 @@ Kernel parameters q = USB_QUIRK_FORCE_ONE_CONFIG (Device claims zero configurations, forcing to 1); + r = USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE (Device + fails during initialization when asked for + 9-bytes configuration descriptor request. + Ask for 255-bytes request instead to mirror + Windows' behavior); Example: quirks=0781:5580:bk,0a5c:5834:gij usbhid.mousepoll= diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 45e20c6d76c0..346a2faa9bb8 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -912,6 +912,18 @@ int usb_get_configuration(struct usb_device *dev) unsigned char *bigbuffer; struct usb_config_descriptor *desc; int result; + size_t usb_config_req_size; + + /* + * We usually start by grabbing the first 9-bytes descriptor so we know + * how long the whole configuration is. Some devices with quirky + * firmware will fail enumeration, so if the quirk is set, use 255 instead, + * mirroring the behavior of Windows. + */ + if (dev->quirks & USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE) + usb_config_req_size = 255; + else + usb_config_req_size = USB_DT_CONFIG_SIZE; if (ncfg > USB_MAXCONFIG) { dev_notice(ddev, "too many configurations: %d, " @@ -938,15 +950,13 @@ int usb_get_configuration(struct usb_device *dev) if (!dev->rawdescriptors) return -ENOMEM; - desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL); + desc = kmalloc(usb_config_req_size, GFP_KERNEL); if (!desc) return -ENOMEM; for (cfgno = 0; cfgno < ncfg; cfgno++) { - /* We grab just the first descriptor so we know how long - * the whole configuration is */ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, - desc, USB_DT_CONFIG_SIZE); + desc, usb_config_req_size); if (result < 0) { dev_err(ddev, "unable to read config index %d " "descriptor/%s: %d\n", cfgno, "start", result); @@ -956,16 +966,14 @@ int usb_get_configuration(struct usb_device *dev) dev->descriptor.bNumConfigurations = cfgno; break; } else if (result < 4) { - dev_err(ddev, "config index %d descriptor too short " - "(expected %i, got %i)\n", cfgno, - USB_DT_CONFIG_SIZE, result); + dev_err(ddev, "config index %d descriptor too short (asked for %zu, got %i)\n", + cfgno, usb_config_req_size, result); result = -EINVAL; goto err; } length = max_t(int, le16_to_cpu(desc->wTotalLength), USB_DT_CONFIG_SIZE); - /* Now that we know the length, get the whole thing */ bigbuffer = kmalloc(length, GFP_KERNEL); if (!bigbuffer) { result = -ENOMEM; @@ -975,6 +983,13 @@ int usb_get_configuration(struct usb_device *dev) if (dev->quirks & USB_QUIRK_DELAY_INIT) msleep(200); + /* Skip the second read if we already got everything */ + if (result >= length) { + memcpy(bigbuffer, desc, length); + goto store_and_parse; + } + + /* Get the whole thing */ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length); if (result < 0) { @@ -989,6 +1004,7 @@ int usb_get_configuration(struct usb_device *dev) length = result; } +store_and_parse: dev->rawdescriptors[cfgno] = bigbuffer; result = usb_parse_configuration(dev, cfgno, diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 87ee2d938bc0..f5a60ccf21d3 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -142,6 +142,10 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp) break; case 'q': flags |= USB_QUIRK_FORCE_ONE_CONFIG; + break; + case 'r': + flags |= USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE; + break; /* Ignore unrecognized flag characters */ } } diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index b3cc7beab4a3..a4043b33c2c2 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -81,4 +81,7 @@ /* Device claims zero configurations, forcing to 1 */ #define USB_QUIRK_FORCE_ONE_CONFIG BIT(18) +/* Use a 255 bytes config descriptor request mirroring windows behavior */ +#define USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE BIT(19) + #endif /* __LINUX_USB_QUIRKS_H */ -- cgit v1.2.3 From 37a6e2f9c30245d06de4d5755a9d8cc1de3923f6 Mon Sep 17 00:00:00 2001 From: Nikhil Solanke Date: Wed, 29 Jul 2026 01:21:58 +0530 Subject: usb: hub: Split announce_device() to log device identity before enumeration announce_device() currently logs the device VID:PID and string descriptors only after successful enumeration. This means that if enumeration fails, no identifying information about the device appears in the kernel log, making it difficult to diagnose failures. Split announce_device() into announce_device_ids(), which logs the VID:PID and bcdDevice immediately after the device descriptor is read, and announce_device_strings(), which logs the product, manufacturer, and serial number strings after successful enumeration. This ensures that a device's identity is always visible in the log regardless of whether enumeration succeeds or fails. Suggested-by: Michal Pecio Acked-by: Alan Stern Signed-off-by: Nikhil Solanke Link: https://patch.msgid.link/20260728195158.65162-3-nikhilsolanke5@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5262e11c12cd..d92bf887739d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2401,7 +2401,7 @@ static void show_string(struct usb_device *udev, char *id, char *string) dev_info(&udev->dev, "%s: %s\n", id, string); } -static void announce_device(struct usb_device *udev) +static void announce_device_ids(struct usb_device *udev) { u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice); @@ -2410,6 +2410,10 @@ static void announce_device(struct usb_device *udev) le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), bcdDevice >> 8, bcdDevice & 0xff); +} + +static void announce_device_strings(struct usb_device *udev) +{ dev_info(&udev->dev, "New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", udev->descriptor.iManufacturer, @@ -2420,7 +2424,8 @@ static void announce_device(struct usb_device *udev) show_string(udev, "SerialNumber", udev->serial); } #else -static inline void announce_device(struct usb_device *udev) { } +static inline void announce_device_ids(struct usb_device *udev) { } +static inline void announce_device_strings(struct usb_device *udev) { } #endif @@ -2651,6 +2656,9 @@ int usb_new_device(struct usb_device *udev) device_init_wakeup(&udev->dev, 0); } + /* Announce the device identity */ + announce_device_ids(udev); + /* Tell the runtime-PM framework the device is active */ pm_runtime_set_active(&udev->dev); pm_runtime_get_noresume(&udev->dev); @@ -2672,8 +2680,8 @@ int usb_new_device(struct usb_device *udev) udev->dev.devt = MKDEV(USB_DEVICE_MAJOR, (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); - /* Tell the world! */ - announce_device(udev); + /* Announce the device's product, manufacturer and serial number */ + announce_device_strings(udev); if (udev->serial) add_device_randomness(udev->serial, strlen(udev->serial)); -- cgit v1.2.3 From f3988e68fc089f6a5883f4f807955a3825bb7d45 Mon Sep 17 00:00:00 2001 From: Ishaan Dandekar Date: Sun, 2 Aug 2026 17:31:29 +0530 Subject: usb: quirks: Add ShanWan gamepad to quirk list The ShanWan Wireless Gamepad (dongle ID 2563:0575) crashes with a -71 EPROTO error during standard enumeration because it expects a 255-byte initial configuration request. Add this device to the quirk list to use the USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE flag. Signed-off-by: Ishaan Dandekar Cc: stable Link: https://patch.msgid.link/20260802120128.38302-1-ishaan.dandekar@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 f5a60ccf21d3..b5b577f0b931 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -593,6 +593,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM }, + /* ShanWan Wireless Gamepad */ + { USB_DEVICE(0x2563, 0x0575), .driver_info = USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE }, + /* UGREEN 35871 - BOS descriptor fetch hangs at SuperSpeed Plus */ { USB_DEVICE(0x2b89, 0x5871), .driver_info = USB_QUIRK_NO_BOS }, -- cgit v1.2.3 From 0710dd08824a6f3b9892fc5be24acd2e4a36f178 Mon Sep 17 00:00:00 2001 From: Anatolii Shumak Date: Sat, 1 Aug 2026 08:19:52 +0300 Subject: smb: compress: reject Pattern_V1 when not negotiated Pattern_V1 is an optional chained payload type selected during SMB 3.1.1 compression negotiate. conn->compress_pattern was only consulted when building responses, so a peer that negotiated LZ77 with chained support could still submit Pattern payloads on the receive path. Pass allow_pattern through smb_compression_decompress() and reject SMB3_COMPRESS_PATTERN in the chained decoder when it is false. Link: https://github.com/namjaejeon/ksmbd/issues/529 Fixes: a08de24c2b85 ("ksmbd: negotiate and decode SMB2 compression") Signed-off-by: Anatolii Shumak Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/common/compress/compress.c | 11 ++++++++--- fs/smb/common/compress/compress.h | 3 ++- fs/smb/server/compress.c | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/smb/common/compress/compress.c b/fs/smb/common/compress/compress.c index b07a317597a4..a4123c8f1c0a 100644 --- a/fs/smb/common/compress/compress.c +++ b/fs/smb/common/compress/compress.c @@ -95,6 +95,7 @@ static int smb_decompress_lz77_payload(const u8 **src, u32 *slen, u8 **dst, } static int smb_decompress_chained(__le16 alg, bool allow_chained, + bool allow_pattern, const struct smb2_compression_hdr *hdr, u32 slen, void *dst, u32 dlen) { @@ -143,6 +144,8 @@ static int smb_decompress_chained(__le16 alg, bool allow_chained, rc = smb_decompress_none(&src, &remaining, &out, &out_remaining, len); } else if (payload_alg == SMB3_COMPRESS_PATTERN) { + if (!allow_pattern) + return -EINVAL; rc = smb_decompress_pattern(&src, &remaining, &out, &out_remaining, len); } else if (payload_alg == alg && alg == SMB3_COMPRESS_LZ77) { @@ -185,6 +188,7 @@ static int smb_decompress_unchained(__le16 alg, * smb_compression_decompress() - decode an SMB2 compression transform * @alg: negotiated general-purpose compression algorithm * @allow_chained: whether chained transforms were negotiated + * @allow_pattern: whether Pattern_V1 payloads were negotiated * @src: transform header followed by compressed payload data * @slen: total number of bytes available at @src * @dst: output buffer for the reconstructed SMB2 message @@ -197,7 +201,8 @@ static int smb_decompress_unchained(__le16 alg, * Return: 0 on success, otherwise a negative errno. */ int smb_compression_decompress(__le16 alg, bool allow_chained, - const void *src, u32 slen, void *dst, u32 dlen) + bool allow_pattern, const void *src, u32 slen, + void *dst, u32 dlen) { const struct smb2_compression_hdr *hdr = src; @@ -207,8 +212,8 @@ int smb_compression_decompress(__le16 alg, bool allow_chained, return -EINVAL; if (hdr->Flags == cpu_to_le16(SMB2_COMPRESSION_FLAG_CHAINED)) - return smb_decompress_chained(alg, allow_chained, hdr, slen, - dst, dlen); + return smb_decompress_chained(alg, allow_chained, allow_pattern, + hdr, slen, dst, dlen); if (hdr->Flags != cpu_to_le16(SMB2_COMPRESSION_FLAG_NONE)) return -EINVAL; diff --git a/fs/smb/common/compress/compress.h b/fs/smb/common/compress/compress.h index 7ace3bf4b664..d6916669f887 100644 --- a/fs/smb/common/compress/compress.h +++ b/fs/smb/common/compress/compress.h @@ -20,7 +20,8 @@ static __always_inline bool smb_compress_alg_valid(__le16 alg, bool valid_none) } int smb_compression_decompress(__le16 alg, bool allow_chained, - const void *src, u32 slen, void *dst, u32 dlen); + bool allow_pattern, const void *src, u32 slen, + void *dst, u32 dlen); int smb_compression_compress_chained(__le16 alg, bool allow_pattern, const void *src, u32 slen, void *dst, u32 *dlen); diff --git a/fs/smb/server/compress.c b/fs/smb/server/compress.c index 95e48fa6b448..821299888ad3 100644 --- a/fs/smb/server/compress.c +++ b/fs/smb/server/compress.c @@ -69,6 +69,7 @@ int ksmbd_decompress_request(struct ksmbd_conn *conn) *(__be32 *)out = cpu_to_be32(out_size); rc = smb_compression_decompress(conn->compress_algorithm, conn->compress_chained, + conn->compress_pattern, buf, pdu_size, out + 4, out_size); if (rc) { kvfree(out); -- cgit v1.2.3 From ba3afa8ccd154962c4a6b975b6e8b11027c5ab95 Mon Sep 17 00:00:00 2001 From: Anatolii Shumak Date: Sat, 1 Aug 2026 08:19:53 +0300 Subject: ksmbd: validate compression Flags before kvmalloc ksmbd_decompress_request() allocated the decompressed request buffer before smb_compression_decompress() rejected unknown transform Flags or chained mode when it was not negotiated. A remote peer could force a transient multi-megabyte allocation that was immediately freed on -EINVAL. Validate CHAINED/NONE Flags and compress_chained before kvmalloc. Link: https://github.com/namjaejeon/ksmbd/issues/529 Fixes: a08de24c2b85 ("ksmbd: negotiate and decode SMB2 compression") Signed-off-by: Anatolii Shumak Reviewed-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/compress.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/compress.c b/fs/smb/server/compress.c index 821299888ad3..7e13cae705e2 100644 --- a/fs/smb/server/compress.c +++ b/fs/smb/server/compress.c @@ -46,13 +46,22 @@ int ksmbd_decompress_request(struct ksmbd_conn *conn) return -EINVAL; orig_size = le32_to_cpu(hdr->OriginalCompressedSegmentSize); + /* + * For chained transforms the top-level header is only eight bytes; the + * Flags field overlays the first payload header. Reject unknown Flags + * and unnegotiated chained mode before allocating the output buffer. + */ if (hdr->Flags == cpu_to_le16(SMB2_COMPRESSION_FLAG_CHAINED)) { + if (!conn->compress_chained) + return -EINVAL; out_size = orig_size; - } else { + } else if (hdr->Flags == cpu_to_le16(SMB2_COMPRESSION_FLAG_NONE)) { offset = le32_to_cpu(hdr->Offset); if (offset > pdu_size - sizeof(*hdr) || check_add_overflow(orig_size, offset, &out_size)) return -EINVAL; + } else { + return -EINVAL; } max_allowed_pdu_size = SMB3_MAX_MSGSIZE + conn->vals->max_write_size; -- cgit v1.2.3 From ab88cb66cb0028cb8038b64c2fa71b0f3e91d5f2 Mon Sep 17 00:00:00 2001 From: Sujal Tuladhar Date: Sun, 2 Aug 2026 00:53:03 +0900 Subject: ksmbd: apply the pre-authentication PDU limit when decompressing ksmbd_conn_handler_loop() caps a request from an unauthenticated connection at SMB3_MAX_MSGSIZE, and only allows the larger SMB3_MAX_MSGSIZE + conn->vals->max_write_size once the connection has authenticated. ksmbd_decompress_request() runs inside that same loop but applies the authenticated limit unconditionally, and then allocates from it. The unauthenticated cap is not re-applied afterwards, as the caller only refreshes pdu_size from the new RFC1002 header. An unauthenticated client that negotiates SMB 3.1.1 with a compression context can therefore send a 104 byte chained transform whose OriginalCompressedSegmentSize is SMB3_MAX_MSGSIZE + max_write_size and have ksmbd kvmalloc() that much memory, 4210693 bytes by default. The payload costs the client nothing, because a SMB3_COMPRESS_PATTERN payload expands an eight byte structure into arbitrarily many output bytes. The decompressed PDU is rejected later by ksmbd_smb2_check_message(), but that happens in the worker, after the allocation has been made and conn->req_running has been incremented, and it results in an error response rather than dropping the connection. A client that stops reading its socket keeps each work queued for up to KSMBD_TCP_SEND_TIMEOUT while ksmbd_conn_write() holds conn->srv_mutex, so the allocations accumulate up to server_conf.max_inflight_req per connection. Move the limit into ksmbd_max_allowed_pdu_size() and call it from both sites, so the authentication state is consulted in one place and the two ceilings cannot drift apart again. Fixes: a08de24c2b85 ("ksmbd: negotiate and decode SMB2 compression") Signed-off-by: Sujal Tuladhar Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/compress.c | 2 +- fs/smb/server/connection.c | 6 +----- fs/smb/server/connection.h | 9 +++++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/compress.c b/fs/smb/server/compress.c index 7e13cae705e2..01d1771ff663 100644 --- a/fs/smb/server/compress.c +++ b/fs/smb/server/compress.c @@ -64,7 +64,7 @@ int ksmbd_decompress_request(struct ksmbd_conn *conn) return -EINVAL; } - max_allowed_pdu_size = SMB3_MAX_MSGSIZE + conn->vals->max_write_size; + max_allowed_pdu_size = ksmbd_max_allowed_pdu_size(conn); if (out_size < sizeof(struct smb2_pdu) || out_size > max_allowed_pdu_size || out_size > MAX_STREAM_PROT_LEN) diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index dee8e4aced99..ef6f202f4024 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -488,11 +488,7 @@ recheck: pdu_size = get_rfc1002_len(hdr_buf); ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); - if (ksmbd_conn_good(conn)) - max_allowed_pdu_size = - SMB3_MAX_MSGSIZE + conn->vals->max_write_size; - else - max_allowed_pdu_size = SMB3_MAX_MSGSIZE; + max_allowed_pdu_size = ksmbd_max_allowed_pdu_size(conn); if (pdu_size > max_allowed_pdu_size) { pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n", diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index 2a194ee36fb4..0e4ebfac5558 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -210,6 +210,15 @@ static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; } +static inline unsigned int +ksmbd_max_allowed_pdu_size(struct ksmbd_conn *conn) +{ + if (ksmbd_conn_good(conn)) + return SMB3_MAX_MSGSIZE + conn->vals->max_write_size; + + return SMB3_MAX_MSGSIZE; +} + static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn) { return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE; -- cgit v1.2.3 From 588852647b8183530922033fd69f2e7673ca43e4 Mon Sep 17 00:00:00 2001 From: Rudi Heitbaum Date: Mon, 3 Aug 2026 13:49:38 +0000 Subject: ASoC: rt5645: Make the Kconfig symbol user selectable SND_SOC_RT5645 has no prompt, so it can only be turned on by a machine driver that selects it. Every such driver is x86, MediaTek or Rockchip, which leaves the codec unreachable on any other platform. The part has a devicetree binding of its own, Documentation/devicetree/bindings/sound/realtek,rt5645.yaml, and nothing in the driver is machine specific, so a board that describes it with simple-audio-card or audio-graph-card should be able to build it. Today there is no configuration in which that is possible. Give the symbol a prompt, matching SND_SOC_RT5640 immediately above it. The machine drivers that select it are unaffected. Signed-off-by: Rudi Heitbaum Link: https://patch.msgid.link/anCcciWmO6QOLTsl@5e001e58230e Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 252f683be3c1..d43475a63ac2 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1856,7 +1856,7 @@ config SND_SOC_RT5640 depends on I2C config SND_SOC_RT5645 - tristate + tristate "Realtek RT5645/RT5650 Codec" depends on I2C config SND_SOC_RT5651 -- cgit v1.2.3 From 1ed35ac7f3fe2b4396bdd29ac3a7f0ebc0829e94 Mon Sep 17 00:00:00 2001 From: Linfeng Sun  Date: Sat, 20 Jun 2026 21:00:05 +0800 Subject: vhost_iotlb: bound map allocation in add_range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vhost_iotlb_add_range_ctx() only retires an old entry when the table has a non-zero limit, has exactly reached that limit and has VHOST_IOTLB_FLAG_RETIRE set. Non-retiring tables can keep allocating entries after reaching their configured limit. Existing vhost devices allocate their IOTLB with max_iotlb_entries from vhost.c, which defaults to 2048 and is tunable by module parameter. Use the caller-provided limit at the allocation point instead of adding a separate default in the common IOTLB helper, and reject non-positive values in vhost paths that can report an error. Other vhost IOTLB users should not create zero-limit tables when entries can be populated from userspace or guest-controlled requests. Add caller-side max_iotlb_entries parameters for mlx5 vDPA, VDUSE and vhost-vDPA. Reject non-positive VDUSE and vhost-vDPA values, and require at least two entries for vdpa_sim and mlx5 vDPA paths that install full-range mappings, since those mappings are split into two IOTLB entries. Handle full-range mappings in the common helper by checking that the IOTLB can hold both split entries before inserting the first half. This avoids returning an error after leaving a half mapping behind. When the table is full, keep the existing retire behavior for retiring tables and return -ENOSPC for non-retiring tables. Reuse the retired map node instead of freeing it and allocating a replacement, so a stream of IOTLB updates cannot keep forcing GFP_ATOMIC allocations after the table has reached its limit. If a zero-limit IOTLB still reaches the common helper, treat it as a configuration error and return -EINVAL. I found this bug myself, though the patch was written with AI assistance. Fixes: 0bbe30668d89 ("vhost: factor out IOTLB") Assisted-by: OpenAI-Codex:GPT-5 Signed-off-by: Linfeng Sun  Message-ID: Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/mlx5/core/mlx5_vdpa.h | 2 ++ drivers/vdpa/mlx5/core/mr.c | 5 +++- drivers/vdpa/mlx5/core/resources.c | 11 ++++++++- drivers/vdpa/vdpa_sim/vdpa_sim.c | 10 +++++--- drivers/vdpa/vdpa_user/iova_domain.c | 11 ++++++++- drivers/vhost/iotlb.c | 47 +++++++++++++++++++++++++----------- drivers/vhost/vdpa.c | 9 ++++++- drivers/vhost/vhost.c | 8 ++++++ 8 files changed, 82 insertions(+), 21 deletions(-) diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h index 2cedf7e2dbc4..42f2f44b383c 100644 --- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h +++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h @@ -11,6 +11,8 @@ #define MLX5V_ETH_HARD_MTU (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN) +extern int mlx5_vdpa_max_iotlb_entries; + struct mlx5_vdpa_direct_mr { u64 start; u64 end; diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index 6d02ccf9eb91..7d681961a5cc 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -776,6 +776,9 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, { int err; + if (mlx5_vdpa_max_iotlb_entries < 2) + return -EINVAL; + if (iotlb) err = create_user_mr(mvdev, mr, iotlb); else @@ -784,7 +787,7 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, if (err) return err; - mr->iotlb = vhost_iotlb_alloc(0, 0); + mr->iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0); if (!mr->iotlb) { err = -ENOMEM; goto err_mr; diff --git a/drivers/vdpa/mlx5/core/resources.c b/drivers/vdpa/mlx5/core/resources.c index aeae31d0cefa..28a4d7a35bf4 100644 --- a/drivers/vdpa/mlx5/core/resources.c +++ b/drivers/vdpa/mlx5/core/resources.c @@ -3,8 +3,14 @@ #include #include +#include #include "mlx5_vdpa.h" +int mlx5_vdpa_max_iotlb_entries = 2048; +module_param_named(max_iotlb_entries, mlx5_vdpa_max_iotlb_entries, int, 0444); +MODULE_PARM_DESC(max_iotlb_entries, + "Maximum number of iotlb entries. (default: 2048)"); + static int alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid) { struct mlx5_core_dev *mdev = dev->mdev; @@ -229,7 +235,10 @@ int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey) static int init_ctrl_vq(struct mlx5_vdpa_dev *mvdev) { - mvdev->cvq.iotlb = vhost_iotlb_alloc(0, 0); + if (mlx5_vdpa_max_iotlb_entries < 2) + return -EINVAL; + + mvdev->cvq.iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0); if (!mvdev->cvq.iotlb) return -ENOMEM; diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index 8cb1cc2ea139..4d116644851d 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -34,7 +34,7 @@ MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable"); static int max_iotlb_entries = 2048; module_param(max_iotlb_entries, int, 0444); MODULE_PARM_DESC(max_iotlb_entries, - "Maximum number of iotlb entries for each address space. 0 means unlimited. (default: 2048)"); + "Maximum number of iotlb entries for each address space. (default: 2048)"); static bool use_va = true; module_param(use_va, bool, 0444); @@ -201,6 +201,8 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr, if (!dev_attr->alloc_size) return ERR_PTR(-EINVAL); + if (max_iotlb_entries < 2) + return ERR_PTR(-EINVAL); if (config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) { if (config->device_features & @@ -261,8 +263,10 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr, for (i = 0; i < vdpasim->dev_attr.nas; i++) { vhost_iotlb_init(&vdpasim->iommu[i], max_iotlb_entries, 0); - vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX, 0, - VHOST_MAP_RW); + ret = vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX, + 0, VHOST_MAP_RW); + if (ret) + goto err_iommu; vdpasim->iommu_pt[i] = true; } diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index 4dc76c0d0d13..b6c958224b7c 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -12,11 +12,17 @@ #include #include #include +#include #include #include #include "iova_domain.h" +static int max_iotlb_entries = 2048; +module_param(max_iotlb_entries, int, 0444); +MODULE_PARM_DESC(max_iotlb_entries, + "Maximum number of iotlb entries. (default: 2048)"); + static int vduse_iotlb_add_range(struct vduse_iova_domain *domain, u64 start, u64 last, u64 addr, unsigned int perm, @@ -622,11 +628,14 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size) if (iova_limit <= bounce_size) return NULL; + if (max_iotlb_entries <= 0) + return NULL; + domain = kzalloc_obj(*domain); if (!domain) return NULL; - domain->iotlb = vhost_iotlb_alloc(0, 0); + domain->iotlb = vhost_iotlb_alloc(max_iotlb_entries, 0); if (!domain->iotlb) goto err_iotlb; diff --git a/drivers/vhost/iotlb.c b/drivers/vhost/iotlb.c index e1414c774c34..a1d4376a5b87 100644 --- a/drivers/vhost/iotlb.c +++ b/drivers/vhost/iotlb.c @@ -20,6 +20,14 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map, rb, __u64, __subtree_last, START, LAST, static inline, vhost_iotlb_itree); +static void vhost_iotlb_map_unlink(struct vhost_iotlb *iotlb, + struct vhost_iotlb_map *map) +{ + vhost_iotlb_itree_remove(map, &iotlb->root); + list_del(&map->link); + iotlb->nmaps--; +} + /** * vhost_iotlb_map_free - remove a map node and free it * @iotlb: the IOTLB @@ -28,10 +36,8 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map, void vhost_iotlb_map_free(struct vhost_iotlb *iotlb, struct vhost_iotlb_map *map) { - vhost_iotlb_itree_remove(map, &iotlb->root); - list_del(&map->link); + vhost_iotlb_map_unlink(iotlb, map); kfree(map); - iotlb->nmaps--; } EXPORT_SYMBOL_GPL(vhost_iotlb_map_free); @@ -57,14 +63,25 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb, if (last < start) return -EFAULT; + if (!iotlb->limit) + return -EINVAL; + /* If the range being mapped is [0, ULONG_MAX], split it into two entries * otherwise its size would overflow u64. */ if (start == 0 && last == ULONG_MAX) { u64 mid = last / 2; - int err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr, - perm, opaque); + int err; + + if (iotlb->limit < 2) + return -ENOSPC; + if (!(iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) && + iotlb->nmaps > iotlb->limit - 2) + return -ENOSPC; + + err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr, + perm, opaque); if (err) return err; @@ -72,17 +89,19 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb, start = mid + 1; } - if (iotlb->limit && - iotlb->nmaps == iotlb->limit && - iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) { - map = list_first_entry(&iotlb->list, typeof(*map), link); - vhost_iotlb_map_free(iotlb, map); + if (iotlb->nmaps >= iotlb->limit) { + if (iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) { + map = list_first_entry(&iotlb->list, typeof(*map), link); + vhost_iotlb_map_unlink(iotlb, map); + } else { + return -ENOSPC; + } + } else { + map = kmalloc_obj(*map, GFP_ATOMIC); + if (!map) + return -ENOMEM; } - map = kmalloc_obj(*map, GFP_ATOMIC); - if (!map) - return -ENOMEM; - map->start = start; map->size = last - start + 1; map->last = last; diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index ac55275fa0d0..ef642bc9f97e 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -34,6 +34,11 @@ enum { #define VHOST_VDPA_DEV_MAX (1U << MINORBITS) +static int max_iotlb_entries = 2048; +module_param(max_iotlb_entries, int, 0444); +MODULE_PARM_DESC(max_iotlb_entries, + "Maximum number of iotlb entries. (default: 2048)"); + #define VHOST_VDPA_IOTLB_BUCKETS 16 struct vhost_vdpa_as { @@ -109,12 +114,14 @@ static struct vhost_vdpa_as *vhost_vdpa_alloc_as(struct vhost_vdpa *v, u32 asid) if (asid >= v->vdpa->nas) return NULL; + if (max_iotlb_entries <= 0) + return NULL; as = kmalloc_obj(*as); if (!as) return NULL; - vhost_iotlb_init(&as->iotlb, 0, 0); + vhost_iotlb_init(&as->iotlb, max_iotlb_entries, 0); as->id = asid; hlist_add_head(&as->hash_link, head); diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 4c525b3e16ea..ae29a7ef7baa 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1137,6 +1137,9 @@ EXPORT_SYMBOL_GPL(vhost_dev_set_owner); static struct vhost_iotlb *iotlb_alloc(void) { + if (max_iotlb_entries <= 0) + return NULL; + return vhost_iotlb_alloc(max_iotlb_entries, VHOST_IOTLB_FLAG_RETIRE); } @@ -1981,6 +1984,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m) return -EOPNOTSUPP; if (mem.nregions > max_mem_regions) return -E2BIG; + if (max_iotlb_entries <= 0) + return -EINVAL; newmem = kvzalloc_flex(*newmem, regions, mem.nregions); if (!newmem) return -ENOMEM; @@ -2275,6 +2280,9 @@ int vhost_init_device_iotlb(struct vhost_dev *d) struct vhost_iotlb *niotlb, *oiotlb; int i; + if (max_iotlb_entries <= 0) + return -EINVAL; + niotlb = iotlb_alloc(); if (!niotlb) return -ENOMEM; -- cgit v1.2.3 From 0619aaa34c0c2a2dcb07f0e9c8a34e7efb8c4cdf Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 24 Jun 2026 15:02:02 -0700 Subject: vhost/vdpa: reject overflowing PA map page counts on 32-bit vhost_vdpa_pa_map() adds the IOVA page offset to the user-controlled map size before computing the number of pages to pin. On 32-bit systems, where unsigned long is narrower than u64, that addition can overflow and the code can pin and map fewer pages than the requested IOTLB range. Reject sizes that overflow the unsigned long page-count calculation. Fixes: 22af48cf91aa ("vdpa: factor out vhost_vdpa_pa_map() and vhost_vdpa_pa_unmap()") Acked-by: Michael S. Tsirkin Signed-off-by: Yousef Alhouseen Signed-off-by: Michael S. Tsirkin Message-ID: --- drivers/vhost/vdpa.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index ef642bc9f97e..c3d913bd7cac 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -1109,6 +1109,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v, unsigned int gup_flags = FOLL_LONGTERM; unsigned long npages, cur_base, map_pfn, last_pfn = 0; unsigned long lock_limit, sz2pin, nchunks, i; + unsigned long page_offset; u64 start = iova; long pinned; int ret = 0; @@ -1121,7 +1122,13 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v, if (perm & VHOST_ACCESS_WO) gup_flags |= FOLL_WRITE; - npages = PFN_UP(size + (iova & ~PAGE_MASK)); + page_offset = iova & ~PAGE_MASK; + if (size > ULONG_MAX - page_offset) { + ret = -EINVAL; + goto free; + } + + npages = PFN_UP(size + page_offset); if (!npages) { ret = -EINVAL; goto free; -- cgit v1.2.3 From 727e1f569855df83579edbd73dcb4a0723543a12 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 6 Jul 2026 16:15:37 +0200 Subject: vdpa/mlx5: Fix buffer length in create_direct_keys() We have seen in our CI the following KASAN message: BUG: KASAN: slab-out-of-bounds in cmd_exec+0x550/0xca0 [mlx5_core] Read of size 272 at addr 0000000176795020 by task qemu-system-s39/82764 [...] [<000011388ab3a7a0>] cmd_exec+0x550/0xca0 [mlx5_core] [<000011388ab3b61c>] mlx5_cmd_exec_cb+0x25c/0x4f0 [mlx5_core] [<000011388b21e82e>] mlx5_vdpa_exec_async_cmds+0x22e/0x5e0 [mlx5_vdpa] [<000011388b21fd44>] create_direct_keys+0x954/0xef0 [mlx5_vdpa] [...] The buggy address is located 4128 bytes inside of allocated 4384-byte region [0000000176794000, 0000000176795120) So in essence we read 16 bytes beyond 4384-byte allocation. create_direct_keys calculates the pointer and length for in and out buffers. The size calculation for in includes the entire structure size (out + in + mtt[]) but the pointer passed to cmd_exec points only to the 'in' field, skipping the 'out' field. This causes mlx5_copy_to_msg() to read beyond the allocated buffer by sizeof(out) bytes when copying command data. Properly calculate the input size to match the pointer and allocation size. Fixes: 0071b138d44a ("vdpa/mlx5: Create direct MKEYs in parallel") Signed-off-by: Christian Borntraeger Tested-by: Dragos Tatulea Reviewed-by: Dragos Tatulea Signed-off-by: Michael S. Tsirkin Message-ID: <20260706141537.3510294-1-borntraeger@linux.ibm.com> --- drivers/vdpa/mlx5/core/mr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index 7d681961a5cc..77a479aeaa85 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -233,7 +233,8 @@ static int create_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr * cmds[i].out = cmd_mem->out; cmds[i].outlen = sizeof(cmd_mem->out); cmds[i].in = cmd_mem->in; - cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount); + cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount) - + offsetof(struct mlx5_create_mkey_mem, in); fill_create_direct_mr(mvdev, dmr, cmd_mem); -- cgit v1.2.3 From 71638a456852a18a4578f52d51926ba1d223053b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 3 Aug 2026 11:40:50 -0700 Subject: hwmon: (pmbus/core) Avoid race condition during probe pmbus_write_smbalert_mask() is not guarded, which can cause race conditions with concurrent sysfs attribute accesses. Similar, PMBus accesses in pmbus_init_debugfs() are not guarded, also resulting in potential race conditions. Add guards to both functions to prevent the races. Reported-by: Sashiko Fixes: 221819ca4c36e ("hwmon: (pmbus/core) Add interrupt support") Fixes: 1e069dfd96dfe ("hwmon: (pmbus) Add debugfs for status registers") Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 5567d37f13fe..095e128709af 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3427,6 +3427,8 @@ static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, { int ret; + guard(pmbus_lock)(client); + ret = _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8)); /* @@ -3662,6 +3664,8 @@ static void pmbus_init_debugfs(struct i2c_client *client, if (!entries) return; + guard(pmbus_lock)(client); + /* * Add device-specific entries. * Please note that the PMBUS standard allows all registers to be -- cgit v1.2.3 From 59bd68ab05a8f9c9a60b6ec44682084184803ff4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 23 Jul 2026 10:57:35 -0700 Subject: hwmon: (pmbus) Fix type confusion in notification logic Sashiko reports: At the start of the loop in pmbus_notify(), the code unconditionally casts every attribute to a struct sensor_device_attribute: drivers/hwmon/pmbus/pmbus_core.c:pmbus_notify() { for (i = 0; i < data->num_attributes; i++) { struct device_attribute *da = to_dev_attr(data->group.attrs[i]); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int index = attr->index; ... } However, data->group.attrs can contain other types like struct pmbus_samples_reg or struct pmbus_sensor, which only embed a base struct device_attribute. If da is a struct pmbus_samples_reg, dev_attr is the last member. Casting it to struct sensor_device_attribute and reading the index field appears to access memory past the end of the allocation, which might trigger a slab-out-of-bounds read. Additionally, if da is a struct pmbus_sensor, casting it causes the index field to overlap with the page, phase, and reg fields. Could this produce a garbage mask on little-endian systems that spuriously matches the target reg, page, and flags during an alert? Fix the problem by using struct sensor_device_attr in struct pmbus_sensor and struct pmbus_label. Since those attributes never trigger a notification, set the value of attr->index to -1 for them. Use this value to distinguish from boolean attributes which _can_ trigger a notification and use the index field to encode mask, page, and register values. Fixes: f469bde9afd1 ("hwmon: (pmbus/core) Notify hwmon events") Cc: Vincent Jardin Tested-by: Vincent Jardin Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 61 ++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 095e128709af..3b219ed5386f 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -45,7 +45,7 @@ module_param(wp, int, 0444); struct pmbus_sensor { struct pmbus_sensor *next; char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ - struct device_attribute attribute; + struct sensor_device_attribute attribute; u8 page; /* page number */ u8 phase; /* phase number, 0xff for all phases */ u16 reg; /* register */ @@ -68,7 +68,7 @@ struct pmbus_boolean { struct pmbus_label { char name[PMBUS_NAME_SIZE]; /* sysfs label name */ - struct device_attribute attribute; + struct sensor_device_attribute attribute; char label[PMBUS_NAME_SIZE]; /* label */ }; #define to_pmbus_label(_attr) \ @@ -1241,7 +1241,8 @@ static ssize_t pmbus_show_sensor(struct device *dev, struct device_attribute *devattr, char *buf) { struct i2c_client *client = to_i2c_client(dev->parent); - struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pmbus_sensor *sensor = to_pmbus_sensor(attr); struct pmbus_data *data = i2c_get_clientdata(client); s64 val; @@ -1261,7 +1262,8 @@ static ssize_t pmbus_set_sensor(struct device *dev, { struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_data *data = i2c_get_clientdata(client); - struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pmbus_sensor *sensor = to_pmbus_sensor(attr); s64 val; int ret; u16 regval; @@ -1283,7 +1285,8 @@ static ssize_t pmbus_set_sensor(struct device *dev, static ssize_t pmbus_show_label(struct device *dev, struct device_attribute *da, char *buf) { - struct pmbus_label *label = to_pmbus_label(da); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pmbus_label *label = to_pmbus_label(attr); return sysfs_emit(buf, "%s\n", label->label); } @@ -1436,8 +1439,8 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, bool update, bool readonly, bool writeonly, bool convert) { + struct sensor_device_attribute *a; struct pmbus_sensor *sensor; - struct device_attribute *a; sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) @@ -1461,12 +1464,11 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, sensor->update = update; sensor->convert = convert; sensor->data = -ENODATA; - pmbus_dev_attr_init(a, sensor->name, - readonly ? 0444 : 0644, - writeonly ? pmbus_show_zero : pmbus_show_sensor, - pmbus_set_sensor); + pmbus_attr_init(a, sensor->name, readonly ? 0444 : 0644, + writeonly ? pmbus_show_zero : pmbus_show_sensor, + pmbus_set_sensor, -1); - if (pmbus_add_attribute(data, &a->attr)) + if (pmbus_add_attribute(data, &a->dev_attr.attr)) return NULL; sensor->next = data->sensors; @@ -1483,8 +1485,8 @@ static int pmbus_add_label(struct pmbus_data *data, const char *name, int seq, const char *lstring, int index, int phase) { + struct sensor_device_attribute *a; struct pmbus_label *label; - struct device_attribute *a; label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL); if (!label) @@ -1508,8 +1510,8 @@ static int pmbus_add_label(struct pmbus_data *data, lstring, index, phase); } - pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL); - return pmbus_add_attribute(data, &a->attr); + pmbus_attr_init(a, label->name, 0444, pmbus_show_label, NULL, -1); + return pmbus_add_attribute(data, &a->dev_attr.attr); } /* @@ -2397,7 +2399,7 @@ struct pmbus_samples_attr { struct pmbus_samples_reg { int page; struct pmbus_samples_attr *attr; - struct device_attribute dev_attr; + struct sensor_device_attribute attribute; }; static struct pmbus_samples_attr pmbus_samples_registers[] = { @@ -2419,14 +2421,15 @@ static struct pmbus_samples_attr pmbus_samples_registers[] = { } }; -#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, dev_attr) +#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, attribute) static ssize_t pmbus_show_samples(struct device *dev, struct device_attribute *devattr, char *buf) { int val; struct i2c_client *client = to_i2c_client(dev->parent); - struct pmbus_samples_reg *reg = to_samples_reg(devattr); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pmbus_samples_reg *reg = to_samples_reg(attr); scoped_guard(pmbus_lock, client) { val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg); @@ -2444,7 +2447,8 @@ static ssize_t pmbus_set_samples(struct device *dev, int ret; long val; struct i2c_client *client = to_i2c_client(dev->parent); - struct pmbus_samples_reg *reg = to_samples_reg(devattr); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pmbus_samples_reg *reg = to_samples_reg(attr); if (kstrtol(buf, 0, &val) < 0) return -EINVAL; @@ -2459,6 +2463,7 @@ static ssize_t pmbus_set_samples(struct device *dev, static int pmbus_add_samples_attr(struct pmbus_data *data, int page, struct pmbus_samples_attr *attr) { + struct sensor_device_attribute *a; struct pmbus_samples_reg *reg; reg = devm_kzalloc(data->dev, sizeof(*reg), GFP_KERNEL); @@ -2468,10 +2473,12 @@ static int pmbus_add_samples_attr(struct pmbus_data *data, int page, reg->attr = attr; reg->page = page; - pmbus_dev_attr_init(®->dev_attr, attr->name, 0644, - pmbus_show_samples, pmbus_set_samples); + a = ®->attribute; + + pmbus_attr_init(a, attr->name, 0644, + pmbus_show_samples, pmbus_set_samples, -1); - return pmbus_add_attribute(data, ®->dev_attr.attr); + return pmbus_add_attribute(data, &a->dev_attr.attr); } static int pmbus_add_samples_attributes(struct i2c_client *client, @@ -2979,9 +2986,15 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags) struct device_attribute *da = to_dev_attr(data->group.attrs[i]); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int index = attr->index; - u16 smask = pb_index_to_mask(index); - u8 spage = pb_index_to_page(index); - u16 sreg = pb_index_to_reg(index); + u16 smask, sreg; + u8 spage; + + if (index == -1) + continue; + + smask = pb_index_to_mask(index); + spage = pb_index_to_page(index); + sreg = pb_index_to_reg(index); if (reg == sreg && page == spage && (smask & flags)) { dev_dbg(data->dev, "sysfs notify: %s", da->attr.name); -- cgit v1.2.3 From e5c0235a3c4e9eb047a16cd02323fe4ecf2f570e Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 31 Jul 2026 12:44:09 -0500 Subject: selinux: do not cancel a policy conversion that never started sel_write_load() calls selinux_policy_cancel() when sel_make_policy_nodes() fails, and that helper dereferences the outgoing policy to cancel its sidtab conversion. On the first policy load there is no outgoing policy: security_load_policy() returns early for that case, before it converts anything, and state->policy is still NULL. A first load that fails while building the selinuxfs tree therefore takes a NULL dereference in selinux_policy_cancel(), reached from a write(2) to /sys/fs/selinux/load. Skip the cancel when there is no old policy, mirroring the check security_load_policy() already makes before it converts. Cc: stable@vger.kernel.org Fixes: 02a52c5c8c3b ("selinux: move policy commit after updating selinuxfs") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/services.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 2d828548f3db..90e81186cb2e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2221,7 +2221,9 @@ void selinux_policy_cancel(struct selinux_load_state *load_state) oldpolicy = rcu_dereference_protected(state->policy, lockdep_is_held(&state->policy_mutex)); - sidtab_cancel_convert(oldpolicy->sidtab); + /* a first load has no outgoing policy and converted nothing */ + if (oldpolicy) + sidtab_cancel_convert(oldpolicy->sidtab); selinux_policy_free(load_state->policy); kfree(load_state->convert_data); } -- cgit v1.2.3 From b98a8ac50775540f3804397ed08f61ef9910bcab Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 31 Jul 2026 12:44:10 -0500 Subject: selinux: require a class's permission values to cover its permission count security_get_permissions() sizes an array by the class's permissions.nprim and fills it at value - 1, from the inherited common's permission table and then the class's own. A value no permission defines leaves a NULL that sel_make_perm_files() passes to d_alloc_name(), an oops inside sel_write_load() that strands selinux_state.policy_mutex and leaves every later load in uninterruptible sleep; two permissions sharing a value overwrite the first kstrdup(). Bounding each value by nprim catches neither, and neither would a count: the symbol table is keyed on the permission name, so duplicates pass. Track the values each permission table claims and require them to cover exactly what its count declares, rejecting a count no value can reach. Conforming policies are unaffected. Cc: stable@vger.kernel.org Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 51 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 69777e885ae7..d358200817bd 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1154,7 +1154,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len) return 0; } -static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp) +/* + * Bitmap of the permission values a symtab has claimed. Values are 1-based + * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set + * fits in a u32 and the callers reject an nprim past that width. + */ +static u32 perm_claimed_mask(u32 nprim) +{ + return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0; +} + +static int perm_read(struct policydb *p, struct symtab *s, + struct policy_file *fp, u32 *claimed) { char *key = NULL; struct perm_datum *perdatum; @@ -1178,6 +1189,10 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f /* indexes an nprim-sized array in security_get_permissions() */ if (perdatum->value > s->nprim) goto bad; + /* two permissions cannot share one slot of that array */ + if (*claimed & (1U << (perdatum->value - 1))) + goto bad; + *claimed |= 1U << (perdatum->value - 1); rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) @@ -1198,7 +1213,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file char *key = NULL; struct common_datum *comdatum; __le32 buf[4]; - u32 i, len, nel; + u32 i, len, nel, claimed = 0; int rc; comdatum = kzalloc_obj(*comdatum); @@ -1225,17 +1240,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file if (rc) goto bad; comdatum->permissions.nprim = le32_to_cpu(buf[2]); + /* no permission value can reach a slot past SEL_VEC_MAX */ + rc = -EINVAL; + if (comdatum->permissions.nprim > SEL_VEC_MAX) + goto bad; rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) goto bad; for (i = 0; i < nel; i++) { - rc = perm_read(p, &comdatum->permissions, fp); + rc = perm_read(p, &comdatum->permissions, fp, &claimed); if (rc) goto bad; } + rc = -EINVAL; + if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) { + pr_err("SELinux: common %s does not define every permission it declares\n", + key); + goto bad; + } + hash_eval(&comdatum->permissions.table, "common_permissions", key); rc = symtab_insert(s, key, comdatum); @@ -1369,7 +1395,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * char *key = NULL; struct class_datum *cladatum; __le32 buf[6]; - u32 i, len, len2, ncons, nel, val; + u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0; int rc; cladatum = kzalloc_obj(*cladatum); @@ -1402,6 +1428,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * if (rc) goto bad; cladatum->permissions.nprim = le32_to_cpu(buf[3]); + /* no permission value can reach a slot past SEL_VEC_MAX */ + rc = -EINVAL; + if (cladatum->permissions.nprim > SEL_VEC_MAX) + goto bad; ncons = le32_to_cpu(buf[5]); @@ -1436,11 +1466,22 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * } } for (i = 0; i < nel; i++) { - rc = perm_read(p, &cladatum->permissions, fp); + rc = perm_read(p, &cladatum->permissions, fp, &claimed); if (rc) goto bad; } + /* the class's own permissions must claim the slots the common leaves */ + if (cladatum->comdatum) + inherited = cladatum->comdatum->permissions.nprim; + rc = -EINVAL; + if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) & + ~perm_claimed_mask(inherited))) { + pr_err("SELinux: class %s does not define every permission it declares\n", + key); + goto bad; + } + hash_eval(&cladatum->permissions.table, "class_permissions", key); rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); -- cgit v1.2.3 From 22b05fec62c0fe9864cfceb52f7d0f3a34d9b1dd Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 31 Jul 2026 12:44:11 -0500 Subject: selinux: reject an unclaimed class value in security_get_classes() security_get_classes() sizes an array by p_classes.nprim and fills it at value - 1, so a class value the policy never defines leaves a NULL. sel_make_classes() passes every entry to sel_make_dir(), reaching the same d_alloc_name() dereference as the permission array. The class symbol table is allowed to be sparse (policydb_class_isvalid() exists to absorb that), but this getter builds its own array straight from the hash table and has no such predicate. Fail the lookup when a value went unclaimed instead of handing out the NULL. Conforming policies define every class they declare and are unaffected. Cc: stable@vger.kernel.org Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/services.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 90e81186cb2e..7afce975436e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -3304,6 +3304,7 @@ int security_get_classes(struct selinux_policy *policy, char ***classes, u32 *nclasses) { struct policydb *policydb; + u32 i; int rc; policydb = &policy->policydb; @@ -3316,16 +3317,29 @@ int security_get_classes(struct selinux_policy *policy, rc = hashtab_map(&policydb->p_classes.table, get_classes_callback, *classes); - if (rc) { - u32 i; + if (rc) + goto err; - for (i = 0; i < *nclasses; i++) - kfree((*classes)[i]); - kfree(*classes); + /* + * The class symtab may be sparse, which policydb_class_isvalid() exists + * to absorb; the callback fills this array by value, so an unclaimed + * one leaves a NULL that sel_make_classes() hands to sel_make_dir(). + */ + for (i = 0; i < *nclasses; i++) { + if (!(*classes)[i]) { + rc = -EINVAL; + goto err; + } } out: return rc; + +err: + for (i = 0; i < *nclasses; i++) + kfree((*classes)[i]); + kfree(*classes); + return rc; } static int get_permissions_callback(void *k, void *d, void *args) -- cgit v1.2.3 From a93d37a09b863810653f93d371fb197457d59deb Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 31 Jul 2026 12:44:12 -0500 Subject: selinux: require every boolean value to be defined p_bools.nprim comes from the policy image independently of how many booleans follow it, and cond_index_bool() fills bool_val_to_struct[] at value - 1, so a count larger than the values present leaves NULL entries. Every user of that array then walks it by index and dereferences each entry: cond_evaluate_expr() on the access-vector path, security_get_bools() and security_get_bool_value() behind selinuxfs, and security_set_bools(). A sparse class value is absorbed by policydb_class_isvalid() and its siblings; booleans have no such predicate, and no consumer that could use one. Reject a boolean value that no boolean defines, once, where the array is built. Conforming policies define every boolean they declare and are unaffected. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index d358200817bd..d88713201be9 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -719,6 +719,7 @@ static inline void symtab_hash_eval(struct symtab *s) static int policydb_index(struct policydb *p) { int i, rc; + u32 v; if (p->mls_enabled) pr_debug( @@ -769,6 +770,24 @@ static int policydb_index(struct policydb *p) if (rc) goto out; } + + /* + * A sparse class value is absorbed by policydb_class_isvalid() and + * its siblings, but no such predicate exists for booleans: every + * user of bool_val_to_struct[] walks it by index and dereferences + * each entry -- cond_evaluate_expr(), the two getters and + * security_set_bools() -- so an unclaimed one has no consumer that + * can tolerate it. + */ + for (v = 0; v < p->p_bools.nprim; v++) { + if (!p->bool_val_to_struct[v]) { + pr_err("SELinux: boolean %u is declared but not defined\n", + v + 1); + rc = -EINVAL; + goto out; + } + } + rc = 0; out: return rc; -- cgit v1.2.3 From 0e125ecfe20c077625cf0be8d750d5c3abc0dce9 Mon Sep 17 00:00:00 2001 From: Nathan Gao Date: Fri, 24 Jul 2026 20:08:06 -0700 Subject: tcp: do not change rcv_ssthresh in tcp_measure_rcv_mss() Commit f5da7c45188e ("tcp: adjust rcvq_space after updating scaling ratio") replaced the direct window_clamp update in tcp_measure_rcv_mss() with a call to tcp_set_window_clamp(), a helper that implements the TCP_WINDOW_CLAMP setsockopt. As a side effect, the helper also shrinks rcv_ssthresh via __tcp_adjust_rcv_ssthresh(). As a result, each scaling_ratio decrease detected by tcp_measure_rcv_mss() also cuts rcv_ssthresh. Elsewhere in TCP, rcv_ssthresh is usually cut under memory pressure and grows via tcp_grow_window(). Flows whose segment sizes vary keep scaling_ratio oscillating, which leads to an unstable rcv_ssthresh: a dip of rcv_ssthresh only recovers via tcp_grow_window(), keeping the advertised window at a relatively low level even after the ratio itself has recovered, and can even stall the sender. Observed on a customer's proxy gateway after upgrading from kernel 6.1 to 6.12: in the worst case, rcv_ssthresh was cut in half by a scaling_ratio dip. P99 latency jumped from <10ms on 6.1 to ~100ms on 6.12, and almost returned to the 6.1 level with this patch applied. Restore the plain WRITE_ONCE() update of window_clamp, as introduced in commit a2cbb1603943 ("tcp: Update window clamping condition"), and keep the rcvq_space.space adjustment. Now rcv_ssthresh is decoupled from scaling_ratio changes in tcp_measure_rcv_mss(). Fixes: f5da7c45188e ("tcp: adjust rcvq_space after updating scaling ratio") Signed-off-by: Nathan Gao Link: https://patch.msgid.link/20260725030806.28135-1-zcgao@amazon.com Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index daff93d51342..5b6378b94701 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -252,7 +252,7 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb) struct tcp_sock *tp = tcp_sk(sk); val = tcp_win_from_space(sk, sk->sk_rcvbuf); - tcp_set_window_clamp(sk, val); + WRITE_ONCE(tp->window_clamp, val); if (tp->window_clamp < tp->rcvq_space.space) tp->rcvq_space.space = tp->window_clamp; -- cgit v1.2.3 From 9f2cf069a9a72a2d6b97ca8b4c70e714aac99749 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Thu, 30 Jul 2026 01:00:28 +0900 Subject: sctp: keep chunk->transport in step with the list it is queued on __sctp_outq_flush_rtx() moves a gap-acked chunk onto another transport's transmitted list without updating chunk->transport: if (chunk->tsn_gap_acked) { list_move_tail(&chunk->transmitted_list, &transport->transmitted); continue; } The chunk then sits on a live transport's list while chunk->transport still names a different one. If that transport is removed - sctp_assoc_rm_peer() from an ASCONF Delete-IP - sctp_transport_free() RCU-frees it and the chunk is left with a dangling pointer. sctp_assoc_rm_peer() scrubs peer->transmitted and asoc->outqueue.out_chunk_list, but the chunk is on neither. The pointer is not followed while tsn_gap_acked is set. A SACK that reneges on the TSN clears the flag, and the next SACK reaches tchunk->transport->flight_size -= sctp_data_size(tchunk); inside the freed transport. KASAN reports a slab-use-after-free read in sctp_check_transmitted(), freed from sctp_assoc_rm_peer(). Both the removal and the SACKs come from the association peer. Set chunk->transport at the move. The ordinary resend path needs nothing: it reaches its list_move_tail() only after sctp_packet_append_chunk() returned SCTP_XMIT_OK, and __sctp_packet_append_chunk() has rebound the chunk by then. Discovered by XBOW, triaged by Baul Lee Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Acked-by: Xin Long Link: https://patch.msgid.link/20260729160028.54546-1-baul.lee@xbow.com Signed-off-by: Jakub Kicinski --- net/sctp/outqueue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index f6b8c13dafa4..e92fb9da4647 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -650,6 +650,7 @@ static int __sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, if (chunk->tsn_gap_acked) { list_move_tail(&chunk->transmitted_list, &transport->transmitted); + chunk->transport = transport; continue; } -- cgit v1.2.3 From d1ad8fb2ac6a1afb71dc22d9ae8efb4dda96c824 Mon Sep 17 00:00:00 2001 From: Yuejie Shi Date: Thu, 30 Jul 2026 11:52:32 +0800 Subject: ipv6: fix Route Information option length validation rt6_route_rcv() validates the Route Information option (RFC 4191) length against the prefix length, but both checks are off by one. rinfo->length is the ND option length in units of 8 octets and it *includes* the 8-byte option header, so an option carrying N bytes of prefix has length == 1 + N/8. RFC 4191 section 2.3 requires length 3 when Prefix Length is greater than 64, and 2 or 3 when it is greater than 0. The code accepts length >= 2 and length >= 1 respectively. ipv6_addr_prefix() then copies prefix_len/8 bytes out of rinfo->prefix, so a Router Advertisement with (prefix_len=128, length=2) or (prefix_len=64, length=1) makes the kernel read up to 8 bytes past the end of the option. Those bytes end up in the prefix of the route that gets installed, so they are visible to userspace: # RA with a Route Information option (prefix_len=128, length=2) # followed by a source link-layer address option, 01 01 de ad be ef ca fe $ ip -6 route show 2001:db8:dead:beef:101:dead:beef:cafe via fe80::1234 dev veth0 proto ra ^^^^^^^^^^^^^^^^^^ the next option, read out of bounds When the Route Information option is the last one in the packet, those eight bytes come from the skb tail room instead. Reject the option lengths RFC 4191 does not allow. Fixes: 70ceb4f53929 ("[IPV6]: ROUTE: Add experimental support for Route Information Option in RA (RFC4191).") Cc: stable@vger.kernel.org Signed-off-by: Yuejie Shi Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260730035310.74584-1-syjcnss@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/route.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fc42d67e5822..5968ce5ad150 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -988,13 +988,13 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, } else if (rinfo->prefix_len > 128) { return -EINVAL; } else if (rinfo->prefix_len > 64) { - if (rinfo->length < 2) { + /* RFC 4191: Length MUST be 3 when Prefix Length > 64 */ + if (rinfo->length < 3) return -EINVAL; - } } else if (rinfo->prefix_len > 0) { - if (rinfo->length < 1) { + /* RFC 4191: Length MUST be 2 or 3 when Prefix Length > 0 */ + if (rinfo->length < 2) return -EINVAL; - } } pref = rinfo->route_pref; -- cgit v1.2.3 From d1000fd7995e51deec872d154e0a40d82f7a539f Mon Sep 17 00:00:00 2001 From: Will Chen Date: Wed, 29 Jul 2026 15:01:31 -0700 Subject: bnxt: fix memory leak in bnxt_queue_mem_alloc error cases There is a small memory leak in bnxt_queue_mem_alloc: when bnxt_alloc_rx_agg_bmap() succeeds but bnxt_alloc_one_tpa_info() later fails, the rx_agg_bmap allocated by bnxt_alloc_rx_agg_bmap() is not freed in the fallthrough cleanup cases. Free the rx_agg_bmap in the err_free_rx_agg_ring case and initialize clone->rx_agg_bmap = NULL earlier in the function to allow for safe fallthrough. Fixes: bd649c5cc958 ("bnxt_en: handle tpa_info in queue API implementation") Signed-off-by: Will Chen Reviewed-by: Joe Damato Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260729220132.1256924-1-will.chen.tty@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7513618793da..004ef99f4530 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -16217,6 +16217,7 @@ static int bnxt_queue_mem_alloc(struct net_device *dev, clone->rx_next_cons = 0; clone->need_head_pool = false; clone->rx_page_size = qcfg->rx_page_size; + clone->rx_agg_bmap = NULL; rc = bnxt_alloc_rx_page_pool(bp, clone, rxr->page_pool->p.nid); if (rc) @@ -16269,6 +16270,8 @@ err_free_tpa_info: bnxt_free_one_tpa_info(bp, clone); err_free_rx_agg_ring: bnxt_free_ring(bp, &clone->rx_agg_ring_struct.ring_mem); + kfree(clone->rx_agg_bmap); + clone->rx_agg_bmap = NULL; err_free_rx_ring: bnxt_free_ring(bp, &clone->rx_ring_struct.ring_mem); err_rxq_info_unreg: -- cgit v1.2.3 From ae2567b11c3df43861d05f856bdb3434b3961aa1 Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Thu, 30 Jul 2026 09:38:02 -0700 Subject: btrfs: flush the fixup workers during close_ctree Reintroducing the COW fixup worker brought back the unmount race fixed by commit 41fd1e94066a ("btrfs: wait for fixup workers before stopping cleaner kthread during umount") without bringing back the fix. A fixup work item queued by the final writeback pass can still be in flight when close_ctree() stops the cleaner kthread and frees the fs roots. While destroy_workqueue() drains the queue, that happens after the cleaner thread was freed, so btrfs_add_delayed_iput() called from the fixup worker is no longer safe (not to mention that we are already in BTRFS_FS_STATE_NO_DELAYED_IPUT when it runs). Therefore we need to bring back explicitly flushing the fixup workqueue as in Filipe's original fix. The first flush will catch all the fixup writeback queued during the final sync before umount, but some of that might hit memory allocation errors and stay fixup in the blocks/folio, leading any subsequent writeback triggered *inside* umount (e.g. reclaim workers shutting down) to hit it and queue again. To fix that, and the possibility of any really long-lived pinned folios getting marked, deny queueing new fixup during umount. That allows us to flush twice (once before doing a real writeback pass to get the actual data, second time to clean up any rather unlikely stragglers right before declaring BTRFS_FS_STATE_NO_DELAYED_IPUT) and be certain nothing got re-queued. Reproduced by injecting a one-shot 30s sleep at the head of btrfs_writepage_fixup_worker() on a KASAN kernel, running the normal reproducing read dio workload before unmount and then observing: BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x35/0x50 Read of size 1 at addr ffff88810b4b08f8 by task kworker/u32:5/219 Workqueue: btrfs-fixup btrfs_writepage_fixup_worker [btrfs] Call Trace: _raw_spin_lock_irqsave+0x35/0x50 try_to_wake_up+0xc0/0x18c0 btrfs_writepage_fixup_worker+0x7f3/0xf20 [btrfs] ... Fixes: 4be9c7da6860 ("btrfs: trigger cow fixup via dirty_folio()") Assisted-by: LLM (reproduction, analysis) Reviewed-by: Qu Wenruo Signed-off-by: Boris Burkov Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 21 +++++++++++++++++++++ fs/btrfs/inode.c | 17 +++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 6bb70c43a63f..8bdc94d3ddee 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -4361,6 +4361,18 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) /* clear out the rbtree of defraggable inodes */ btrfs_cleanup_defrag_inodes(fs_info); + /* + * Before the unmount, we sync down all the writeback which can + * generate fixup work. We are about to run delalloc for autodefrag so + * piggy back on that by also flushing the fixup work which can also + * generate delalloc we would like to get run. + * + * After this, it is still possible that some thread doing writeback is + * in btrfs_queue_writepage_fixup() and might finish queueing some final + * work, racing the btrfs_fs_closing() check there. + */ + flush_workqueue(fs_info->fixup_workers); + /* * Handle the error fs first, as it will flush and wait for all ordered * extents. This will generate delayed iputs, thus we want to handle @@ -4438,6 +4450,15 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info) cancel_work_sync(&fs_info->preempt_reclaim_work); cancel_work_sync(&fs_info->em_shrinker_work); + /* + * Reclaim workers can run writeback which can queue fixup. + * After the above cancel_work_sync() calls, any such queueing attempts are + * guaranteed to see btrfs_fs_closing(), so at this point we can genuinely fully + * flush the fixup workqueue. This relies on the belief that *now* no thread can + * still be sitting in btrfs_queue_writepage_fixup(). + */ + flush_workqueue(fs_info->fixup_workers); + /* * Run delayed iputs again because an async reclaim worker may have * added new ones if it was flushing delalloc: diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 98b31a090626..9b1bf2e03497 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2952,6 +2952,23 @@ void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio) struct btrfs_fs_info *fs_info = inode->root->fs_info; struct btrfs_writepage_fixup *fixup; + /* + * Disallow queueing more fixup during unmount to break the cycle + * of writeback queuing fixup queuing writeback etc. + * + * If it actually hit, then something which was fixup wasn't written + * which we should warn about. + */ + if (btrfs_fs_closing(fs_info)) { + btrfs_warn_rl(fs_info, + "dropping unqueued fixup blocks at unmount. root %lld ino %llu folio %llu", + btrfs_root_id(inode->root), btrfs_ino(inode), + folio_pos(folio)); + btrfs_folio_clear_fixup_dirty(fs_info, folio, + folio_pos(folio), folio_size(folio)); + return; + } + fixup = kzalloc_obj(*fixup, GFP_NOFS); if (!fixup) return; -- cgit v1.2.3 From 3971921a055330669b281962ff723d1abb76a58c Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 3 Aug 2026 16:30:05 +0200 Subject: ata: libata-core: Disable LPM on WDC WD141KFGX-68FH9N0 According to a user report, WDC WD141KFGX-68FH9N0 has problems with LPM. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220693 Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 961d03fbc4d9..59e87b987d73 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4420,6 +4420,7 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { */ { "WDC WD100EFGX-68CPLN0", NULL, ATA_QUIRK_NOLPM }, { "WDC WD102KFBX-68M95N0", NULL, ATA_QUIRK_NOLPM }, + { "WDC WD141KFGX-68FH9N0", NULL, ATA_QUIRK_NOLPM }, { "WD Green 2.5 480GB", NULL, ATA_QUIRK_NOLPM }, /* -- cgit v1.2.3 From ebac8f6b1ef0e9278afe204b8692a7479988dace Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 29 Jul 2026 12:16:54 -0700 Subject: vsock/virtio: read virtqueues under worker locks Commit bd50c5dc182b ("vsock/virtio: add support for device suspend/resume") made the *_run flags transition from false to true when restore installs replacement virtqueues. The RX, TX and event workers read their virtqueue before locking and checking the corresponding flag, so a worker delayed across freeze and restore can observe the replacement queue's running state while retaining a pointer to the deleted queue. Read each virtqueue under its mutex after checking the run flag, keeping the pointer and state in the same queue generation. Fixes: bd50c5dc182b ("vsock/virtio: add support for device suspend/resume") Cc: stable@vger.kernel.org Reported-by: Xiang Mei Link: https://lore.kernel.org/r/20260727035804.1860862-1-bestswngs@gmail.com Signed-off-by: Weiming Shi Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/e79f68ad9284c983364fc3ac46904b6d9ef50231.1785352330.git.bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/vmw_vsock/virtio_transport.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index 57f2d6ec3ffc..a8e1dd95ba8c 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -346,12 +346,13 @@ static void virtio_transport_tx_work(struct work_struct *work) struct virtqueue *vq; bool added = false; - vq = vsock->vqs[VSOCK_VQ_TX]; mutex_lock(&vsock->tx_lock); if (!vsock->tx_run) goto out; + vq = vsock->vqs[VSOCK_VQ_TX]; + do { struct sk_buff *skb; unsigned int len; @@ -451,13 +452,13 @@ static void virtio_transport_event_work(struct work_struct *work) container_of(work, struct virtio_vsock, event_work); struct virtqueue *vq; - vq = vsock->vqs[VSOCK_VQ_EVENT]; - mutex_lock(&vsock->event_lock); if (!vsock->event_run) goto out; + vq = vsock->vqs[VSOCK_VQ_EVENT]; + do { struct virtio_vsock_event *event; unsigned int len; @@ -634,13 +635,13 @@ static void virtio_transport_rx_work(struct work_struct *work) container_of(work, struct virtio_vsock, rx_work); struct virtqueue *vq; - vq = vsock->vqs[VSOCK_VQ_RX]; - mutex_lock(&vsock->rx_lock); if (!vsock->rx_run) goto out; + vq = vsock->vqs[VSOCK_VQ_RX]; + do { virtqueue_disable_cb(vq); for (;;) { -- cgit v1.2.3 From a31e0ad444698d8aa7534a0f89fda543730f97a5 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 29 Jul 2026 12:16:55 -0700 Subject: vsock/virtio: avoid refilling the RX queue after teardown Commit b917507e5ad9 ("vsock/virtio: stop workers during the .remove()") made the RX worker jump to its common exit when rx_run is clear. That exit still refills the RX queue when the buffer count is low, so work queued across virtio_vsock_vqs_del() can add buffers after the virtqueues have been deleted. BUG: KASAN: slab-use-after-free in virtqueue_add_sgs Read of size 4 by task kworker/0:1 Workqueue: virtio_vsock virtio_transport_rx_work Call Trace: virtqueue_add_sgs (drivers/virtio/virtio_ring.c:2796) virtio_vsock_rx_fill (net/vmw_vsock/virtio_transport.c:332) virtio_transport_rx_work (net/vmw_vsock/virtio_transport.c:701) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3478) 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) ... Freed by task 141: kfree (mm/slub.c:6566) vp_del_vq (drivers/virtio/virtio_pci_common.c:259) vp_del_vqs (drivers/virtio/virtio_pci_common.c:285) virtio_vsock_freeze (net/vmw_vsock/virtio_transport.c:912) virtio_device_freeze (drivers/virtio/virtio.c:658) virtio_pci_freeze (drivers/virtio/virtio_pci_common.c:601) pci_pm_freeze (drivers/pci/pci-driver.c:1098) device_suspend (drivers/base/power/main.c:1968) Kernel panic - not syncing: KASAN: panic_on_warn set ... Jump to a no-refill exit when rx_run is clear, leaving the normal exit to replenish a running queue. Fixes: b917507e5ad9 ("vsock/virtio: stop workers during the .remove()") Cc: stable@vger.kernel.org Reported-by: Xiang Mei Link: https://lore.kernel.org/r/20260727035804.1860862-1-bestswngs@gmail.com Suggested-by: Stefano Garzarella Signed-off-by: Weiming Shi Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/f9c8c1d64cad9d262f305d02ffe164c2f900fadf.1785352330.git.bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/vmw_vsock/virtio_transport.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index a8e1dd95ba8c..96c9fe8d357c 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -638,7 +638,7 @@ static void virtio_transport_rx_work(struct work_struct *work) mutex_lock(&vsock->rx_lock); if (!vsock->rx_run) - goto out; + goto out_nofill; vq = vsock->vqs[VSOCK_VQ_RX]; @@ -692,6 +692,7 @@ static void virtio_transport_rx_work(struct work_struct *work) out: if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2) virtio_vsock_rx_fill(vsock); +out_nofill: mutex_unlock(&vsock->rx_lock); } -- cgit v1.2.3 From 1bb30b181d9f0484e141f8411e15ed906d5c6780 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:54 -0700 Subject: xsk: require at least 16 bytes of TX metadata AF_XDP accepts a TX metadata length as small as eight bytes, but every supported request needs the flags plus at least one eight-byte request field. Such short metadata also lets the kernel read beyond the registered area. Require 16 bytes rather than sizeof(struct xsk_tx_metadata) to preserve compatibility with applications that do not use launch-time metadata. Fixes: 341ac980eab9 ("xsk: Support tx_metadata_len") Reported-by: AutonomousCodeSecurity@microsoft.com Reported-by: Cen Zhang (Microsoft) Link: https://lore.kernel.org/netdev/20260720155210.34229-1-blbllhy@gmail.com/ Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-2-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- net/xdp/xdp_umem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c index 58da2f4f4397..cd8643360eb3 100644 --- a/net/xdp/xdp_umem.c +++ b/net/xdp/xdp_umem.c @@ -210,6 +210,8 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr) if (mr->flags & XDP_UMEM_TX_METADATA_LEN) { if (mr->tx_metadata_len >= 256 || mr->tx_metadata_len % 8) return -EINVAL; + if (mr->tx_metadata_len < 16) + return -EINVAL; umem->tx_metadata_len = mr->tx_metadata_len; } -- cgit v1.2.3 From 19366db6dfccac9b0867a151678cd7b89fb8fd99 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:55 -0700 Subject: xsk: pass TX metadata pointer by reference Completion handling needs to know whether a timestamp was requested when the metadata was processed. Let xsk_tx_metadata_request() update the caller's metadata pointer so that decision can be carried forward without rereading user-controlled flags. This only changes the interface; behavior remains unchanged. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-3-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c | 14 +++++++++----- drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h | 4 ++-- drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- include/net/libeth/xsk.h | 2 +- include/net/xdp_sock.h | 8 +++++--- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index b3883a5a7d7a..beaea1479485 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -3082,7 +3082,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring) meta_req.tx_buffer = bi; meta_req.meta = meta; meta_req.used_desc = 0; - xsk_tx_metadata_request(meta, &igc_xsk_tx_metadata_ops, + xsk_tx_metadata_request(&meta, &igc_xsk_tx_metadata_ops, &meta_req); /* xsk_tx_metadata_request() may have updated next_to_use */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 6867a5aed42c..822859c360e7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -483,7 +483,7 @@ typedef int (*mlx5e_fp_xmit_xdp_frame_check)(struct mlx5e_xdpsq *); typedef bool (*mlx5e_fp_xmit_xdp_frame)(struct mlx5e_xdpsq *, struct mlx5e_xmit_data *, int, - struct xsk_tx_metadata *); + struct xsk_tx_metadata **); struct mlx5e_xdpsq { /* data path */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index d8c7cb8837d7..dac5e9d4c8bd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -452,11 +452,11 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq INDIRECT_CALLABLE_SCOPE bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - int check_result, struct xsk_tx_metadata *meta); + int check_result, struct xsk_tx_metadata **meta); INDIRECT_CALLABLE_SCOPE bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - int check_result, struct xsk_tx_metadata *meta) + int check_result, struct xsk_tx_metadata **meta) { struct mlx5e_tx_mpwqe *session = &sq->mpwqe; struct mlx5e_xdpsq_stats *stats = sq->stats; @@ -504,7 +504,10 @@ mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptx * and it's safe to complete it at any time. */ mlx5e_xdp_mpwqe_session_start(sq); - xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, &session->wqe->eth); + if (meta) + xsk_tx_metadata_request(meta, + &mlx5e_xsk_tx_metadata_ops, + &session->wqe->eth); } mlx5e_xdp_mpwqe_add_dseg(sq, p, stats); @@ -535,7 +538,7 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq) INDIRECT_CALLABLE_SCOPE bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - int check_result, struct xsk_tx_metadata *meta) + int check_result, struct xsk_tx_metadata **meta) { struct mlx5e_xmit_data_frags *xdptxdf = container_of(xdptxd, struct mlx5e_xmit_data_frags, xd); @@ -649,7 +652,8 @@ mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, sq->pc += num_wqebbs; - xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, eseg); + if (meta) + xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, eseg); sq->doorbell_cseg = cseg; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h index 3c54f8962664..5b8f4094d553 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h @@ -114,11 +114,11 @@ extern const struct xsk_tx_metadata_ops mlx5e_xsk_tx_metadata_ops; INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, int check_result, - struct xsk_tx_metadata *meta)); + struct xsk_tx_metadata **meta)); INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, int check_result, - struct xsk_tx_metadata *meta)); + struct xsk_tx_metadata **meta)); INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq *sq)); INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c index 8aeab4b21035..3d19dad8f868 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c @@ -105,7 +105,7 @@ bool mlx5e_xsk_tx(struct mlx5e_xdpsq *sq, unsigned int budget) ret = INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, mlx5e_xmit_xdp_frame, sq, &xdptxd, - check_result, meta); + check_result, &meta); if (unlikely(!ret)) { if (sq->mpwqe.wqe) mlx5e_xdp_mpwqe_complete(sq); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3801f9d45278..7c3a7c56a81a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2748,7 +2748,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) meta_req.set_ic = &set_ic; meta_req.tbs = tx_q->tbs; meta_req.edesc = &tx_q->dma_entx[entry]; - xsk_tx_metadata_request(meta, &stmmac_xsk_tx_metadata_ops, + xsk_tx_metadata_request(&meta, &stmmac_xsk_tx_metadata_ops, &meta_req); if (set_ic) { tx_q->tx_count_frames = 0; diff --git a/include/net/libeth/xsk.h b/include/net/libeth/xsk.h index 82b5d21aae87..e2fa6bf6b1b3 100644 --- a/include/net/libeth/xsk.h +++ b/include/net/libeth/xsk.h @@ -205,7 +205,7 @@ __libeth_xsk_xmit_fill_buf_md(const struct xdp_desc *xdesc, BUILD_BUG_ON(!__builtin_constant_p(tmo == libeth_xsktmo)); tmo = tmo == libeth_xsktmo ? &__libeth_xsktmo : tmo; - xsk_tx_metadata_request(ctx.meta, tmo, &desc); + xsk_tx_metadata_request(&ctx.meta, tmo, &desc); return desc; } diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 8b51876efbed..06c081feff42 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -153,17 +153,19 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, /** * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission * and call appropriate xsk_tx_metadata_ops operation. - * @meta: pointer to AF_XDP metadata area + * @pmeta: pointer to pointer to AF_XDP metadata area * @ops: pointer to struct xsk_tx_metadata_ops * @priv: pointer to driver-private aread * * This function should be called by the networking device when * it prepares AF_XDP egress packet. */ -static inline void xsk_tx_metadata_request(const struct xsk_tx_metadata *meta, +static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, const struct xsk_tx_metadata_ops *ops, void *priv) { + const struct xsk_tx_metadata *meta = *pmeta; + if (!meta) return; @@ -231,7 +233,7 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, { } -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata *meta, +static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, const struct xsk_tx_metadata_ops *ops, void *priv) { -- cgit v1.2.3 From 9f60a67df8d3c862503bee62bada8e7089cba438 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:56 -0700 Subject: xsk: clear metadata pointer when no timestamp is requested User space can change metadata flags after request processing. Rereading them during completion can therefore make the kernel write a timestamp that was not requested when the packet was submitted. Clear the metadata pointer during request processing unless timestamp completion is requested. Completion handling can then use the pointer itself instead of rereading the flags. On the mlx5 multi-packet WQE path metadata is evaluated per batch: xsk_tx_metadata_request() runs only for the descriptor that starts a session, just like the checksum offload that is applied once through the shared WQE. Only that descriptor's pointer is reset, so completion handling can record a timestamp for the other descriptors of the session regardless of their own XDP_TXMD_FLAGS_TIMESTAMP bit. The write stays inside the metadata area; the single-WQE, other zero-copy, and generic paths reset the pointer per descriptor and are unaffected. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-4-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- include/net/xdp_sock.h | 14 ++++++++++---- net/xdp/xsk.c | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 06c081feff42..2b2eb9b9d580 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -141,13 +141,16 @@ INDIRECT_CALLABLE_DECLARE(void xsk_destruct_skb(struct sk_buff *)); static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, struct xsk_tx_metadata_compl *compl) { + compl->tx_timestamp = NULL; + if (!meta) return; - if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) - compl->tx_timestamp = &meta->completion.tx_timestamp; - else - compl->tx_timestamp = NULL; + /* we can only arrive here if the completion timestamp has been + * requested via XDP_TXMD_FLAGS_TIMESTAMP, see xsk_tx_metadata_request + */ + + compl->tx_timestamp = &meta->completion.tx_timestamp; } /** @@ -182,6 +185,9 @@ static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) ops->tmo_request_checksum(meta->request.csum_start, meta->request.csum_offset, priv); + + if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + *pmeta = NULL; } /** diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index f906d51b6699..fcc6f17f3576 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -998,6 +998,8 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) skb->skb_mstamp_ns = meta->request.launch_time; + if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + meta = NULL; xsk_tx_metadata_to_compl(meta, &skb_shinfo(skb)->xsk_meta); return 0; -- cgit v1.2.3 From 439ce2dddf3d22129b9113a7881637256a35e936 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:57 -0700 Subject: xsk: validate launch-time metadata size Launch-time metadata extends beyond the first 16 bytes of struct xsk_tx_metadata. Reject the request when the registered metadata area does not contain the complete field. Snapshot the validated flags for the generic transmit path and use that snapshot for request and completion processing, avoiding inconsistent decisions if user space changes the flags concurrently. Note that only xsk_skb_metadata is properly using the flags, __xsk_buff_get_metadata ignores them. Next commits address that. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-5-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- include/net/xdp_sock_drv.h | 17 +++++++++++++---- net/xdp/xsk.c | 11 ++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index 46797645a0c2..68a787601357 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -260,21 +260,28 @@ xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr) 0) static inline bool -xsk_buff_valid_tx_metadata(const struct xsk_tx_metadata *meta) +xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, + const struct xsk_tx_metadata *meta, u64 *flags) { - return !(meta->flags & ~XDP_TXMD_FLAGS_VALID); + *flags = READ_ONCE(meta->flags); + if (*flags & XDP_TXMD_FLAGS_LAUNCH_TIME) + if (pool->tx_metadata_len < + offsetofend(struct xsk_tx_metadata, request.launch_time)) + return false; + return !(*flags & ~XDP_TXMD_FLAGS_VALID); } static inline struct xsk_tx_metadata * __xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data) { struct xsk_tx_metadata *meta; + u64 flags; if (!pool->tx_metadata_len) return NULL; meta = data - pool->tx_metadata_len; - if (unlikely(!xsk_buff_valid_tx_metadata(meta))) + if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) return NULL; /* no way to signal the error to the user */ return meta; @@ -469,7 +476,9 @@ xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr) return (struct xdp_desc_ctx){ }; } -static inline bool xsk_buff_valid_tx_metadata(struct xsk_tx_metadata *meta) +static inline bool +xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, + const struct xsk_tx_metadata *meta, u64 *flags) { return false; } diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index fcc6f17f3576..7855ee09c4b6 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -967,15 +967,16 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, { struct xsk_tx_metadata *meta = NULL; u16 csum_start, csum_offset; + u64 flags; if (unlikely(pool->tx_metadata_len == 0)) return -EINVAL; meta = buffer - pool->tx_metadata_len; - if (unlikely(!xsk_buff_valid_tx_metadata(meta))) + if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) return -EINVAL; - if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) { + if (flags & XDP_TXMD_FLAGS_CHECKSUM) { csum_start = READ_ONCE(meta->request.csum_start); csum_offset = READ_ONCE(meta->request.csum_offset); @@ -996,9 +997,9 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, } } - if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) - skb->skb_mstamp_ns = meta->request.launch_time; - if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME) + skb->skb_mstamp_ns = READ_ONCE(meta->request.launch_time); + if (!(flags & XDP_TXMD_FLAGS_TIMESTAMP)) meta = NULL; xsk_tx_metadata_to_compl(meta, &skb_shinfo(skb)->xsk_meta); -- cgit v1.2.3 From ddd0d6c5bfe2fef7c7cf31f62265f29b7b9eb9ef Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:58 -0700 Subject: xsk: move xsk_tx_metadata_request() to xdp_sock_drv.h xsk_tx_metadata_request() must validate metadata with xsk_buff_valid_tx_metadata(), which is defined in xdp_sock_drv.h. Move the helper there before adding that dependency. All callers already include the destination header, so this has no functional effect. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-6-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- include/net/xdp_sock.h | 43 ------------------------------------------- include/net/xdp_sock_drv.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 2b2eb9b9d580..6e70b320b399 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -153,43 +153,6 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, compl->tx_timestamp = &meta->completion.tx_timestamp; } -/** - * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission - * and call appropriate xsk_tx_metadata_ops operation. - * @pmeta: pointer to pointer to AF_XDP metadata area - * @ops: pointer to struct xsk_tx_metadata_ops - * @priv: pointer to driver-private aread - * - * This function should be called by the networking device when - * it prepares AF_XDP egress packet. - */ -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, - const struct xsk_tx_metadata_ops *ops, - void *priv) -{ - const struct xsk_tx_metadata *meta = *pmeta; - - if (!meta) - return; - - if (ops->tmo_request_launch_time) - if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) - ops->tmo_request_launch_time(meta->request.launch_time, - priv); - - if (ops->tmo_request_timestamp) - if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) - ops->tmo_request_timestamp(priv); - - if (ops->tmo_request_checksum) - if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) - ops->tmo_request_checksum(meta->request.csum_start, - meta->request.csum_offset, priv); - - if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) - *pmeta = NULL; -} - /** * xsk_tx_metadata_complete - Evaluate AF_XDP TX metadata at completion * and call appropriate xsk_tx_metadata_ops operation. @@ -239,12 +202,6 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, { } -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, - const struct xsk_tx_metadata_ops *ops, - void *priv) -{ -} - static inline void xsk_tx_metadata_complete(struct xsk_tx_metadata_compl *compl, const struct xsk_tx_metadata_ops *ops, void *priv) diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index 68a787601357..f87c4215673e 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -271,6 +271,43 @@ xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, return !(*flags & ~XDP_TXMD_FLAGS_VALID); } +/** + * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission + * and call appropriate xsk_tx_metadata_ops operation. + * @pmeta: pointer to pointer to AF_XDP metadata area + * @ops: pointer to struct xsk_tx_metadata_ops + * @priv: pointer to driver-private aread + * + * This function should be called by the networking device when + * it prepares AF_XDP egress packet. + */ +static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, + const struct xsk_tx_metadata_ops *ops, + void *priv) +{ + const struct xsk_tx_metadata *meta = *pmeta; + + if (!meta) + return; + + if (ops->tmo_request_launch_time) + if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) + ops->tmo_request_launch_time(meta->request.launch_time, + priv); + + if (ops->tmo_request_timestamp) + if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) + ops->tmo_request_timestamp(priv); + + if (ops->tmo_request_checksum) + if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) + ops->tmo_request_checksum(meta->request.csum_start, + meta->request.csum_offset, priv); + + if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + *pmeta = NULL; +} + static inline struct xsk_tx_metadata * __xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data) { @@ -483,6 +520,12 @@ xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, return false; } +static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, + const struct xsk_tx_metadata_ops *ops, + void *priv) +{ +} + static inline struct xsk_tx_metadata * __xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data) { -- cgit v1.2.3 From 849b1664dbda1cf6c63e0fd4f9dec23782b8c851 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:59 -0700 Subject: xsk: validate metadata when processing requests The zero-copy path validates TX metadata while obtaining the descriptor context, then reads it again later when preparing the hardware request. User space can change the metadata between those operations and bypass the original validation. Validate the metadata in xsk_tx_metadata_request() and use the resulting flags snapshot for every feature check. Read request fields once so all zero-copy drivers process only values observed after successful validation. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-7-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c | 5 ++- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +- include/net/libeth/xsk.h | 2 +- include/net/xdp_sock_drv.h | 51 ++++++++++++----------- net/xdp/xsk_buff_pool.c | 6 +-- 6 files changed, 37 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index beaea1479485..16fcc8cf6191 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -3082,7 +3082,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring) meta_req.tx_buffer = bi; meta_req.meta = meta; meta_req.used_desc = 0; - xsk_tx_metadata_request(&meta, &igc_xsk_tx_metadata_ops, + xsk_tx_metadata_request(pool, &meta, &igc_xsk_tx_metadata_ops, &meta_req); /* xsk_tx_metadata_request() may have updated next_to_use */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index dac5e9d4c8bd..77ea51bfbaae 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -505,7 +505,7 @@ mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptx */ mlx5e_xdp_mpwqe_session_start(sq); if (meta) - xsk_tx_metadata_request(meta, + xsk_tx_metadata_request(sq->xsk_pool, meta, &mlx5e_xsk_tx_metadata_ops, &session->wqe->eth); } @@ -653,7 +653,8 @@ mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, sq->pc += num_wqebbs; if (meta) - xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, eseg); + xsk_tx_metadata_request(sq->xsk_pool, meta, + &mlx5e_xsk_tx_metadata_ops, eseg); sq->doorbell_cseg = cseg; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7c3a7c56a81a..97b28cfa14c2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2748,8 +2748,8 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) meta_req.set_ic = &set_ic; meta_req.tbs = tx_q->tbs; meta_req.edesc = &tx_q->dma_entx[entry]; - xsk_tx_metadata_request(&meta, &stmmac_xsk_tx_metadata_ops, - &meta_req); + xsk_tx_metadata_request(pool, &meta, + &stmmac_xsk_tx_metadata_ops, &meta_req); if (set_ic) { tx_q->tx_count_frames = 0; stmmac_set_tx_ic(priv, tx_desc); diff --git a/include/net/libeth/xsk.h b/include/net/libeth/xsk.h index e2fa6bf6b1b3..5dcc0d7f65b7 100644 --- a/include/net/libeth/xsk.h +++ b/include/net/libeth/xsk.h @@ -205,7 +205,7 @@ __libeth_xsk_xmit_fill_buf_md(const struct xdp_desc *xdesc, BUILD_BUG_ON(!__builtin_constant_p(tmo == libeth_xsktmo)); tmo = tmo == libeth_xsktmo ? &__libeth_xsktmo : tmo; - xsk_tx_metadata_request(&ctx.meta, tmo, &desc); + xsk_tx_metadata_request(sq->pool, &ctx.meta, tmo, &desc); return desc; } diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index f87c4215673e..b344789f5df8 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -245,7 +245,7 @@ static inline void *xsk_buff_raw_get_data(struct xsk_buff_pool *pool, u64 addr) * details. * * Return: new &xdp_desc_ctx struct containing desc's DMA address and metadata - * pointer, if it is present and valid (initialized to %NULL otherwise). + * pointer, if it is present (initialized to %NULL otherwise). */ static inline struct xdp_desc_ctx xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr) @@ -274,54 +274,56 @@ xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, /** * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission * and call appropriate xsk_tx_metadata_ops operation. + * @pool: pointer to AF_XDP buffer pool, used to validate the metadata * @pmeta: pointer to pointer to AF_XDP metadata area * @ops: pointer to struct xsk_tx_metadata_ops - * @priv: pointer to driver-private aread + * @priv: pointer to driver-private area * * This function should be called by the networking device when * it prepares AF_XDP egress packet. */ -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, - const struct xsk_tx_metadata_ops *ops, - void *priv) +static inline void +xsk_tx_metadata_request(const struct xsk_buff_pool *pool, + struct xsk_tx_metadata **pmeta, + const struct xsk_tx_metadata_ops *ops, void *priv) { const struct xsk_tx_metadata *meta = *pmeta; + u64 flags; if (!meta) return; + if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) { + *pmeta = NULL; + return; /* no way to signal the error to the user */ + } + if (ops->tmo_request_launch_time) - if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) - ops->tmo_request_launch_time(meta->request.launch_time, - priv); + if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME) + ops->tmo_request_launch_time( + READ_ONCE(meta->request.launch_time), priv); if (ops->tmo_request_timestamp) - if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) + if (flags & XDP_TXMD_FLAGS_TIMESTAMP) ops->tmo_request_timestamp(priv); if (ops->tmo_request_checksum) - if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) - ops->tmo_request_checksum(meta->request.csum_start, - meta->request.csum_offset, priv); + if (flags & XDP_TXMD_FLAGS_CHECKSUM) + ops->tmo_request_checksum( + READ_ONCE(meta->request.csum_start), + READ_ONCE(meta->request.csum_offset), priv); - if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + if (!(flags & XDP_TXMD_FLAGS_TIMESTAMP)) *pmeta = NULL; } static inline struct xsk_tx_metadata * __xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data) { - struct xsk_tx_metadata *meta; - u64 flags; - if (!pool->tx_metadata_len) return NULL; - meta = data - pool->tx_metadata_len; - if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) - return NULL; /* no way to signal the error to the user */ - - return meta; + return data - pool->tx_metadata_len; } static inline struct xsk_tx_metadata * @@ -520,9 +522,10 @@ xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, return false; } -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, - const struct xsk_tx_metadata_ops *ops, - void *priv) +static inline void +xsk_tx_metadata_request(const struct xsk_buff_pool *pool, + struct xsk_tx_metadata **pmeta, + const struct xsk_tx_metadata_ops *ops, void *priv) { } diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index a4089480b22b..78c14f106395 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -765,11 +765,11 @@ EXPORT_SYMBOL(xp_raw_get_dma); * @addr: desc address (from userspace) * * Helper for getting desc's DMA address and metadata pointer, if present. - * Saves one call on hotpath, double calculation of the actual address, - * and inline checks for metadata presence and sanity. + * Saves one call on hotpath and double calculation of the actual address. + * Metadata is validated later by xsk_tx_metadata_request(). * * Return: new &xdp_desc_ctx struct containing desc's DMA address and metadata - * pointer, if it is present and valid (initialized to %NULL otherwise). + * pointer, if it is present (initialized to %NULL otherwise). */ struct xdp_desc_ctx xp_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr) { -- cgit v1.2.3 From 1f428e30947395d9b9aacee03e25a4e6cfcad7a4 Mon Sep 17 00:00:00 2001 From: Yi Cong Date: Wed, 29 Jul 2026 11:04:36 +0800 Subject: net: usb: ax88179_178a: fix skb leak in ax88179_tx_fixup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the interface has NETIF_F_SG enabled and skb_linearize() fails in ax88179_tx_fixup(), the function returns NULL without freeing the skb. usbnet_start_xmit() treats a NULL return from tx_fixup() as a drop (info->flags does not set FLAG_MULTI_PACKET for this driver), jumping to the "drop" label where it does `if (skb) dev_kfree_skb_any(skb)`. Because tx_fixup() returned NULL, the local skb variable in usbnet_start_xmit() is NULL, so the original skb is never freed — a memory leak on every TX frame whose linearization fails (i.e. under memory pressure). Free the skb before returning, matching the error handling already used for the pskb_expand_head() failure path in the same function. Fixes: 16b1c4e01c89 ("net: usb: ax88179_178a: add TSO feature") Cc: stable@vger.kernel.org Signed-off-by: Yi Cong Link: https://patch.msgid.link/20260729030436.3420477-1-cong.yi@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/usb/ax88179_178a.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 98f899ea2e94..81d8412ce8e2 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1487,8 +1487,10 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) headroom = skb_headroom(skb) - 8; - if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb)) + if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb)) { + dev_kfree_skb_any(skb); return NULL; + } if ((skb_header_cloned(skb) || headroom < 0) && pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) { -- cgit v1.2.3 From 90f305f2c7a30257c683e13f4bf7c798eea992a0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 Jul 2026 11:30:45 -0700 Subject: Input: evdev - fix information leak in evdev_pass_values() In evdev_pass_values(), the input_event structure is allocated on the kernel stack and populated field-by-field. However, it is never fully initialized. On architectures where struct input_event contains explicit or implicit padding (such as the 32-bit __pad field on SPARC64), these padding bytes are left uninitialized. When this event structure is subsequently passed to the client buffer and later copied to userspace, the uninitialized padding bytes leak kernel stack memory, potentially exposing sensitive information. Similar issues exist in __evdev_queue_syn_dropped and __pass_event. Fix this by explicitly zeroing the entire event structure with memset() before populating its fields. This ensures all padding bytes are cleared before the data crosses the security boundary. Reported-by: sashiko-bot@kernel.org Cc: stable@vger.kernel.org Link: https://patch.msgid.link/ampGGKo4UMKru6f5@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index c7325226cb86..531db70aa9f9 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -146,11 +146,11 @@ static void __evdev_queue_syn_dropped(struct evdev_client *client) struct timespec64 ts = ktime_to_timespec64(ev_time[client->clk_type]); struct input_event ev; + memset(&ev, 0, sizeof(ev)); ev.input_event_sec = ts.tv_sec; ev.input_event_usec = ts.tv_nsec / NSEC_PER_USEC; ev.type = EV_SYN; ev.code = SYN_DROPPED; - ev.value = 0; client->buffer[client->head++] = ev; client->head &= client->bufsize - 1; @@ -218,20 +218,20 @@ static void __pass_event(struct evdev_client *client, client->head &= client->bufsize - 1; if (unlikely(client->head == client->tail)) { + struct input_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.input_event_sec = event->input_event_sec; + ev.input_event_usec = event->input_event_usec; + ev.type = EV_SYN; + ev.code = SYN_DROPPED; + /* * This effectively "drops" all unconsumed events, leaving * EV_SYN/SYN_DROPPED plus the newest event in the queue. */ client->tail = (client->head - 2) & (client->bufsize - 1); - - client->buffer[client->tail] = (struct input_event) { - .input_event_sec = event->input_event_sec, - .input_event_usec = event->input_event_usec, - .type = EV_SYN, - .code = SYN_DROPPED, - .value = 0, - }; - + client->buffer[client->tail] = ev; client->packet_head = client->tail; } @@ -253,6 +253,8 @@ static void evdev_pass_values(struct evdev_client *client, if (client->revoked) return; + memset(&event, 0, sizeof(event)); + ts = ktime_to_timespec64(ev_time[client->clk_type]); event.input_event_sec = ts.tv_sec; event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC; -- cgit v1.2.3 From 1cb4298810e27e037d3ca07286ecbb97e89ba58d Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Fri, 31 Jul 2026 12:22:59 -0700 Subject: bnge: Fix NULL pointer dereference in aux device release If allocation of auxr_dev fails during auxiliary device setup, the error path calls auxiliary_device_uninit(), which eventually invokes bnge_aux_dev_release(). The release callback unconditionally dereferences aux_priv->auxr_dev->pdev to retrieve the parent bnge_dev. Since auxr_dev has not yet been allocated on this failure path, the dereference results in a NULL pointer exception Retrieve the parent bnge_dev from the auxiliary device's parent instead of auxr_dev, and free auxr_dev only when it was successfully allocated. This allows the release callback to correctly clean up partially initialized auxiliary devices. Fixes: 8ac050ec3b1c ("bng_en: Add RoCE aux device support") Signed-off-by: Alok Tiwari Reviewed-by: Bhargava Marreddy Link: https://patch.msgid.link/20260731192301.1427645-1-alok.a.tiwari@oracle.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_auxr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c index 67e93e17d4d9..0955b488b6fe 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c @@ -141,12 +141,15 @@ static void bnge_aux_dev_release(struct device *dev) { struct bnge_auxr_priv *aux_priv = container_of(dev, struct bnge_auxr_priv, aux_dev.dev); - struct bnge_dev *bd = pci_get_drvdata(aux_priv->auxr_dev->pdev); + struct bnge_auxr_dev *auxr_dev = aux_priv->auxr_dev; + struct bnge_dev *bd = pci_get_drvdata(to_pci_dev(dev->parent)); ida_free(&bnge_aux_dev_ids, aux_priv->id); - kfree(aux_priv->auxr_dev->auxr_info); + if (auxr_dev) { + kfree(auxr_dev->auxr_info); + kfree(auxr_dev); + } bd->auxr_dev = NULL; - kfree(aux_priv->auxr_dev); kfree(aux_priv); bd->aux_priv = NULL; } -- cgit v1.2.3 From 5d9686af2976741bbd79b150d1c9e60b81e7f12e Mon Sep 17 00:00:00 2001 From: Daming Li Date: Thu, 30 Jul 2026 22:55:52 +0800 Subject: net: smc: fix splice entry lifetime imbalance in smc_rx_splice smc_rx_splice() passes pages to splice_to_pipe() before taking the references that cover the lifetime of each splice entry. In the VM-backed RMB path, splice_to_pipe() may drop unqueued entries through smc_rx_spd_release(), while queued entries are released later via the pipe buffer callback. The old post-splice accounting also derives the number of queued VM pages from an offset mutated while building the descriptor, and a multi-page splice pairs one sock_hold() with multiple sock_put() calls. Take the page and socket references for every candidate entry before splice_to_pipe(), and drop the matching private state, page reference, and socket reference from smc_rx_spd_release() for entries that never get queued. This fixes a refcount imbalance that can underflow page refcounts and trigger a use-after-free. Fixes: 9014db202cb7 ("smc: add support for splice()") Cc: stable@vger.kernel.org Reported-by: Vega Co-developed-by: Xiao Liu Signed-off-by: Xiao Liu Signed-off-by: Daming Li Signed-off-by: Ren Wei Reviewed-by: Dust Li Reviewed-by: Sidraya Jayagond Link: https://patch.msgid.link/20260730145552.360287-2-enjou1224z@gmail.com Signed-off-by: Jakub Kicinski --- net/smc/smc_rx.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index c1d9b923938d..5c9e4d8b57de 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -150,7 +150,12 @@ static const struct pipe_buf_operations smc_pipe_ops = { static void smc_rx_spd_release(struct splice_pipe_desc *spd, unsigned int i) { + struct smc_spd_priv *priv = (struct smc_spd_priv *)spd->partial[i].private; + struct sock *sk = &priv->smc->sk; + + kfree(priv); put_page(spd->pages[i]); + sock_put(sk); } static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len, @@ -209,6 +214,10 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len, offset = 0; } } + for (i = 0; i < nr_pages; i++) { + get_page(pages[i]); + sock_hold(&smc->sk); + } spd.nr_pages_max = nr_pages; spd.nr_pages = nr_pages; spd.pages = pages; @@ -217,16 +226,8 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len, spd.spd_release = smc_rx_spd_release; bytes = splice_to_pipe(pipe, &spd); - if (bytes > 0) { - sock_hold(&smc->sk); - if (!lgr->is_smcd && smc->conn.rmb_desc->is_vm) { - for (i = 0; i < PAGE_ALIGN(bytes + offset) / PAGE_SIZE; i++) - get_page(pages[i]); - } else { - get_page(smc->conn.rmb_desc->pages); - } + if (bytes > 0) atomic_add(bytes, &smc->conn.splice_pending); - } kfree(priv); kfree(partial); kfree(pages); -- cgit v1.2.3 From 4ff9548d84945d2cbf9e4c207288063a200ea397 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Thu, 30 Jul 2026 12:59:26 +0000 Subject: ipv4: Fix fib_nlmsg_size() for RTA_VIA nexthops fib_nlmsg_size() still estimates nexthop space as if every gateway is encoded as an IPv4 RTA_GATEWAY attribute. IPv4 routes can also carry an IPv6 gateway, which fib_nexthop_info() dumps as RTA_VIA. As a result, route notifications can allocate an skb that is too small. fib_dump_info() then fails with -EMSGSIZE and rtmsg_fib() hits the WARN_ON() that marks such failures as a fib_nlmsg_size() bug. With panic_on_warn set, this becomes a kernel panic. Mirror the actual nexthop dump layout in fib_nlmsg_size(): account for IPv6 nexthop gateways dumped as RTA_VIA, for the no-header rtnexthop layout used inside RTA_MULTIPATH, and for RTA_FLOW only when it is actually present. Fixes: d15662682db2 ("ipv4: Allow ipv6 gateway with ipv4 routes") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zihan Xi Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/6f53fa797fcaeb26966432ed7ae9bb87c4961f37.1785411220.git.zihanx@nebusec.ai Signed-off-by: Jakub Kicinski --- net/ipv4/fib_semantics.c | 67 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 4f3c0740dde9..78f84ae3ee12 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -490,6 +490,34 @@ int ip_fib_check_default(__be32 gw, struct net_device *dev) return -1; } +static size_t fib_nexthop_nlmsg_size(const struct fib_nh_common *nhc, + bool skip_oif) +{ + size_t nhsize = 0; + + switch (nhc->nhc_gw_family) { + case AF_INET: + nhsize += nla_total_size(4); /* RTA_GATEWAY */ + break; + case AF_INET6: + nhsize += nla_total_size(sizeof(struct rtvia) + + sizeof(struct in6_addr)); + break; + } + + if (!skip_oif && nhc->nhc_dev) + nhsize += nla_total_size(4); /* RTA_OIF */ + + if (nhc->nhc_lwtstate) { + /* RTA_ENCAP */ + nhsize += lwtunnel_get_encap_size(nhc->nhc_lwtstate); + /* RTA_ENCAP_TYPE */ + nhsize += nla_total_size(2); + } + + return nhsize; +} + size_t fib_nlmsg_size(struct fib_info *fi) { size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) @@ -507,32 +535,35 @@ size_t fib_nlmsg_size(struct fib_info *fi) payload += nla_total_size(4); /* RTA_NH_ID */ if (nhs) { - size_t nh_encapsize = 0; - /* Also handles the special case nhs == 1 */ - - /* each nexthop is packed in an attribute */ - size_t nhsize = nla_total_size(sizeof(struct rtnexthop)); + size_t mpsize = 0; unsigned int i; - /* may contain flow and gateway attribute */ - nhsize += 2 * nla_total_size(4); - - /* grab encap info */ for (i = 0; i < fib_info_num_path(fi); i++) { struct fib_nh_common *nhc = fib_info_nhc(fi, i); + size_t nhsize; + + nhsize = fib_nexthop_nlmsg_size(nhc, nhs != 1); - if (nhc->nhc_lwtstate) { - /* RTA_ENCAP_TYPE */ - nh_encapsize += lwtunnel_get_encap_size( - nhc->nhc_lwtstate); - /* RTA_ENCAP */ - nh_encapsize += nla_total_size(2); + if (nhs != 1) + nhsize += NLA_ALIGN(sizeof(struct rtnexthop)); + +#ifdef CONFIG_IP_ROUTE_CLASSID + if (nhc->nhc_family == AF_INET) { + struct fib_nh *nh; + + nh = container_of(nhc, struct fib_nh, nh_common); + if (nh->nh_tclassid) + nhsize += nla_total_size(4); } +#endif + if (nhs == 1) + payload += nhsize; + else + mpsize += nhsize; } - /* all nexthops are packed in a nested attribute */ - payload += nla_total_size((nhs * nhsize) + nh_encapsize); - + if (nhs != 1) + payload += nla_total_size(mpsize); } return payload; -- cgit v1.2.3 From d141f087b1af656f055d7c5793a3e87817ba0bbe Mon Sep 17 00:00:00 2001 From: Hidayath Khan Date: Thu, 30 Jul 2026 16:22:16 +0200 Subject: s390/qeth: validate user buffer length in SNMP and ARP query ioctls qeth_snmp_command() and qeth_l3_arp_query() allocate a buffer sized by a user-supplied length (udata_len) without checking a lower bound, then set udata_offset to a fixed non-zero value and pass both to a reply callback. The callback bounds-checks the copy with if ((udata_len - udata_offset) < len) Both fields are u32, so a udata_len smaller than udata_offset makes the subtraction wrap and the check pass, and the following memcpy() writes past the allocation. A udata_len of 0 also yields ZERO_SIZE_PTR from kzalloc(), which the existing NULL check does not catch. Reject buffers smaller than udata_offset before allocating, so the callback subtraction can no longer underflow. Fixes: 4a71df50047f ("qeth: new qeth device driver") Cc: stable@vger.kernel.org Reviewed-by: Alexandra Winter Signed-off-by: Hidayath Khan Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260730142216.218309-1-hidayath@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/s390/net/qeth_core_main.c | 3 +++ drivers/s390/net/qeth_l3_main.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index f18eed9df3c7..c3257b213360 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4710,6 +4710,9 @@ static int qeth_snmp_command(struct qeth_card *card, char __user *udata) if (req_len > QETH_BUFSIZE) return -EINVAL; + if (qinfo.udata_len < sizeof(struct qeth_snmp_ureq_hdr)) + return -EINVAL; + iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, req_len); if (!iob) return -ENOMEM; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 1542bfc9f561..f1ac9950dcb4 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1415,6 +1415,11 @@ static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) rc = -EFAULT; goto out; } + + if (qinfo.udata_len < QETH_QARP_ENTRIES_OFFSET) { + rc = -EINVAL; + goto out; + } qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); if (!qinfo.udata) { rc = -ENOMEM; -- cgit v1.2.3 From d0f86fb36eb260abd10007b62c9dcc1028e03e61 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Thu, 30 Jul 2026 17:35:54 +0800 Subject: udp: fix potential use-after-free in tunnel segmentation __skb_udp_tunnel_segment() gets the UDP header before ensuring the tunnel header is in the skb head. If the pull reallocates skb->head, the saved UDP header pointer is no longer valid. Get the UDP header after the pull to avoid a potential use-after-free. Fixes: dbef491ebe7f ("udp: Use uh->len instead of skb->len to compute checksum in segmentation") Signed-off-by: Xuanqiang Luo Reviewed-by: Antoine Tenart Link: https://patch.msgid.link/20260730093554.68127-1-xuanqiang.luo@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv4/udp_offload.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 29651b1a0bc7..abcd3cc2d9a3 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -178,17 +178,19 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); bool remcsum, need_csum, offload_csum, gso_partial; struct sk_buff *segs = ERR_PTR(-EINVAL); - struct udphdr *uh = udp_hdr(skb); u16 mac_offset = skb->mac_header; __be16 protocol = skb->protocol; u16 mac_len = skb->mac_len; int udp_offset, outer_hlen; + struct udphdr *uh; __wsum partial; bool need_ipsec; if (unlikely(!pskb_may_pull(skb, tnl_hlen))) goto out; + uh = udp_hdr(skb); + /* Adjust partial header checksum to negate old length. * We cannot rely on the value contained in uh->len as it is * possible that the actual value exceeds the boundaries of the -- cgit v1.2.3 From 2a33516f9ef59ad11844d4fc152f889449b5daf3 Mon Sep 17 00:00:00 2001 From: Toke Høiland-Jørgensen Date: Wed, 29 Jul 2026 21:14:16 +0200 Subject: net/sched: sch_cake: drop WARN_ON(1) for malformed packets in ACK filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sch_cake ACK filter parses packets to find the TCP header and filter duplicated ACKs if the flow is backlogged. The parsing code contains a WARN_ON(1) which can be triggered by a malformed IP header in certain cases. Depending on the system configuration, this leads either to either spamming dmesg with warnings, or a panic if panic_on_warn is set. The code already correctly skips the offending packet in the branch that triggers the warning, so the WARN_ON itself doesn't really serve any purpose. So just drop it altogether to avoid the inconvenient side effects. Fixes: 8b7138814f29 ("sch_cake: Add optional ACK filter") Reported-by: Zhiling Zou Reported-by: Ren Wei Signed-off-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260729191417.45665-1-toke@toke.dk Signed-off-by: Jakub Kicinski --- net/sched/sch_cake.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index f64be54ead49..f25f60978631 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1287,7 +1287,6 @@ static struct sk_buff *cake_ack_filter(struct cake_sched_data *q, seglen = ipv6_payload_len(skb, ipv6h_check); } else { - WARN_ON(1); /* shouldn't happen */ continue; } -- cgit v1.2.3 From d876c493fc4b811941bfeb4c80beb2dfc4bf025e Mon Sep 17 00:00:00 2001 From: Linfeng Sun Date: Mon, 27 Jul 2026 16:18:41 +0800 Subject: vhost-scsi: Validate T10 PI scatterlist counts When T10 PI is negotiated, vhost-scsi splits protection bytes from the data iterator before mapping the request scatterlists. A malformed request can claim protection bytes that cover or exceed the full payload length. The former leaves no data bytes to map, while the latter underflows exp_data_len before advancing the iterator. Both cases can let a zero data SGL count reach sg_alloc_table_chained(), which triggers BUG_ON(!nents). Reject protection lengths that cover or exceed the payload before subtracting prot_bytes and advancing the iterator. Also propagate negative errors from the protection SGL calculation before calling the allocator, matching the data SGL path. Fixes: bca939d5bcd0 ("vhost-scsi: Dynamically allocate scatterlists") Suggested-by: Jia Jia Signed-off-by: Jia Jia Assisted-by: OpenAI-Codex:GPT-5 Signed-off-by: Linfeng Sun Message-ID: <20260727081841.923151-1-slf@hdu.edu.cn> Signed-off-by: Michael S. Tsirkin --- drivers/vhost/scsi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 9a1253b9d8c5..c79197edb163 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -972,6 +972,9 @@ vhost_scsi_mapal(struct vhost_scsi *vs, struct vhost_scsi_cmd *cmd, if (prot_bytes) { sgl_count = vhost_scsi_calc_sgls(prot_iter, prot_bytes, VHOST_SCSI_PREALLOC_PROT_SGLS); + if (sgl_count < 0) + return sgl_count; + cmd->prot_table.sgl = cmd->prot_sgl; ret = sg_alloc_table_chained(&cmd->prot_table, sgl_count, cmd->prot_table.sgl, @@ -1416,6 +1419,11 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) * actual data payload length. */ if (prot_bytes) { + if (prot_bytes >= exp_data_len) { + vq_err(vq, "Protection data exceeds payload length\n"); + goto err; + } + exp_data_len -= prot_bytes; prot_iter = data_iter; iov_iter_truncate(&prot_iter, prot_bytes); -- cgit v1.2.3 From de845981da67a6b049080c87e605130b0c30adc5 Mon Sep 17 00:00:00 2001 From: Jun Yang Date: Mon, 3 Aug 2026 09:45:14 +0800 Subject: vhost: reset the vring metadata cache on vring reconfiguration vq->meta_iotlb[] caches the vhost_iotlb_map that backs each vring metadata region, and iotlb_access_ok() returns early on a cache hit, taking the hit as proof that the region has already been validated: if (vhost_vq_meta_fetch(vq, addr, len, type)) return true; The cache is reset on VHOST_IOTLB_UPDATE and VHOST_IOTLB_INVALIDATE, on device IOTLB (re)initialisation and on vq reset, but not when VHOST_SET_VRING_ADDR replaces vq->desc, vq->avail and vq->used, nor when VHOST_SET_VRING_NUM changes the region sizes. With a device IOTLB attached both ioctls are accepted while the vq is live, and neither validates the addresses at ioctl time: vq_access_ok() and vq_log_used_access_ok() return true early because the addresses are GIOVAs, deferring validation to prefetch time. Once the cache has been populated that deferred validation no longer runs -- vq_meta_prefetch() hits the stale entry and returns true -- and vhost_vq_meta_fetch() keeps translating through the old mapping as map->addr + addr - map->start for an address the mapping no longer covers. vhost_copy_to_user() and vhost_copy_from_user() consume the result with __copy_to_user() and __copy_from_user(), which do not check it either, so a subsequent used ring update or descriptor fetch accesses memory outside the region the IOTLB actually maps. Reset the metadata cache whenever the vring is reconfigured, so the new addresses are pushed back through iotlb_access_ok()'s slow path. Fixes: f88949138058 ("vhost: introduce O(1) vq metadata cache") Cc: stable@vger.kernel.org Assisted-by: tencentos-corvus-ai:kimi-k3 Signed-off-by: Jun Yang Message-ID: <20260803014823.68623-1-juny24602@gmail.com> Signed-off-by: Michael S. Tsirkin --- drivers/vhost/vhost.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index ae29a7ef7baa..269efad90369 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -2131,6 +2131,14 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d, BUG(); } + /* + * The metadata cache holds the IOTLB mapping that backed the previous + * desc/avail/used addresses and vring size, both of which are being + * replaced here. iotlb_access_ok() takes a cache hit as proof that the + * region was validated, so the stale entries have to go. + */ + __vhost_vq_meta_reset(vq); + mutex_unlock(&vq->mutex); return r; -- cgit v1.2.3 From 22598f55a4c2b510b3df5e69e563387a963222ae Mon Sep 17 00:00:00 2001 From: Jia Jia Date: Fri, 24 Jul 2026 14:09:19 +0800 Subject: vhost-scsi: flush backend after device ioctls vhost-scsi translates guest response descriptors into userspace iovecs when commands are submitted. Target-core completes those commands asynchronously, so VHOST_SET_MEM_TABLE can replace the memory table while an in-flight command still retains response iovecs translated through the old table. If the old mapping is reused after VHOST_SET_MEM_TABLE returns, command completion can write the response to an unrelated userspace object. Flush the vhost-scsi backend after vhost_dev_ioctl() handles a device ioctl. This waits for in-flight commands that can still use the old response iovecs before the ioctl returns. Signed-off-by: Jia Jia Signed-off-by: Michael S. Tsirkin Message-ID: <20260724060919.1569170-1-physicalmtea@gmail.com> --- drivers/vhost/scsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index c79197edb163..aae1164e1ca9 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -2434,9 +2434,10 @@ vhost_scsi_ioctl(struct file *f, default: mutex_lock(&vs->dev.mutex); r = vhost_dev_ioctl(&vs->dev, ioctl, argp); - /* TODO: flush backend after dev ioctl. */ if (r == -ENOIOCTLCMD) r = vhost_vring_ioctl(&vs->dev, ioctl, argp); + else + vhost_scsi_flush(vs); mutex_unlock(&vs->dev.mutex); return r; } -- cgit v1.2.3 From 42bc45df5905e2b7dccb72adaf7730f66cfbe03f Mon Sep 17 00:00:00 2001 From: Jia Jia Date: Sun, 26 Jul 2026 22:43:14 +0800 Subject: vhost-scsi: reject feature changes after endpoint vhost_scsi_setup_vq_cmds() runs from VHOST_SCSI_SET_ENDPOINT and allocates each command's protection scatterlist array (prot_sgl) according to the acknowledged VIRTIO_SCSI_F_T10_PI bit. The command pools are not rebuilt when VHOST_SET_FEATURES changes that bit later. Although virtio feature bits must not change after feature negotiation, vhost_scsi_set_features() currently accepts such a request after the endpoint is active and updates acked_features. Enabling T10-PI after endpoint setup therefore leaves prot_sgl NULL while the I/O path follows the new feature bit. For a 129-page protection payload, vhost_scsi_mapal() passes the missing first chunk to sg_alloc_table_chained(): sg_alloc_table_chained(table, 129, first_chunk=NULL, nents_first_chunk=inline_sg_cnt) sg_pool_index() then hits: BUG_ON(nents > SG_CHUNK_SIZE); /* 129 > 128 */ The kernel reported the following call trace and register state: Call Trace: ? __sg_alloc_table+0x1d8/0x250 ? __pfx_vhost_run_work_list+0x10/0x10 [vhost] sg_alloc_table_chained+0x59/0xf0 ? __pfx_sg_pool_alloc+0x10/0x10 ? vhost_scsi_calc_sgls.constprop.0+0x43/0x60 [vhost_scsi] vhost_scsi_handle_vq+0xf02/0x1700 [vhost_scsi] ? __pfx_vhost_scsi_handle_vq+0x10/0x10 [vhost_scsi] vhost_scsi_handle_kick+0x37/0x50 [vhost_scsi] vhost_run_work_list+0x8e/0xd0 [vhost] vhost_task_fn+0xe1/0x210 ret_from_fork+0x348/0x540 RIP: 0010:0x4 CR2 = 0x4 RSP: 0018:ffffc90000dbf940 EFLAGS: 00010202 RAX: ffffffff82396810 RBX: ffff88811dc28b80 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000820 RDI: 0000000000000081 VHOST_F_LOG_ALL is a vhost-specific runtime feature and remains the only exception. Reject changes to any feature other than VHOST_F_LOG_ALL while the endpoint is active. This preserves the existing runtime log toggle while preventing feature-dependent command resources and data-path state from becoming inconsistent. Userspace must clear the endpoint before changing any other negotiated feature and set the endpoint up again afterward. Fixes: bf2d650391be ("vhost-scsi: Allocate T10 PI structs only when enabled") Signed-off-by: Jia Jia Reviewed-by: Stefan Hajnoczi Signed-off-by: Michael S. Tsirkin Message-ID: <20260726144314.1652934-1-physicalmtea@gmail.com> --- drivers/vhost/scsi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index aae1164e1ca9..7a1f39a327da 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -2227,6 +2227,7 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) { struct vhost_virtqueue *vq; bool is_log, was_log; + u64 old_features; int i; if (features & ~VHOST_SCSI_FEATURES) @@ -2242,6 +2243,14 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) if (!vs->dev.nvqs) goto out; + old_features = vs->vqs[0].vq.acked_features; + if (vs->vs_tpg && + ((features ^ old_features) & + ~(1ULL << VHOST_F_LOG_ALL))) { + mutex_unlock(&vs->dev.mutex); + return -EBUSY; + } + is_log = features & (1 << VHOST_F_LOG_ALL); /* * All VQs should have same feature. -- cgit v1.2.3 From 6cdd8cbbf89611da6c948d58daed1c53f4bf8321 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Thu, 23 Jul 2026 10:33:09 +0800 Subject: MAINTAINERS: add Ryan Chen and Billy Tsai as reviewer for ARM/ASPEED Add Ryan Chen and myself as a reviewer for the ARM/ASPEED MACHINE SUPPORT entry to reflect ongoing review and contribution work on AST2xxx/AST27xx platform support. Signed-off-by: Billy Tsai Signed-off-by: Andrew Jeffery --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..791a5d5865d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2676,6 +2676,8 @@ F: drivers/irqchip/irq-aspeed-i2c-ic.c ARM/ASPEED MACHINE SUPPORT M: Joel Stanley M: Andrew Jeffery +R: Ryan Chen +R: Billy Tsai L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) S: Supported -- cgit v1.2.3 From 27f380ef0e1d3de3cde114e02d33f9320ce3a5a6 Mon Sep 17 00:00:00 2001 From: Jianing Li Date: Mon, 3 Aug 2026 21:18:52 -0700 Subject: Input: hynitron_cstxxx - validate touch count and finger IDs The driver allocates max_touch_num input slots, which are indexed from zero through max_touch_num - 1. The current check allows a finger ID equal to max_touch_num to reach cst3xx_report_contact(). While the input core ignores out-of-range slot indices, reporting touch data without a valid slot change corrupts the touch state of the previously active slot. The touch count is read from the controller's report and is used to index the fixed-size report buffer without first checking its range. Reject counts larger than the supported number of touch slots before checking the trailing byte or parsing touch data. Reject finger IDs equal to or greater than max_touch_num, and return immediately when an invalid finger ID is encountered so that corrupt touch frames are discarded instead of reporting partial contact state. The V821 Avaota F1 board configures the vendor driver with one touch slot, so finger ID 1 is already invalid on that device. Fixes: 66603243f528 ("Input: add driver for Hynitron cstxxx touchscreens") Signed-off-by: Jianing Li Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260804031339.2379-1-m13940358460@163.com Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/hynitron_cstxxx.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/hynitron_cstxxx.c b/drivers/input/touchscreen/hynitron_cstxxx.c index 1d8ca90dcda6..af66d91848b3 100644 --- a/drivers/input/touchscreen/hynitron_cstxxx.c +++ b/drivers/input/touchscreen/hynitron_cstxxx.c @@ -313,6 +313,12 @@ static void cst3xx_touch_report(struct i2c_client *client) return; touch_cnt = buf[5] & CST3XX_TOUCH_COUNT_MASK; + if (touch_cnt > ts_data->chip->max_touch_num) { + dev_err(&client->dev, "cst3xx invalid touch count (%d vs %d max)\n", + touch_cnt, ts_data->chip->max_touch_num); + return; + } + /* * Check the check bit of the last touch slot. The check bit is * always present after touch point 1 for valid data, and then @@ -335,9 +341,10 @@ static void cst3xx_touch_report(struct i2c_client *client) finger_id = (buf[idx] >> 4) & 0x0f; /* Sanity check we don't have more fingers than we expect */ - if (ts_data->chip->max_touch_num < finger_id) { - dev_err(&client->dev, "cst3xx touch read failure\n"); - break; + if (finger_id >= ts_data->chip->max_touch_num) { + dev_err(&client->dev, + "cst3xx invalid finger id %d\n", finger_id); + return; } /* sw value of 0 means no touch, 0x03 means touch */ -- cgit v1.2.3 From 3e8ec7c0387273329374f5c7bd61f5f38af71fe1 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 3 Aug 2026 11:12:31 -0700 Subject: fsverity: Fix bpf_get_fsverity_digest() dynptr assumptions The BPF verifier and the dynptr abstraction ensure that the memory space referenced by a dynptr remains valid. They do not, however, provide any guarantee that the contents of the memory are stable. kfuncs are expected to remain memory-safe even if concurrent modifications occur. bpf_get_fsverity_digest() didn't follow that: it could crash if arg->digest_size was concurrently modified. Fix that by using the known-good value hash_alg->digest_size instead. Also widen 'dynptr_sz' and 'out_digest_sz' to u64 to match the return type of __bpf_dynptr_size(). It doesn't appear that it can actually be more than INT_MAX currently (since __bpf_dynptr_data_rw() excludes file-based pointers), but the correct type might as well be used. Fixes: 67814c00de31 ("bpf, fsverity: Add kfunc bpf_get_fsverity_digest") Signed-off-by: Eric Biggers Acked-by: Kumar Kartikeya Dwivedi Acked-by: Song Liu Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/20260803181232.14743-2-ebiggers@kernel.org Signed-off-by: Kumar Kartikeya Dwivedi --- fs/verity/measure.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/verity/measure.c b/fs/verity/measure.c index cfe2d5e535f9..f8b3526af004 100644 --- a/fs/verity/measure.c +++ b/fs/verity/measure.c @@ -122,11 +122,11 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, const struct bpf_dynp { const struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p; const struct inode *inode = file_inode(file); - u32 dynptr_sz = __bpf_dynptr_size(digest_ptr); + u64 dynptr_sz = __bpf_dynptr_size(digest_ptr); struct fsverity_digest *arg; const struct fsverity_info *vi; const struct fsverity_hash_alg *hash_alg; - int out_digest_sz; + u64 out_digest_sz; if (dynptr_sz < sizeof(struct fsverity_digest)) return -EINVAL; @@ -150,11 +150,13 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, const struct bpf_dynp out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest); /* copy digest */ - memcpy(arg->digest, vi->file_digest, min_t(int, hash_alg->digest_size, out_digest_sz)); + memcpy(arg->digest, vi->file_digest, + min(hash_alg->digest_size, out_digest_sz)); /* fill the extra buffer with zeros */ if (out_digest_sz > hash_alg->digest_size) - memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size); + memset(arg->digest + hash_alg->digest_size, 0, + out_digest_sz - hash_alg->digest_size); return 0; } -- cgit v1.2.3 From 7c68ed5c5ad4c185ea9654f5d8ee36560277b7dd Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 3 Aug 2026 11:12:32 -0700 Subject: fsverity: Fix silent truncation in bpf_get_fsverity_digest() bpf_get_fsverity_digest() silently truncates the digest if the provided buffer is too small. This is a footgun, and it doesn't match the semantics of the equivalent UAPI (FS_IOC_MEASURE_VERITY). Change it to return -EOVERFLOW instead, matching FS_IOC_MEASURE_VERITY. Fixes: 67814c00de31 ("bpf, fsverity: Add kfunc bpf_get_fsverity_digest") Signed-off-by: Eric Biggers Acked-by: Song Liu Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/20260803181232.14743-3-ebiggers@kernel.org Signed-off-by: Kumar Kartikeya Dwivedi --- fs/verity/measure.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/verity/measure.c b/fs/verity/measure.c index f8b3526af004..68dfccb69772 100644 --- a/fs/verity/measure.c +++ b/fs/verity/measure.c @@ -144,14 +144,15 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, const struct bpf_dynp hash_alg = vi->tree_params.hash_alg; + out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest); + if (out_digest_sz < hash_alg->digest_size) + return -EOVERFLOW; + arg->digest_algorithm = hash_alg - fsverity_hash_algs; arg->digest_size = hash_alg->digest_size; - out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest); - /* copy digest */ - memcpy(arg->digest, vi->file_digest, - min(hash_alg->digest_size, out_digest_sz)); + memcpy(arg->digest, vi->file_digest, hash_alg->digest_size); /* fill the extra buffer with zeros */ if (out_digest_sz > hash_alg->digest_size) -- cgit v1.2.3 From 3d26cd1f3ff25cebd10d4b0e8188cf40dade28e9 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Tue, 4 Aug 2026 11:36:39 +0300 Subject: usb: xhci: use BIT_ULL for CRCR bits to fix incorrect 64bit mask xhci is unusable on some systems after driver switched to BIT() macro. Upper 32bits of 64bit CRCR command register are unintentionally cleared. Seen on a raspberry pi 4B compiled for arm32. The main symptoms were the following log message: [ 0.549897] raspberrypi-firmware soc:firmware: Attached to firmware from 2021-02-25T12:11:39 [ 0.626859] xhci_hcd 0000:01:00.0: xHCI Host Controller [ 0.626889] xhci_hcd 0000:01:00.0: new USB bus registered, assigned bus number 1 [ 0.812619] xhci_hcd 0000:01:00.0: hcc params 0x002841eb hci version 0x100 quirks 0x0000200000000890 [ 0.813188] xhci_hcd 0000:01:00.0: xHCI Host Controller [ 0.813203] xhci_hcd 0000:01:00.0: new USB bus registered, assigned bus number 2 [ 0.813219] xhci_hcd 0000:01:00.0: Host supports USB 3.0 SuperSpeed [ 0.813602] hub 1-0:1.0: USB hub found [ 0.814052] hub 2-0:1.0: USB hub found [ 0.952714] xhci_hcd 0000:01:00.0: ERROR mismatched command completion event Additionally running lsusb just hangs. Running the same kernel compiled for aarch64 worked fine. Bisected to the commit in the Fixes line. Additionally a USB device plugged in to the USB3.0 (or 2.0) did not enumerate. Once this patch is applied the USB device enumerates properly. The CRCR register is 64 bits wide - commit abe93f27cdd7 ("xhci: use BIT macro") changed the flag definitions from (1 << n), a signed int, to BIT(n), an unsigned long. Within xhci_set_cmd_ring_deq(), the following operation is performed on the CRCR register: ... crcr &= ~CMD_RING_PTR_MASK; crcr |= deq_dma; crcr &= ~CMD_RING_CYCLE; crcr |= xhci->cmd_ring->cycle_state; ... Previously, ~CMD_RING_CYCLE was ~(int)1, a negative signed value (0xFFFFFFFE with the sign bit set). Widening a negative signed int to u64 sign-extends it to 0xFFFFFFFFFFFFFFFE, correctly clearing only bit 0 and preserving the 64-bit pointer written two lines above. After the change when running on 32 bit kernels, ~CMD_RING_CYCLE is ~(unsigned long)1UL. On a 32-bit host this is an unsigned 32-bit value (0xFFFFFFFE, no sign bit). Widening an unsigned value to u64 zero-extends it instead (0x00000000FFFFFFFE), so the subsequent AND silently clears bits 63:32 of crcr, truncating the command ring pointer that was just written before the value reaches hardware. To fix, similar to how CMD_RING_PTR_MASK is defined, make sure we use the BIT_ULL variant when defining the CRCR bits. [Mathias: use BIT_ULL() for ERST_EHB and EP_CTX_CYCLE_MASK as suggested by Michal Pecio, also include raspberry case in commit message] Fixes: abe93f27cdd7 ("xhci: use BIT macro") Cc: stable Assisted-by: Claude:claude-sonnet-5 cc: Michal Pecio Signed-off-by: Lachlan Hodges Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260804083639.2148950-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 2d3941b5e1e3..708e3ccc5d87 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -190,13 +190,13 @@ struct xhci_op_regs { /* CRCR - Command Ring Control Register - cmd_ring bitmasks */ /* bit 0 - Cycle bit indicates the ownership of the command ring */ -#define CMD_RING_CYCLE BIT(0) +#define CMD_RING_CYCLE BIT_ULL(0) /* stop ring operation after completion of the currently executing command */ -#define CMD_RING_PAUSE BIT(1) +#define CMD_RING_PAUSE BIT_ULL(1) /* stop ring immediately - abort the currently executing command */ -#define CMD_RING_ABORT BIT(2) +#define CMD_RING_ABORT BIT_ULL(2) /* true: command ring is running */ -#define CMD_RING_RUNNING BIT(3) +#define CMD_RING_RUNNING BIT_ULL(3) /* bits 63:6 - Command Ring pointer */ #define CMD_RING_PTR_MASK GENMASK_ULL(63, 6) @@ -271,7 +271,7 @@ struct xhci_intr_reg { * bit 3 - Event Handler Busy (EHB), whether the event ring is scheduled to be serviced by * a work queue (or delayed service routine)? */ -#define ERST_EHB BIT(3) +#define ERST_EHB BIT_ULL(3) /* bits 63:4 - Event Ring Dequeue Pointer */ #define ERST_PTR_MASK GENMASK_ULL(63, 4) @@ -502,7 +502,7 @@ struct xhci_ep_ctx { #define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ -#define EP_CTX_CYCLE_MASK BIT(0) +#define EP_CTX_CYCLE_MASK BIT_ULL(0) /* bits 63:4 - TR Dequeue Pointer */ #define TR_DEQ_PTR_MASK GENMASK_ULL(63, 4) -- cgit v1.2.3 From 1a35da325cac4d5bcad76a2aa943408a6f1d9000 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Wed, 29 Jul 2026 09:16:53 +0000 Subject: packet: synchronize pressure clearing with ring reconfiguration packet_set_ring() updates the RX ring state under sk_receive_queue.lock, but used to publish the tpacket receive mode through po->prot_hook.func after releasing that lock. packet_poll() and packet_recvmsg() can then run the pressure clearing path after the ring has been cleared while still seeing tpacket_rcv, causing __packet_rcv_has_room() to dereference stale or NULL ring storage. Move the existing receive hook assignment into the same sk_receive_queue.lock section as the ring state update. Keep the assignment otherwise unchanged, including on TX ring reconfiguration, to avoid adding behavior changes that are not required for the fix. Serialize packet_recvmsg() pressure clearing with the same queue lock only after PACKET_SOCK_PRESSURE has been observed. If the flag is clear and the socket has moved away from tpacket_rcv, packet_set_ring() has already detached the socket and waited for synchronize_net(), so no new packet input can set the flag again. packet_poll() already holds sk_receive_queue.lock, so it uses the new unlocked helper directly. Fixes: 2ccdbaa6d55b ("packet: rollover lock contention avoidance") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Link: https://patch.msgid.link/f90b5688311fa278d1361ea8c6be0bf25967d591.1785247446.git.zihanx@nebusec.ai Signed-off-by: Paolo Abeni --- net/packet/af_packet.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 5ae0511e89e3..0e1355be89f6 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1315,13 +1315,25 @@ static int packet_rcv_has_room(struct packet_sock *po, struct sk_buff *skb) return ret; } -static void packet_rcv_try_clear_pressure(struct packet_sock *po) +static void __packet_rcv_try_clear_pressure(struct packet_sock *po) { if (packet_sock_flag(po, PACKET_SOCK_PRESSURE) && __packet_rcv_has_room(po, NULL) == ROOM_NORMAL) packet_sock_flag_set(po, PACKET_SOCK_PRESSURE, false); } +static void packet_rcv_try_clear_pressure(struct packet_sock *po) +{ + struct sock *sk = &po->sk; + + if (!packet_sock_flag(po, PACKET_SOCK_PRESSURE)) + return; + + spin_lock_bh(&sk->sk_receive_queue.lock); + __packet_rcv_try_clear_pressure(po); + spin_unlock_bh(&sk->sk_receive_queue.lock); +} + static void packet_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->sk_error_queue); @@ -4305,7 +4317,7 @@ static __poll_t packet_poll(struct file *file, struct socket *sock, TP_STATUS_KERNEL)) mask |= EPOLLIN | EPOLLRDNORM; } - packet_rcv_try_clear_pressure(po); + __packet_rcv_try_clear_pressure(po); spin_unlock_bh(&sk->sk_receive_queue.lock); spin_lock_bh(&sk->sk_write_queue.lock); if (po->tx_ring.pg_vec) { @@ -4545,14 +4557,14 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, rb->frame_max = (req->tp_frame_nr - 1); rb->head = 0; rb->frame_size = req->tp_frame_size; + po->prot_hook.func = (po->rx_ring.pg_vec) ? + tpacket_rcv : packet_rcv; spin_unlock_bh(&rb_queue->lock); swap(rb->pg_vec_order, order); swap(rb->pg_vec_len, req->tp_block_nr); rb->pg_vec_pages = req->tp_block_size/PAGE_SIZE; - po->prot_hook.func = (po->rx_ring.pg_vec) ? - tpacket_rcv : packet_rcv; skb_queue_purge(rb_queue); if (atomic_long_read(&po->mapped)) pr_err("packet_mmap: vma is busy: %ld\n", -- cgit v1.2.3 From cf6f8b29befb92173659bcef6a441d274947bfae Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Thu, 30 Jul 2026 18:20:06 -0400 Subject: net/openvswitch: check Ethernet header length in key_extract() When a packet arrives on an ARPHRD_NONE device (e.g. TUN), ovs_flow_key_extract() trusts the user-provided skb->protocol field: if it is ETH_P_TEB, the packet is classified as MAC_PROTO_ETHERNET and key_extract() is called without ensuring the skb has ETH_HLEN (14) bytes of linear data. key_extract() unconditionally pulls 2 * ETH_ALEN bytes for MAC addresses and parse_ethertype() pulls 2 more, either of which triggers a kernel BUG in __skb_pull() when the linear area is too small. kernel BUG at include/linux/skbuff.h:2848! RIP: 0010:key_extract+0xa7e/0xd90 net/openvswitch/flow.c:933 ovs_flow_key_extract+0x419/0xa70 ovs_vport_receive+0x222/0x390 netdev_frame_hook+0x3e0/0x630 tun_get_user+0x2d0c/0x38e0 Fixed by calling check_header() in key_extract() before accessing the Ethernet header. Fixes: 217ac77a3c25 ("openvswitch: allow L3 netdev ports") Reported-by: AutonomousCodeSecurity@microsoft.com Reviewed-by: Eelco Chaudron Signed-off-by: Cen Zhang (Microsoft) Reviewed-by: Ilya Maximets Link: https://patch.msgid.link/20260730222006.118652-1-blbllhy@gmail.com Signed-off-by: Paolo Abeni --- net/openvswitch/flow.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 66366982f604..46c1d66aad8c 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -889,8 +889,6 @@ static int key_extract_l3l4(struct sk_buff *skb, struct sw_flow_key *key) * Ethernet header * @key: output flow key * - * The caller must ensure that skb->len >= ETH_HLEN. - * * Initializes @skb header fields as follows: * * - skb->mac_header: the L2 header. @@ -910,8 +908,6 @@ static int key_extract_l3l4(struct sk_buff *skb, struct sw_flow_key *key) */ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) { - struct ethhdr *eth; - /* Flags are always used as part of stats */ key->tp.flags = 0; @@ -926,6 +922,13 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) skb_reset_network_header(skb); key->eth.type = skb->protocol; } else { + struct ethhdr *eth; + int err; + + err = check_header(skb, ETH_HLEN); + if (unlikely(err)) + return err; + eth = eth_hdr(skb); ether_addr_copy(key->eth.src, eth->h_source); ether_addr_copy(key->eth.dst, eth->h_dest); -- cgit v1.2.3 From ae31bcc92bb42502bb7c9029e6dc7a824cf6cd14 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 27 Jul 2026 18:38:49 +0800 Subject: mmc: sdhci: make tuning_err a signed int Coverity report INTEGER_OVERFLOW for host->tuning_err. The tuning_err field in struct sdhci_host is used to store an error code for re-tuning, but it was declared as unsigned int. Several call sites store negative error codes into it and later compare against negative values: - sdhci.c, sdhci-of-dwcmshc.c and sdhci-pci-gli.c assign it the return value of __sdhci_execute_tuning()/__sdhci_execute_tuning_9750(), both of which return a signed int (possibly a negative errno); - sdhci-of-esdhc.c assigns host->tuning_err = -EAGAIN and later does "ret = host->tuning_err; if (ret == -EAGAIN ...)"; - sdhci-of-dwcmshc.c prints it with the %d (signed) conversion. Storing a negative errno in an unsigned int and reading it back as a signed int only happens to work because of two's-complement, same-width integer conversions. It is misleading and triggers sign-conversion warnings. All users treat the value either as a signed error code or as a boolean (zero / non-zero), so changing the type to a signed int is safe and makes the intent explicit. Fixes: 7d8bb1f46e13 ("mmc: sdhci: add tuning error codes") Assisted-by: Cline:claude-sonnet [read_file, search_files, git] Signed-off-by: Haibo Chen Reviewed-by: Frank Li Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b6a571d866fa..f0fd81409a86 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -659,7 +659,7 @@ struct sdhci_host { unsigned int tuning_count; /* Timer count for re-tuning */ unsigned int tuning_mode; /* Re-tuning mode supported by host */ - unsigned int tuning_err; /* Error code for re-tuning */ + int tuning_err; /* Error code for re-tuning */ #define SDHCI_TUNING_MODE_1 0 #define SDHCI_TUNING_MODE_2 1 #define SDHCI_TUNING_MODE_3 2 -- cgit v1.2.3 From 9e9f561269dff35e6f84ed21776ec37fd6360b03 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Mon, 27 Jul 2026 23:03:22 +0900 Subject: mmc: sdhci: unmap the bounce buffer before device release sdhci_allocate_bounce_buffer() allocates its buffer with devm_kmalloc() but maps it with dma_map_single(). The buffer is therefore released by devres without the streaming DMA mapping being unmapped. Register a managed action after dma_map_single() succeeds so the mapping is removed before devres releases the buffer. The action is registered only for buffers allocated and mapped by the SDHCI core, leaving buffers provided by host drivers under their existing ownership. Fixes: bd9b902798ab ("mmc: sdhci: Implement an SDHCI-specific bounce buffer") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e3bf901b10aa..efb4c7742fe2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -4187,6 +4187,14 @@ void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver, } EXPORT_SYMBOL_GPL(__sdhci_read_caps); +static void sdhci_unmap_bounce_buffer(void *data) +{ + struct sdhci_host *host = data; + + dma_unmap_single(mmc_dev(host->mmc), host->bounce_addr, + host->bounce_buffer_size, DMA_BIDIRECTIONAL); +} + static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; @@ -4247,6 +4255,14 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) } host->bounce_buffer_size = bounce_size; + ret = devm_add_action_or_reset(mmc_dev(mmc), + sdhci_unmap_bounce_buffer, host); + if (ret) { + devm_kfree(mmc_dev(mmc), host->bounce_buffer); + host->bounce_buffer = NULL; + host->bounce_buffer_size = 0; + return; + } out: /* Lie about this since we're bouncing */ -- cgit v1.2.3 From c125ee35a49a0518521b52b27631eef061b8719a Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Mon, 3 Aug 2026 17:48:21 +0800 Subject: mmc: atmel-mci: Fix use-after-free in atmci_remove due to race condition In atmci_probe, &host->bh_work is bound with atmci_work_func, and atmci_interrupt, atmci_timeout_timer and atmci_dma_complete can all queue this work on system_bh_wq. If we remove the module, atmci_remove makes cleanup and the memory allocated for host with devm_kzalloc() is released after the remove callback returns, while the work mentioned above may still be pending or running. The sequence of operations that may lead to a UAF bug is as follows: CPU0 CPU1 | atmci_interrupt | queue_work(system_bh_wq, | &host->bh_work) atmci_remove | atmci_cleanup_slot(...) | atmci_writel(host, ATMCI_IDR, ~0UL) | timer_delete_sync(&host->timer) | dma_release_channel(host->dma.chan) | free_irq(platform_get_irq(pdev, 0), host) | | atmci_work_func | // use host // devm resources released after | // remove returns, host is freed | | // use host (use-after-free) Fix it by canceling the work after all the sources that can schedule it (IRQ handler, timeout timer and DMA completion callback) have been stopped, and before proceeding with the remaining cleanup in atmci_remove. Fixes: 7d2be0749a59 ("atmel-mci: Driver for Atmel on-chip MMC controllers") Assisted-by: Codex:deepseek-v4-flash Signed-off-by: Pei Xiao Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 3b4928f5b9b2..8f4df250a77a 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2610,6 +2610,8 @@ static void atmci_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), host); + cancel_work_sync(&host->bh_work); + clk_disable_unprepare(host->mck); pm_runtime_disable(dev); -- cgit v1.2.3 From f64ea900e4bda3055ef24a2c906f8d049cf1c3bd Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Tue, 4 Aug 2026 10:25:00 +0800 Subject: mmc: omap_hsmmc: fix busy_timeout overflow in ns conversion on 32-bit omap_hsmmc_prepare_data() converts the command busy timeout to nanoseconds with: timeout = req->cmd->busy_timeout * NSEC_PER_MSEC; busy_timeout is an unsigned int (milliseconds) and timeout is a u64, but NSEC_PER_MSEC is 1000000L. On 32-bit builds the multiplication is performed in 32-bit arithmetic and wraps for busy_timeout values above ~4294 ms, before the result is assigned to the u64. The driver does not set mmc->max_busy_timeout, so the core does not cap the busy timeout, and commands such as erase or SANITIZE (MMC_SANITIZE_TIMEOUT_MS is 240000 ms) can pass a busy_timeout far larger than 4294 ms. The wrapped, much smaller ns value is then programmed via set_data_timeout(), so the data timeout is set too short and the operation can time out prematurely. Cast busy_timeout to u64 before the multiplication so the conversion is done in 64-bit arithmetic. Fixes: 8cc9a3e73de1 ("mmc: host: omap_hsmmc: use generic_cmd6_time to program timeout value for CMD6") Cc: stable@vger.kernel.org Signed-off-by: Zhan Xusheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 58c881f2725b..3356ac5a1fa0 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1357,7 +1357,7 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) if (req->data == NULL) { OMAP_HSMMC_WRITE(host->base, BLK, 0); if (req->cmd->flags & MMC_RSP_BUSY) { - timeout = req->cmd->busy_timeout * NSEC_PER_MSEC; + timeout = (u64)req->cmd->busy_timeout * NSEC_PER_MSEC; /* * Set an arbitrary 100ms data timeout for commands with -- cgit v1.2.3 From 00179ed9fbe07799676e2cb63c4e7f0e7cd80a5c Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 4 Aug 2026 14:12:00 +0800 Subject: mmc: loongson2: Fix sg iteration in data reorder functions In ls2k0500_mmc_reorder_cmd_data() and ls2k2000_mmc_reorder_cmd_data(), the for_each_sg() macro already iterates over the scatterlist entries, with 'sg' pointing to the current entry. However, the code incorrectly uses '&sg[i]' and 'sg_dma_len(&sg[i])' inside the loop, which treats 'sg' as an array base and indexes it again, leading to access of wrong sg entries (or out-of-bounds if the list is not an array). Cc: stable@vger.kernel.org Fixes: d0f8e961deae ("mmc: loongson2: Add Loongson-2K2000 SD/SDIO/eMMC controller driver") Fixes: 2115772014bd ("mmc: loongson2: Add Loongson-2K SD/SDIO controller driver") Signed-off-by: Binbin Zhou Signed-off-by: Ulf Hansson --- drivers/mmc/host/loongson2-mmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c index f553e92fd9e5..118eccbaf8cd 100644 --- a/drivers/mmc/host/loongson2-mmc.c +++ b/drivers/mmc/host/loongson2-mmc.c @@ -641,8 +641,8 @@ static void ls2k0500_mmc_reorder_cmd_data(struct loongson2_mmc_host *host, return; for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { - data = sg_virt(&sg[i]); - for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++) + data = sg_virt(sg); + for (j = 0; j < (sg_dma_len(sg) / 4); j++) if (cmd->opcode == SD_SWITCH) data[j] = bitrev8x4(data[j]); else @@ -758,8 +758,8 @@ static void ls2k2000_mmc_reorder_cmd_data(struct loongson2_mmc_host *host, return; for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { - data = sg_virt(&sg[i]); - for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++) + data = sg_virt(sg); + for (j = 0; j < (sg_dma_len(sg) / 4); j++) data[j] = bitrev8x4(data[j]); } } -- cgit v1.2.3 From 05eebef3c7b2e8595fdd1bd46912aac9c1de8587 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Tue, 4 Aug 2026 01:06:24 -0700 Subject: MAINTAINERS: add SpacemiT K1/K3 I2S entry Add myself as the maintainer for the SpacemiT K1/K3 I2S driver and its devicetree binding. Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260804-spacemit-k1-k3-i2s-maintainers-v1-1-f19ac47e3a7f@linux.spacemit.com Signed-off-by: Mark Brown --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 716acfc3d7c1..17f755d20929 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25430,6 +25430,12 @@ S: Maintained F: Documentation/devicetree/bindings/i2c/spacemit,k1-i2c.yaml F: drivers/i2c/busses/i2c-k1.c +SPACEMIT K1/K3 I2S DRIVER +M: Troy Mitchell +S: Maintained +F: Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml +F: sound/soc/spacemit/k1_i2s.c + SPANISH DOCUMENTATION M: Carlos Bilbao R: Avadhut Naik -- cgit v1.2.3 From 617bbd08714857c1613d7c550d43a9092ec0fb97 Mon Sep 17 00:00:00 2001 From: "Christian A. Ehrhardt" Date: Mon, 22 Jun 2026 23:57:17 +0200 Subject: drm/shmem_helper: Check VMA boundaries for PMD mappings In the ->huge_fault handler do not install a PMD huge page mapping if the huge page exceeds the boundaries of the VMA. All other ->huge_fault handlers have similar checks and the resulting mapping will trigger a VM_BUG_ON_VMA() if it ever reaches copy_pmd_range(). Cc: Pedro Demarchi Gomes Cc: Boris Brezillon Cc: stable@vger.kernel.org Fixes: fc3bbf34e643 ("drm/shmem-helper: Fix huge page mapping in fault handler") Signed-off-by: Christian A. Ehrhardt Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260622215718.1532689-1-lk@c--e.de Signed-off-by: Boris Brezillon --- drivers/gpu/drm/drm_gem_shmem_helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index c989459eb215..00807039f8d7 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -597,9 +597,13 @@ static vm_fault_t try_insert_pfn(struct vm_fault *vmf, unsigned int order, #ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP } else if (order == PMD_ORDER) { unsigned long paddr = pfn << PAGE_SHIFT; + struct vm_area_struct *vma = vmf->vma; + unsigned long start = ALIGN_DOWN(vmf->address, PMD_SIZE); + unsigned long end = start + PMD_SIZE; + bool in_range = vma->vm_start <= start && end <= vma->vm_end; bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK); - if (aligned && + if (aligned && in_range && folio_test_pmd_mappable(page_folio(pfn_to_page(pfn)))) { vm_fault_t ret; -- cgit v1.2.3 From 818d56ac1d08b68a4c42d0326786f030cc640722 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 29 Jul 2026 15:59:07 +0300 Subject: pmdomain: mediatek: Fix mt8183 hang on boot Depending on firmware, part of the MFG domains may be partially left ON at boot time with kernel detecting PD as ON, while it's OFF. Some of MFG cores may be left powered after bootloader, to let the ACP to prefetch the GPU region when the display controller is brought up for a continuous splash animation performed by downstream stack. This doesn't play well with an eventual delay in probing upstream Panfrost driver when the display controller is fully set up, as that would make genpd's sync_state() to power off the domain while ACP tries to prefetch: this is causing an AXI stall, effectively freezing the AP indefinitely. In order to prevent trouble from happening, the sync_state() functionality must be obliterated on all of the MFG domains: while this guarantees a power leakage if the bootloader boots the kernel with MFG PDs partially powered on, this is the only way to ensure stable operation of the SoC during boot on devices with such firmware because, of course, those will never officially receive a firmware update. Fixes Kappa Chromebook hanging during system boot. Fixes: 0e789b491ba0 ("pmdomain: core: Leave powered-on genpds on until sync_state") Fixes: 13a4b7fb6260 ("pmdomain: core: Leave powered-on genpds on until late_initcall_sync") Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Dmitry Osipenko Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mt8183-pm-domains.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/mediatek/mt8183-pm-domains.h b/drivers/pmdomain/mediatek/mt8183-pm-domains.h index 3742782a2702..5e33b8628e85 100644 --- a/drivers/pmdomain/mediatek/mt8183-pm-domains.h +++ b/drivers/pmdomain/mediatek/mt8183-pm-domains.h @@ -47,7 +47,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = 0, .sram_pdn_ack_bits = 0, - .caps = MTK_SCPD_DOMAIN_SUPPLY, + .caps = MTK_SCPD_DOMAIN_SUPPLY | MTK_SCPD_KEEP_DEFAULT_OFF, }, [MT8183_POWER_DOMAIN_MFG] = { .name = "mfg", @@ -57,7 +57,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_DOMAIN_SUPPLY, + .caps = MTK_SCPD_DOMAIN_SUPPLY | MTK_SCPD_KEEP_DEFAULT_OFF, }, [MT8183_POWER_DOMAIN_MFG_CORE0] = { .name = "mfg_core0", @@ -67,6 +67,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, }, [MT8183_POWER_DOMAIN_MFG_CORE1] = { .name = "mfg_core1", @@ -76,6 +77,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, }, [MT8183_POWER_DOMAIN_MFG_2D] = { .name = "mfg_2d", @@ -85,6 +87,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { .pwr_sta2nd_offs = 0x0184, .sram_pdn_bits = GENMASK(8, 8), .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, .bp_cfg = { BUS_PROT_WR(INFRA, MT8183_TOP_AXI_PROT_EN_1_MFG, -- cgit v1.2.3 From bea79063a35121e49a86801671cca56fc4d83f7d Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 2 Aug 2026 22:35:36 +0200 Subject: docs: threat-model: clarify "security bug" vs "vulnerability" Throwing an LLM (Opus 5) at a file looking for random bugs after having read the threat model made it ignore certain bugs it found because "the threat model said they must be ignored". When asked why, the LLM rightfully reported the ambiguous wording used at a few places: "is not a security bug", which can also be read as "is not a bug", despite the rest of the document. That is particularly true when the LLM decides to look for info using grep. This change replaces "security bug" with "vulnerability" at a few places in order to remove this identified ambiguity, and uses "bugs" instead of issues in two such sentences to insist that what is described remains a bug. Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260802203540.3453-2-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/threat-model.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Documentation/process/threat-model.rst b/Documentation/process/threat-model.rst index 9dd8011dde82..7990673072c4 100644 --- a/Documentation/process/threat-model.rst +++ b/Documentation/process/threat-model.rst @@ -98,11 +98,11 @@ measures whose purpose is to avoid crossing a security boundary when certain classes of bugs are found, but a failure of these extra protections do not constitute a vulnerability alone. -What does not constitute a security bug ---------------------------------------- +What classes of problems are not considered vulnerabilities +----------------------------------------------------------- In the Linux kernel's threat model, the following classes of problems are -**NOT** considered as Linux Kernel security bugs. However, when it is believed +**NOT** considered Linux Kernel vulnerabilities. However, when it is believed that the kernel could do better, they should be reported, so that they can be reviewed and fixed where reasonably possible, but they will be handled as any regular bug: @@ -111,8 +111,8 @@ regular bug: * outdated kernels and particularly end-of-life branches are out of the scope of the kernel's threat model: administrators are responsible for keeping - their system up to date. For a bug to qualify as a security bug, it must be - demonstrated that it affects actively maintained versions. + their system up to date. For a bug to qualify as a vulnerability, it must + be demonstrated that it affects actively maintained versions. * build-level: changes to the kernel configuration that are explicitly documented as lowering the security level (e.g. ``CONFIG_NOMMU``), or @@ -178,7 +178,7 @@ regular bug: involving tens of millions of threads, tens of thousands of CPUs, unrealistic CPU frequencies, RAM sizes or disk capacities, network speeds). - * issues whose reproduction requires hardware modification or emulation, + * bugs whose reproduction requires hardware modification or emulation, including fake USB devices that pretend to be another one. * as well as issues that can be triggered at a cost that is orders of @@ -208,17 +208,17 @@ regular bug: messages. * Leaks of kernel memory addresses/pointers do not constitute an immediately - exploitable vector and are not security bugs, though they must be reported - and fixed. + exploitable vector and are not vulnerabilities, though they must be + reported and fixed. * **Crafted file system images**: * bugs triggered by mounting a corrupted or maliciously crafted file system - image are generally not security bugs, as the kernel assumes the underlying + image are generally not vulnerabilities, as the kernel assumes the underlying storage media is under the administrator's control, unless the filesystem driver is specifically documented as being hardened against untrusted media. - * issues that are resolved, mitigated, or detected by running a filesystem + * bugs that are resolved, mitigated, or detected by running a filesystem consistency check (fsck) on the image prior to mounting. * **Physical access**: @@ -232,4 +232,4 @@ regular bug: * **Functional and performance regressions**: Any issue that can be mitigated by setting proper permissions and limits - doesn't qualify as a security bug. + doesn't qualify as a vulnerability. -- cgit v1.2.3 From 11f46e30e6885734be609e337e65a4784bdd282d Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 2 Aug 2026 22:35:37 +0200 Subject: docs: threat-model: move fake devices out of "non production use" When originally writing the "fake USB device" entry, it was difficult to find a suitable section for it and it ended up in "non production use" but that doesn't fit particularly well. Actually it's very similar to crafted filesystems, it's a matter of spec violation. Both drivers and FS are written against a spec, and what the threat model doesn't cover is out-of-spec use. So let's move the entry there and rename the crafted FS entry to "Non-conforming devices and media" instead. Overall it looks more consistent. The spec was tested agains Qwen3.6-27B-Architect-Polaris2-Fable-B-F451, Opus-5 and Gemini by reading the threat-model file, then reading a tens of FS and driver fixes, and they were now all properly classified as regular bugs, except two that Gemini and Opus rightfully classified as vulns (Qwen didn't spot the security potential but that's out of our scope). Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260802203540.3453-3-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/threat-model.rst | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Documentation/process/threat-model.rst b/Documentation/process/threat-model.rst index 7990673072c4..a68be888ce8e 100644 --- a/Documentation/process/threat-model.rst +++ b/Documentation/process/threat-model.rst @@ -178,9 +178,6 @@ regular bug: involving tens of millions of threads, tens of thousands of CPUs, unrealistic CPU frequencies, RAM sizes or disk capacities, network speeds). - * bugs whose reproduction requires hardware modification or emulation, - including fake USB devices that pretend to be another one. - * as well as issues that can be triggered at a cost that is orders of magnitude higher than the expected benefits (e.g. fully functional keyboard emulator only to retrieve 7 uninitialized bytes in a structure, or @@ -211,16 +208,24 @@ regular bug: exploitable vector and are not vulnerabilities, though they must be reported and fixed. -* **Crafted file system images**: +* **Non-conforming devices and media**: - * bugs triggered by mounting a corrupted or maliciously crafted file system - image are generally not vulnerabilities, as the kernel assumes the underlying - storage media is under the administrator's control, unless the filesystem - driver is specifically documented as being hardened against untrusted media. + Drivers are implemented against a specification. When a device or a storage + medium violates the specification its driver was written against, the + resulting misbehaviour is a regular bug to be fixed, not a vulnerability, + unless the driver is specifically documented as being hardened against + hostile inputs. The following are therefore not considered vulnerabilities: - * bugs that are resolved, mitigated, or detected by running a filesystem + * bugs triggered by mounting a corrupted or maliciously crafted file system + image: mounting a block device is a privileged operation (see above), and + the administrator is responsible for the media they mount. This includes + issues that are resolved, mitigated, or detected by running a filesystem consistency check (fsck) on the image prior to mounting. + * bugs whose reproduction requires hardware modification or emulation, + including fake USB devices that pretend to be another one, or devices + reporting values outside their documented ranges. + * **Physical access**: Issues that require physical access to the machine, hardware modification, or -- cgit v1.2.3 From e447f7f33fa0a50d42d0539c161a662e67bd1a2c Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 2 Aug 2026 22:35:38 +0200 Subject: docs: security-bugs: clarify what counts as a valid version Too often we're getting reports saying "still valid in latest mainline" with no indication of when this was verified, making this indication pointless. Let's clarify it and insist on having a version or commit ID, and that the version must necessarily be for a kernel.org kernel and not a distro one. Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260802203540.3453-4-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index 3c51ddde31dd..6f7c21515e9e 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -18,6 +18,10 @@ information is helpful. The following information are absolutely necessary in **any** security bug report: * **affected kernel version range**: with no version indication, your report + will not be processed. Note that a time-dependent version such as "latest + mainline" is not acceptable. A stable identifier such as a commit ID or an + exact version is required. Versions designating kernels not coming from + kernel.org (such as distro kernels) are meaningless to maintainers and will not be processed. A significant part of reports are for bugs that have already been fixed, so it is extremely important that vulnerabilities are verified on recent versions (development tree or latest stable -- cgit v1.2.3 From 3d7c44f73765d98665fb97a4fb89c002c88ba1b9 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 2 Aug 2026 22:35:39 +0200 Subject: docs: coding-assistant: explain important steps when looking for bugs Due to the increasing capabilities of available AI models, it's becoming common to see them used to find bugs anywhere. Unfortunately the quality of reports (especially when they're believed to be security relevant) is still lacking a lot. Let's add a section dedicated to bug finding, explaining the few mandatory steps (noting commit ID, writing the fix from the session that found the bug, building and testing, etc). This was tested both against Qwen3.6-27B-Architect-Polaris2-Fable-B-F451 running under Hermes, and Opus-5, and both followed the instructions to the letter, verifying their results and checking threat-model.rst to decline the vulnerability aspect. At least in the current form it's expected to improve the situation a little bit. Cc: Greg KH Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260802203540.3453-5-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/coding-assistants.rst | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Documentation/process/coding-assistants.rst b/Documentation/process/coding-assistants.rst index 899f4459c52d..e71df7d28467 100644 --- a/Documentation/process/coding-assistants.rst +++ b/Documentation/process/coding-assistants.rst @@ -57,3 +57,40 @@ Basic development tools (git, gcc, make, editors) should not be listed. Example:: Assisted-by: Claude:claude-3-opus coccinelle sparse + +Procedure for finding and fixing bugs +===================================== + +When an AI assistant is used to find and fix bugs, it **MUST** follow at least +these steps: + +1. Before starting, read the whole process documentation listed above, as well + as any other document mentioned in the request. Do not rely on isolated + parts found by keyword search. +2. Note the commit ID and Locate a bug as instructed. +3. For any bug found that is not trivial, verify that it looks real by + attempting to create a reproducer to demonstrate it. Lacking it may cause + the report to be ignored, as many unverified bug reports sent to maintainers + happen to be invalid. Stop here if it finally looks wrong. +4. Write a fix for the bug. This part is not optional: except in a few very + rare cases, an AI assistant able to find a bug is able to fix it. Note that + fixes written in the same session as used to find the bug will generally + lead to better and more accurate fixes as the LLM's reasoning context + remains present. +5. Build and verify that the fix works either using the reproducer or by + re-running a complete analysis; drop any fix that doesn't work and try + another one. The fix must not add build warnings and must pass the + checkpatch.pl checks (see submitting-patches.rst). +6. Commit the working fix with a detailed message describing the problem, the + solution and a Fixes tag. Do not add a Signed-off-by tag, and add an + Assisted-by tag, as described above. +7. Identify the maintainers and lists using scripts/get_maintainer.pl. + Documentation/process/security-bugs.rst shows how to do that. +8. Indicate what could not be done. If the fix could not be built or tested, or + if no reproducer could be produced, say so explicitly: maintainers currently + waste too much time analyzing unverified reports and untested fixes. +9. Read Documentation/process/threat-model.rst to determine whether the bug is + a vulnerability or a regular bug, and leave the result to the reporter for + review (the assistant must never send anything itself). Regular bugs are + submitted as described in Documentation/process/submitting-patches.rst, + vulnerabilities as described in Documentation/process/security-bugs.rst. -- cgit v1.2.3 From 6ffecfe0805e8811e7637e8f381b6740a3e9767b Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 2 Aug 2026 22:35:40 +0200 Subject: docs: security-bugs: clarify some mandatory steps for AI reports The security team is still seeing a lot of reports lacking a full patch and showing missing contents and formatting issues. Since AI assistants tend to be better than humans at following instructions, let's defer to coding-assistants.rst to follow mandatory steps, and insist on the plain text format, as well as asking for recipient addresses and an e-mail client setup hint to be mentioned early in the report for the reporter. Also add a link to https://github.com/masoncl/kres.git which contains way more advanced and detailed steps for those willing to go further. Tested with Opus-5 and Qwen3.6-27B-Architect-Polaris2-Fable-B-F451, both of which proceeded according to instructions. Cc: Greg KH Cc: Chris Mason Signed-off-by: Willy Tarreau Link: https://patch.msgid.link/20260802203540.3453-6-w@1wt.eu Signed-off-by: Greg Kroah-Hartman --- Documentation/process/security-bugs.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst index 6f7c21515e9e..c7dfecc87764 100644 --- a/Documentation/process/security-bugs.rst +++ b/Documentation/process/security-bugs.rst @@ -229,6 +229,28 @@ there is no need to consume a maintainer's time with an unimportant report. If the issue is clearly trivial and publicly discoverable, you should report it directly to the public mailing lists. +At the very least, when using an AI assistant to find and report bugs, make the +assistant read this file and threat-model.rst before proceeding, and have it +follow the procedure described in coding-assistants.rst: work on an up-to-date +mainline tree and note the commit ID, verify the bug is real, write a fix, +build it warning-free and checkpatch-clean, commit it with a Fixes tag, and +identify the maintainers with get_maintainer.pl. + +On top of that procedure, the AI assistant **MUST**: + +1. Prepare a plain-text report explaining the problem. It must contain the + four items listed at the top of this file as absolutely necessary: the + affected version or commit ID noted while following the procedure above, + the description of the problem, the reproducer or its status, and the + triggering conditions. +2. Start the report with a temporary section listing the recipients' addresses + (maintainers+list for the patch, maintainers only for the report and + reproducer), and with instructions reminding the reporter to check that + their email client is properly setup (see email-clients.rst), and leave it + to the reporter to remove that temporary section. + +A more detailed process is covered at https://github.com/masoncl/kres.git. + Sending the report ------------------ -- cgit v1.2.3 From a347304b2ca1a5377d5bd2d8a72e4b4f12afe648 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sat, 1 Aug 2026 08:56:32 -0400 Subject: net/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers Another challenge with unlocked filters. There is a short window in tc_new_tfilter where a tcf_proto can be found and briefly referenced by a totally unrelated, unlocked classifier's request and cause a race. Feng created a poc which created this race with two threads, one creating a u32 filter and other a flower filter in the same chain/prio: 1. Both threads enter tc_new_tfilter, both find the chain empty, both drop filter_chain_lock 2. u32 finishes tcf_proto_create("u32") first, calls tcf_chain_tp_insert_unique() -> inserts u32_tp into the chain 3. flower finishes tcf_proto_create("flower") later, calls tcf_chain_tp_insert_unique() -> tcf_chain_tp_find() now sees u32_tp already there, takes a reference on it, destroys flower's own tp_new and returns u32_tp to the caller. Flower then hits the kind mismatch check (because it requested for kind "flower" but tp->ops->kind is "u32") and goes through the errout path which calls tcf_proto_put() on u32_tp. If the u32 thread has already gone through its own errout (its change() call failed on the PoC's empty options) and dropped its create and insert refs, flower's put is the last one and drops u32_tp's refcnt to zero. At this point tp->ops->destroy() runs in a context that never took rtnl_lock. When that happens, it might cause a UAF like the following (illustrated by the PoC): [ +0.000710] BUG: KASAN: slab-use-after-free in u32_init (net/sched/cls_u32.c:393) [ +0.000281] Read of size 8 at addr ffff888120022f00 by task poc_feng_xue/524 Call Trace: u32_init (net/sched/cls_u32.c:393) tc_new_tfilter (net/sched/cls_api.c:2378) Allocated by task 526: u32_init (net/sched/cls_u32.c:378) tc_new_tfilter (net/sched/cls_api.c:2378) Freed by task 522: kfree u32_destroy (net/sched/cls_u32.c:662) tcf_proto_destroy (net/sched/cls_api.c:446) tcf_proto_put (net/sched/cls_api.c:459) tc_new_tfilter (net/sched/cls_api.c:2459) Fix this by having tcf_proto_destroy() take rtnl_lock around tp->ops->destroy() for locked classifiers whenever rtnl is not held. To explain why I used a temp variable "not_lockless" I'd like to point to a semi-related note on rtnl_held vs TCF_PROTO_OPS_DOIT_UNLOCKED (adding here for future cleanup if deemed necessary): The rtnl_held parameter and the TCF_PROTO_OPS_DOIT_UNLOCKED flag are redundant sources of truth for whether rtnl_lock is held. Among the nine classifier destroy(..rtnl_held..) callbacks, only flower consults the rtnl_held parameter which it propagates to tc_setup_cb_destroy() and tc_setup_cb_call(). The other eight (u32, flow, bpf, cgroup, route, basic, fw, mall) ignore it entirely;-> those that call tc_setup_cb_destroy() (u32, bpf, mall) hardcode true always instead of forwarding the parameter. A future cleanup should remove the rtnl_held parameter from the destroy callback signature entirely and have callers rely solely on their knowledge whether they are running in an unlocked context. Fixes: 12db03b65c2b ("net: sched: extend proto ops to support unlocked classifiers") Reported-by: Feng Xue Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260801125632.360365-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/cls_api.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index fee4524adc98..4e6a2812a4f3 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -443,7 +443,22 @@ static void tcf_chain_put(struct tcf_chain *chain); static void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held, bool sig_destroy, struct netlink_ext_ack *extack) { - tp->ops->destroy(tp, rtnl_held, extack); + /* A locked classifier's destroy callback (e.g. u32_destroy) uses + * rtnl_dereference() and mutates shared structures (e.g. the + * tc_u_common hash list) that are only safe under rtnl_lock. When an + * unlocked classifier's request (e.g. flower on ingress) loses the + * tcf_chain_tp_insert_unique() race and ends up dropping the last + * reference on a locked classifier's proto, destroy() would run + * without rtnl held. Take it here in that case. + */ + bool not_lockless = !rtnl_held && + !(tp->ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED); + + if (not_lockless) + rtnl_lock(); + tp->ops->destroy(tp, rtnl_held || not_lockless, extack); + if (not_lockless) + rtnl_unlock(); tcf_proto_count_usesw(tp, false); if (sig_destroy) tcf_proto_signal_destroyed(tp->chain, tp); -- cgit v1.2.3 From 9c1cc4a7f79275ef93746f6247685763b475bfb0 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 4 Aug 2026 09:57:00 -0400 Subject: selinux: check level category sets once at load time As reported by Jiri Vozar, commit 7edea6e8c8e8 ("selinux: beef up isvalid checks") introduces a new loop in mls_level_isvalid() that causes ~89-94% throughput regression in System V IPC message queue operations (msgsnd/msgrcv). Move the expensive part of the ebitmap checking to policy load time instead as the reporter suggested. Link: https://lore.kernel.org/selinux/CAMgFczCi2Z011dNf84Amc0Q-qnTt0+VUjWY+Y7zPyXdaH35Jvw@mail.gmail.com/ Fixes: 7edea6e8c8e8 ("selinux: beef up isvalid checks") Reported-by: Jiri Vozar Suggested-by: Jiri Vozar Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/mls.c | 24 +++++++----------------- security/selinux/ss/policydb.c | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 3cd36e2015fa..40c62600650e 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -160,9 +160,6 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l) { const char *name; const struct level_datum *levdatum; - struct ebitmap_node *node; - u32 bit; - int rc; if (!l->sens || l->sens > p->p_levels.nprim) return false; @@ -176,21 +173,14 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l) return false; /* - * Validate that all bits set in l->cat are also be set in - * levdatum->level->cat and no bit in l->cat is larger than - * p->p_cats.nprim. + * l is valid iff every bit in l->cat is set in levdatum->level.cat + * and no bit in l->cat is larger than p->p_cats.nprim. + * policydb_index() has already verified that every bit set in + * levdatum->level.cat names a defined category, so containment is + * sufficient here. */ - rc = ebitmap_contains(&levdatum->level.cat, &l->cat, - p->p_cats.nprim); - if (!rc) - return false; - - ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) { - if (!sym_name(p, SYM_CATS, bit)) - return false; - } - - return true; + return ebitmap_contains(&levdatum->level.cat, &l->cat, + p->p_cats.nprim); } bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index d88713201be9..8a32666c0ba2 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -665,6 +665,23 @@ static int cat_index(void *key, void *datum, void *datap) return 0; } +static int sens_cat_index_check(void *key, void *datum, void *datap) +{ + struct policydb *p = datap; + struct level_datum *levdatum = datum; + struct ebitmap_node *node; + u32 bit; + + ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) { + if (bit >= p->p_cats.nprim || !sym_name(p, SYM_CATS, bit)) { + pr_err("SELinux: sensitivity %s allows undefined category %u\n", + (const char *)key, bit + 1); + return -EINVAL; + } + } + return 0; +} + /* clang-format off */ static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = { common_index, @@ -788,6 +805,12 @@ static int policydb_index(struct policydb *p) } } + if (p->mls_enabled) { + rc = hashtab_map(&p->p_levels.table, sens_cat_index_check, p); + if (rc) + goto out; + } + rc = 0; out: return rc; -- cgit v1.2.3 From 620f1e52a46f604635efd0fb78138afd6a513b5d Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Tue, 4 Aug 2026 21:36:11 +0900 Subject: ALSA: FCP: fix OOB write in fcp_meter_ctl_get() fcp_ioctl_set_meter_map() bounds the user-supplied Level Meter map size by the driver's own limit of 255 if (map.map_size < 1 || map.map_size > 255 || map.meter_slots < 1 || map.meter_slots > 255) return -EINVAL; and passes it to fcp_add_new_ctl() as the control's channel count, where it is stored as elem->channels. Every control read writes into struct snd_ctl_elem_value, whose integer array is declared long value[128], so the limit is 128, not 255. fcp_meter_ctl_get() stores one 64-bit word per channel into that array with no bound of its own: for (i = 0; i < elem->channels; i++) { int idx = private->meter_level_map[i]; int value = idx < 0 ? 0 : le32_to_cpu(resp[idx]); ucontrol->value.integer.value[i] = value; } snd_ctl_elem_read_user() serves that object from memdup_user(_control, sizeof(*control)), 1224 bytes on LP64 out of kmalloc-2048. offsetof(struct snd_ctl_elem_value, value) is 72, so element i is written at byte 72 + 8 * i and element 144 already lands past the allocation. At map_size 255 the last store ends at byte 2112, 888 bytes past the object and 64 bytes into the adjacent slab object. The stored words come from the device and meter_level_map[] selects which word lands in which slot, so extent and contents are both controlled. The core does not catch this. snd_ctl_check_elem_info() is reached only from __snd_ctl_elem_info(), which snd_ctl_elem_read() calls under CONFIG_SND_CTL_DEBUG; without that option snd_ctl_skip_validation() is a compile-time true. __snd_ctl_add_replace() validates kcontrol->count and never inspects elem->channels. Installing an oversized map needs CAP_SYS_RAWIO, but the control outlives the hwdep descriptor that created it, so the out-of-bounds stores are issued by any process able to read controls on /dev/snd/controlC0. KASAN on 7.2.0-rc5 (arm64), triggered by an unprivileged control read: BUG: KASAN: slab-out-of-bounds in fcp_meter_ctl_get Write of size 8 at addr ffff000017af04c8 by task fcp_trigger/185 __asan_store8 fcp_meter_ctl_get snd_ctl_elem_read snd_ctl_ioctl Allocated by task 185: memdup_user snd_ctl_ioctl The buggy address is located 0 bytes to the right of allocated 1224-byte region [ffff000017af0000, ffff000017af04c8) Bound the map size by the ABI limit rather than by 255, and bound the store loop at the sink so it cannot run past the value array whatever elem->channels holds. Discovered by XBOW, triaged by Baul Lee Fixes: 46757a3e7d50 ("ALSA: FCP: Add Focusrite Control Protocol driver") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260804123611.91715-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/fcp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index 6f5dcd35e1d4..2bf572c6fdc4 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -129,6 +129,10 @@ struct fcp_data { #define FCP_SEGMENT_APP_GOLD 0 +#define FCP_MAX_METER_MAP_SIZE \ + (sizeof_field(struct snd_ctl_elem_value, value.integer.value) / \ + sizeof(long)) + /* Forward declarations */ static int fcp_init(struct usb_mixer_interface *mixer, void *step0_resp, void *step2_resp); @@ -410,6 +414,9 @@ static int fcp_meter_ctl_get(struct snd_kcontrol *kctl, if (err < 0) return err; + if (WARN_ON_ONCE(elem->channels > FCP_MAX_METER_MAP_SIZE)) + return -EINVAL; + /* copy & translate from resp[] using meter_level_map[] */ for (i = 0; i < elem->channels; i++) { int idx = private->meter_level_map[i]; @@ -636,7 +643,8 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer, } /* Validate the map size */ - if (map.map_size < 1 || map.map_size > 255 || + if (map.map_size < 1 || + map.map_size > FCP_MAX_METER_MAP_SIZE || map.meter_slots < 1 || map.meter_slots > 255) return -EINVAL; -- cgit v1.2.3 From 2615f0fb90df8cf5a96133ca4be74294ed288604 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Tue, 4 Aug 2026 21:36:25 +0900 Subject: ALSA: us144mkii: re-anchor capture URBs on resubmission capture_urb_complete() resubmits each capture URB without anchoring it: usb_get_urb(urb); ret = usb_submit_urb(urb, GFP_ATOMIC); Anchoring is a property of a submission, not of the URB. The giveback path calls usb_unanchor_urb() before urb->complete(), so an URB resubmitted from its own completion handler is off the anchor. The capture URBs are anchored once, at stream start, so from the first completion onward tascam->capture_anchor is empty. tascam_free_urbs(), tascam_disconnect(), tascam_suspend() and the stop-work path all call usb_kill_anchored_urbs(&tascam->capture_anchor) to reap the capture URBs before anything is freed. With the anchor empty those calls return immediately and the URBs stay queued on the host controller. tascam_free_urbs() then returns the capture transfer buffers with usb_free_coherent(), and snd_card_free() releases the snd_card allocation that embeds tascam (card->private_data). The controller completes the queued URBs afterwards, writing device-supplied data into the freed transfer buffer, and capture_urb_complete() dereferences the freed driver object. KASAN on 7.2.0-rc5 (arm64): BUG: KASAN: slab-use-after-free in dummy_timer Write of size 512 at addr ffff000015b62000 __asan_memcpy dummy_timer hrtimer_run_softirq Allocated by task 64: usb_alloc_coherent tascam_alloc_urbs tascam_probe Freed by task 170: usb_free_coherent tascam_free_urbs tascam_disconnect usb_unbind_interface BUG: KASAN: slab-use-after-free in capture_urb_complete Read of size 4 at addr ffff0000170ee878 Freed by task 170: release_card_device snd_card_free tascam_disconnect Restore the usb_anchor_urb() between the reference count bump and the resubmission. That also makes the handler's usb_unanchor_urb() failure arm meaningful again and restores usb_kill_anchored_urbs() as a barrier on the disconnect, suspend and stop-work paths. The anchoring was removed on the premise that the URB is already anchored from the initial submission, which does not hold once the first giveback has run. Discovered by XBOW, triaged by Baul Lee Fixes: 5cff1529a2f9 ("ALSA: us144mkii: capture_urb_complete: redundant usb_anchor_urb corrupts anchor list on each resubmission") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260804123625.91769-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/usx2y/us144mkii_capture.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/usb/usx2y/us144mkii_capture.c b/sound/usb/usx2y/us144mkii_capture.c index fa01da98151a..d7cdf9e0f685 100644 --- a/sound/usb/usx2y/us144mkii_capture.c +++ b/sound/usb/usx2y/us144mkii_capture.c @@ -302,6 +302,7 @@ void capture_urb_complete(struct urb *urb) } usb_get_urb(urb); + usb_anchor_urb(urb, &tascam->capture_anchor); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) { dev_err_ratelimited(tascam->card->dev, -- cgit v1.2.3 From 5ff232d31106f45ac87c3b64e1d35a0667777797 Mon Sep 17 00:00:00 2001 From: Lincoln Wallace Date: Mon, 3 Aug 2026 10:50:21 -0300 Subject: ima: fix out-of-bounds read in xattr_verify() The digest-length check in xattr_verify() mixes int and size_t: if (xattr_len - sizeof(xattr_value->type) - hash_start >= iint->ima_hash->length) sizeof() yields size_t, so the usual arithmetic conversions promote the whole left-hand side to unsigned 64-bit before the subtraction runs. For a truncated xattr this underflows instead of going negative: a 1-byte IMA_XATTR_DIGEST_NG xattr (xattr_len == 1, hash_start == 1) turns "1 - 1 - 1" into SIZE_MAX, which is trivially >= ima_hash->length. The check then passes and the following memcmp() reads iint->ima_hash->length bytes starting past the end of the buffer vfs_getxattr_alloc() allocated for it. Nothing upstream clamps xattr_len back into a safe range first: ima_get_hash_algo() only special-cases xattr_len < 2 to pick a default algorithm, and evm_verifyxattr() returns INTEGRITY_UNKNOWN rather than failing when no HMAC key is loaded, so a truncated security.ima value reaches the length check as-is. Rewrite the comparison so every operand stays a signed int and no implicit conversion to size_t can occur. Fixes: 3ea7a56067e6 ("ima: provide hash algo info in the xattr") Cc: stable@vger.kernel.org Signed-off-by: Lincoln Wallace Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 18d0d9154317..ced2e131b061 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -274,8 +274,13 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint, } else { set_bit(IMA_DIGSIG, &iint->atomic_flags); } - if (xattr_len - sizeof(xattr_value->type) - hash_start >= - iint->ima_hash->length) + /* + * Use addition, not subtraction: sizeof() forces unsigned + * math and a short xattr_len would wrap around, bypassing + * this bounds check. + */ + if (xattr_len >= (int)sizeof(xattr_value->type) + hash_start + + (int)iint->ima_hash->length) /* * xattr length may be longer. md5 hash in previous * version occupied 20 bytes in xattr, instead of 16 -- cgit v1.2.3 From b80bed5c871a80151351342c065579405ce77145 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 27 Jul 2026 20:39:41 -0400 Subject: ima: Instantiate file_truncate and path_truncate hooks Instantiate the file_truncate and path_truncate LSM hooks to reset the action cache flags (IMA_DONE_MASK) as soon as truncation is requested, so the file, based on policy, is re-collected, re-measured, re-audited, and re-appraised on next access. Tested-by: Frederick Lawler Cc: stable@vger.kernel.org Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_main.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 5cea53fc36df..ff52becc3031 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -687,6 +687,43 @@ static int ima_file_check(struct file *file, int mask) MAY_APPEND), FILE_CHECK, 0, false); } +/* + * ima_reset_action_flags - invalidate action flags after a content change + * @inode: inode of the file whose content is about to be truncated + * + * Clear IMA_DONE_MASK so the file is re-collected, re-measured, + * re-audited, and re-appraised on next access. + */ +static void ima_reset_action_flags(struct inode *inode) +{ + struct ima_iint_cache *iint; + + if (!ima_policy_flag || !S_ISREG(inode->i_mode)) + return; + + iint = ima_iint_find(inode); + if (!iint) + return; + + mutex_lock(&iint->mutex); + iint->flags &= ~IMA_DONE_MASK; + iint->measured_pcrs = 0; + mutex_unlock(&iint->mutex); + return; +} + +static int ima_path_truncate(const struct path *path) +{ + ima_reset_action_flags(path->dentry->d_inode); + return 0; +} + +static int ima_file_truncate(struct file *file) +{ + ima_reset_action_flags(file_inode(file)); + return 0; +} + static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf, size_t buf_size) { @@ -1300,11 +1337,13 @@ static struct security_hook_list ima_hooks[] __ro_after_init = { LSM_HOOK_INIT(file_release, ima_file_free), LSM_HOOK_INIT(mmap_file, ima_file_mmap), LSM_HOOK_INIT(file_mprotect, ima_file_mprotect), + LSM_HOOK_INIT(file_truncate, ima_file_truncate), LSM_HOOK_INIT(kernel_load_data, ima_load_data), LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data), LSM_HOOK_INIT(kernel_read_file, ima_read_file), LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file), LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod), + LSM_HOOK_INIT(path_truncate, ima_path_truncate), #ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update), #endif -- cgit v1.2.3 From c5f500161709f27719701334190dff2325868ef0 Mon Sep 17 00:00:00 2001 From: Michał Winiarski Date: Wed, 24 Jun 2026 13:14:20 +0200 Subject: drm/xe: Fix memory leak in exec_queue_set_hang_replay_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The q->replay_state is blindly overwritten, which can potentially leak memory that was previously allocated by vmemdup_user(). Return an error if q->replay_state is not empty. Discovered using AI-assisted static analysis confirmed by Intel Product Security. Reported-by: Martin Hodo Fixes: 1026c1a73a96 ("drm/xe: Implement DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE") Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260624111421.1258364-1-michal.winiarski@intel.com Signed-off-by: Michał Winiarski (cherry picked from commit f6b6cc1118bdbc4265fa8b3bdf8565b26f13e56e) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_exec_queue.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c index 1b5ca3ce578a..663decb19304 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.c +++ b/drivers/gpu/drm/xe/xe_exec_queue.c @@ -805,6 +805,9 @@ static int exec_queue_set_hang_replay_state(struct xe_device *xe, u64 __user *address = u64_to_user_ptr(value); void *ptr; + if (q->replay_state) + return -EINVAL; + ptr = vmemdup_user(address, size); if (XE_IOCTL_DBG(xe, IS_ERR(ptr))) return PTR_ERR(ptr); -- cgit v1.2.3 From d1643db3b037b57f2af7f85c3821d6fe69c492f6 Mon Sep 17 00:00:00 2001 From: Marcin Bernatowicz Date: Thu, 9 Jul 2026 09:59:45 +0200 Subject: drm/xe/uc: Apply RCS/CCS yield policy to SR-IOV VFs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFs were missing the call to apply the global scheduling policy. Call xe_guc_submit_enable() during vf_uc_load_hw() to ensure VFs get the same policy enforcement as PF. Fixes: 26caeae9fb48 ("drm/xe/guc: Set RCS/CCS yield policy") Suggested-by: Michal Wajdeczko Signed-off-by: Marcin Bernatowicz Cc: Daniele Ceraolo Spurio Cc: Michal Wajdeczko Reviewed-by: Daniele Ceraolo Spurio Link: https://patch.msgid.link/20260709075945.1337660-1-marcin.bernatowicz@linux.intel.com Signed-off-by: Michał Winiarski (cherry picked from commit f09360e857130f7ab7f069e2421e6b4a6e502531) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_uc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_uc.c b/drivers/gpu/drm/xe/xe_uc.c index 75091bde0d50..65f59f06177f 100644 --- a/drivers/gpu/drm/xe/xe_uc.c +++ b/drivers/gpu/drm/xe/xe_uc.c @@ -15,6 +15,7 @@ #include "xe_guc_pc.h" #include "xe_guc_rc.h" #include "xe_guc_engine_activity.h" +#include "xe_guc_submit.h" #include "xe_huc.h" #include "xe_sriov.h" #include "xe_wopcm.h" @@ -159,12 +160,14 @@ static int vf_uc_load_hw(struct xe_uc *uc) if (err) return err; - uc->guc.submission_state.enabled = true; - err = xe_guc_opt_in_features_enable(&uc->guc); if (err) return err; + err = xe_guc_submit_enable(&uc->guc); + if (err) + return err; + err = xe_gt_record_default_lrcs(uc_to_gt(uc)); if (err) return err; -- cgit v1.2.3 From d533882ce1060866a590257f2c77ee23eabef5b8 Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 4 Aug 2026 15:48:42 +0800 Subject: hwmon: (nzxt-smart2) Check return value of init_device() in probe The init_device() call in nzxt_smart2_hid_probe() can fail because it sends HID output reports to the hardware to detect fans and set the update interval. If the hardware is not responding or the HID reports fail, init_device() returns a negative error code. However, the return value was ignored, causing the probe to continue and register an hwmon device even though the device was never properly initialized. This leads to an inconsistent state where the driver reports stale data or blocks on wait queues that will never be woken. The same function's return value is already checked in the reset_resume() handler, confirming the author's intent that errors should be propagated. Note that this fix was not possible before commit 59d104b54b0b ("hwmon: (nzxt-smart2) Stop device IO before calling hid_hw_stop") because the out_hw_close error path was missing hid_device_io_stop(), which would have opened a use-after-free risk window. Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") Signed-off-by: Qingshuang Fu Link: https://lore.kernel.org/r/20260804074842.505923-1-fffsqian@163.com Signed-off-by: Guenter Roeck --- drivers/hwmon/nzxt-smart2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index ff0c0bee0e83..efc78a8f58c8 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -754,7 +754,11 @@ static int nzxt_smart2_hid_probe(struct hid_device *hdev, hid_device_io_start(hdev); - init_device(drvdata, UPDATE_INTERVAL_DEFAULT_MS); + ret = init_device(drvdata, UPDATE_INTERVAL_DEFAULT_MS); + if (ret) { + dev_err(&hdev->dev, "init_device failed: %d\n", ret); + goto out_hw_close; + } drvdata->hwmon = hwmon_device_register_with_info(&hdev->dev, "nzxtsmart2", drvdata, -- cgit v1.2.3 From 0e243671bc7b8eaf00f83dd2f4367436dc0cff98 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Mon, 3 Aug 2026 12:27:57 +0000 Subject: ipv6: prevent in6_dev_get() from resurrecting inet6_dev in6_dev_get() reads dev->ip6_ptr under RCU and then unconditionally increments its refcount. Device teardown can clear the pointer and drop the last reference between these operations. The increment then resurrects an object whose RCU free has already been queued, so callers can use it after it is freed. Use refcount_inc_not_zero() and return NULL when the object has already reached zero. RCU keeps the memory accessible through the attempted reference acquisition, and a successful increment pins the object for the caller. An independent run on the exact unpatched 6f5156d7a31a (v7.2-rc3) kernel reproduced the invalid reference acquisition as UID 1000: refcount_t: addition on 0; use-after-free. ip6_mc_source+0xef4/0x17e0 It was followed by the corresponding reference underflow in ip6_mc_source(). The supplied trace from the same unpatched revision additionally shows the access after the RCU read-side section ends: BUG: KASAN: slab-use-after-free in mutex_lock+0x76/0xe0 Write of size 8 at addr ffff888015b50240 by task poc/1219 Bug found and triaged by OpenAI Security Research and validated by Trail of Bits. Fixes: 8814c4b53381 ("[IPV6] ADDRCONF: Convert addrconf_lock to RCU.") Cc: stable@vger.kernel.org Signed-off-by: Kyle Zeng Co-developed-by: David Lee Signed-off-by: David Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260803122758.666112-1-david.lee@trailofbits.com Signed-off-by: Jakub Kicinski --- include/net/addrconf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 8ced27a8229b..e6764245995f 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -405,8 +405,8 @@ static inline struct inet6_dev *in6_dev_get(const struct net_device *dev) rcu_read_lock(); idev = rcu_dereference(dev->ip6_ptr); - if (idev) - refcount_inc(&idev->refcnt); + if (idev && !refcount_inc_not_zero(&idev->refcnt)) + idev = NULL; rcu_read_unlock(); return idev; } -- cgit v1.2.3 From a213dfaa2596c1c0dc4dae91c14fbfa499c03223 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 3 Aug 2026 02:47:40 -0700 Subject: x86/mce: Set up the polling timer before CMCI discovery I hit the following on one of my machines: mce: CPU0 BANK15 CMCI inherited storm ------------[ cut here ]------------ ODEBUG: assert_init not available (active state 0) object: (____ptrval____) object type: timer_list hint: 0x0 WARNING: lib/debugobjects.c:632 at debug_object_assert_init+0x178/0x230, CPU#0: swapper/0/0 CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 7.2.0-rc5 #3 PREEMPTLAZY RIP: 0010:debug_object_assert_init+0x18f/0x230 Call Trace: __mod_timer mce_timer_kick cmci_discover intel_init_cmci mce_intel_feature_init mcheck_cpu_init identify_cpu identify_boot_cpu arch_cpu_finalize_init start_kernel A second splat follows right after, from timer_setup() finding that same timer already queued: ODEBUG: init active (active state 0) object: (____ptrval____) object type: timer_list hint: stub_timer+0x0/0x10 This is happening because CMCI storm detection is trying to modify the timer before latter was properly set up. Set up the timer first. __mcheck_cpu_setup_timer() only calls timer_setup(), and depends on neither the generic nor the vendor init. [ bp: Massage commit message. ] Fixes: 1f68ce2a0272 ("x86/mce: Handle Intel threshold interrupt storms") Signed-off-by: Breno Leitao Signed-off-by: Borislav Petkov (AMD) Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260803-mce_timer_init-v1-1-9539db424330@debian.org --- arch/x86/kernel/cpu/mce/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 9bba1e2f03af..cfb74be19994 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -2266,10 +2266,10 @@ void mcheck_cpu_init(struct cpuinfo_x86 *c) mca_cfg.initialized = 1; + __mcheck_cpu_setup_timer(); __mcheck_cpu_init_generic(); __mcheck_cpu_init_vendor(c); __mcheck_cpu_init_prepare_banks(); - __mcheck_cpu_setup_timer(); cr4_set_bits(X86_CR4_MCE); } -- cgit v1.2.3 From 0dabe8a56f772f0ece46d2597799f412c277d874 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 4 Aug 2026 14:12:31 -0700 Subject: hwmon: (pmbus/lm25066) Fix PMBus coefficient calculations In lm25066_probe(), the PMBus coefficients for current and power are scaled based on the shunt resistor value. The calculation evaluates the multiplication using 32-bit arithmetic because info->m is an int and shunt is a u32: static int lm25066_probe(struct i2c_client *client) { ... info->m[PSC_CURRENT_IN] = info->m[PSC_CURRENT_IN] * shunt / 1000; info->m[PSC_POWER] = info->m[PSC_POWER] * shunt / 1000; ... } For large coefficients like 26882 (LM25056) or 15076 (LM5066i), a device tree shunt-resistor-micro-ohms value exceeding approximately 159,000 (159 mOhm, which is physically valid for low-current applications) causes the intermediate product to exceed UINT_MAX (4,294,967,295). This results in a silent wraparound before the division by 1000. Furthermore, if the wrapped value has the most significant bit set, converting it back to the signed int info->m results in negative coefficients. This logic error leads to drastically corrupted current and power readings, which can cause erratic thermal or power management behavior in the system. Fix the problem by using 64-bit operations for the multiply/divide operations. This can still overflow, but only for unreasonably large shunt resistor values. Reported-by: Sashiko Fixes: 94ee5fcc240fe ("hwmon: (pmbus/lm25066) Support configurable sense resistor values") Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/lm25066.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 2be50cf1bbde..c797e2e8258f 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "pmbus.h" @@ -542,8 +543,8 @@ static int lm25066_probe(struct i2c_client *client) if (of_property_read_u32(client->dev.of_node, "shunt-resistor-micro-ohms", &shunt)) shunt = 1000; - info->m[PSC_CURRENT_IN] = info->m[PSC_CURRENT_IN] * shunt / 1000; - info->m[PSC_POWER] = info->m[PSC_POWER] * shunt / 1000; + info->m[PSC_CURRENT_IN] = DIV_ROUND_CLOSEST_ULL((u64)info->m[PSC_CURRENT_IN] * shunt, 1000); + info->m[PSC_POWER] = DIV_ROUND_CLOSEST_ULL((u64)info->m[PSC_POWER] * shunt, 1000); #if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR) /* LM25056 doesn't support OPERATION */ -- cgit v1.2.3 From 26fa4d17c023dbe5427a92d3a7bd9ae1d1e58bc5 Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Tue, 4 Aug 2026 14:21:12 -0500 Subject: smb: client: fix SMB1 TRANS2 multi-response truncation in SendReceive() When a TRANS2 response is split across multiple secondary packets, coalesce_t2() assembles the payload into the large response buffer. Two bugs cause SendReceive() to copy only a small fraction of the assembled buffer into the caller's output buffer. This manifests when listing a large directory on an SMB1 share (observed against Windows XP); the first getdents returns only partial results, and subsequent getdents returns EINVAL. Bug 1: coalesce_t2() computes the coalesced size as: *pdu_len += total_in_src; cifs_demultiplex_thread() resets *pdu_len to each secondary's own pdu_length before calling coalesce_t2(), so this accumulates from the wrong baseline on every secondary after the first. Bug 2: after reassembly, cifs_demultiplex_thread() sets mid->resp_buf_size to the final secondary's raw packet size. This value is later used as the memcpy length, so only a portion of the coalesced response is copied. Fix both by replacing the stale *pdu_len arithmetic with smbCalcSize(), which reads the BCC field that coalesce_t2() maintains correctly throughout reassembly. Fixes: 83bfbd0bb902 ("cifs: Remove the RFC1002 header from smb_hdr") Cc: stable@vger.kernel.org Signed-off-by: Frank Sorenson Reviewed-by: Paulo Alcantara Signed-off-by: Steve French --- fs/smb/client/smb1transport.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/fs/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c index 53abb29fe71b..966f2cf83a51 100644 --- a/fs/smb/client/smb1transport.c +++ b/fs/smb/client/smb1transport.c @@ -260,9 +260,23 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, goto out; if (out_buf) { - *pbytes_returned = resp_iov.iov_len; - if (resp_iov.iov_len) - memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len); + /* Use smbCalcSize() for both single- and multi-part T2 responses, + * both here and in coalesce_t2(). + */ + unsigned int copy_len; + if (WARN_ON_ONCE(!resp_iov.iov_base)) { + rc = -EIO; + goto out; + } + copy_len = smbCalcSize(resp_iov.iov_base); + if (copy_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { + cifs_dbg(VFS, "response size %u exceeds buffer\n", + copy_len); + rc = -ENOBUFS; + goto out; + } + *pbytes_returned = copy_len; + memcpy(out_buf, resp_iov.iov_base, copy_len); } out: @@ -386,11 +400,13 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len) } put_bcc(byte_count, target_hdr); - byte_count = *pdu_len; - byte_count += total_in_src; + /* use smbCalcSize() rather than *pdu_len: the demux loop resets + * *pdu_len to each secondary's pdu_length, making it unreliable. + */ + byte_count = smbCalcSize(target_hdr); /* don't allow buffer to overflow */ if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { - cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n", + cifs_dbg(FYI, "coalesced size exceeds buffer size (%u)\n", byte_count); return -ENOBUFS; } -- cgit v1.2.3 From 6e3abef2a27e7402a94111c9eff85d887e64a309 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 4 Aug 2026 21:46:35 +0200 Subject: selftests/ftrace: refactor eprobes test to fix argument checks The add/remove eprobe test installs an eprobe for the openat syscall and runs ls. It checks the filenames that were opened by ls against a whitelist and a blacklist. Commit 206b25c09080 ("tracing: eprobe: read the complete FILTER_PTR_STRING pointer") fixed access to some string fields in eprobes. This triggers test failures as the blacklist does not allow relative paths for the openat parameters. What makes this test unstable is the fact that the openat calls vary a lot between different systems. Refactor the test to make it more robust. "cd " will issue a chdir syscall with the target directory as parameter. Set an eprobe on the sys_enter_chdir event and filter for the exact directory name. Allow (fault) as fallback. Link: https://lore.kernel.org/all/20260804194705.760893-1-martin@kaiser.cx/ Fixes: 206b25c09080 ("tracing: eprobe: read the complete FILTER_PTR_STRING pointer") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202607151010.b68428e1-lkp@intel.com Signed-off-by: Martin Kaiser Signed-off-by: Masami Hiramatsu (Google) --- .../ftrace/test.d/dynevent/add_remove_eprobe.tc | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc index c300eb020262..e2322693d0c3 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc @@ -1,16 +1,16 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: Generic dynamic event - add/remove eprobe events -# requires: dynamic_events events/syscalls/sys_enter_openat ". []":README +# requires: dynamic_events events/syscalls/sys_enter_chdir ". []":README echo 0 > events/enable clear_dynamic_events SYSTEM="syscalls" -EVENT="sys_enter_openat" +EVENT="sys_enter_chdir" FIELD="filename" -EPROBE="eprobe_open" +EPROBE="eprobe_chdir" OPTIONS="file=+0(\$filename):ustring" echo "e:$EPROBE $SYSTEM/$EVENT $OPTIONS" >> dynamic_events @@ -18,20 +18,14 @@ grep -q "$EPROBE" dynamic_events test -d events/eprobes/$EPROBE echo 1 > events/eprobes/$EPROBE/enable -ls +cd /sys/kernel/tracing echo 0 > events/eprobes/$EPROBE/enable -content=`grep '^ *ls-' trace | grep 'file='` -nocontent=`grep '^ *ls-' trace | grep 'file=' | grep -v -e '"/' -e '"."' -e '(fault)' ` || true - +content=`grep -e 'file="/sys/kernel/tracing"\|(fault)' trace` if [ -z "$content" ]; then exit_fail fi -if [ ! -z "$nocontent" ]; then - exit_fail -fi - echo "-:$EPROBE" >> dynamic_events ! grep -q "$EPROBE" dynamic_events -- cgit v1.2.3 From 06232cb44bc8e81adc2f1d40a01bed830b607ea2 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 3 Aug 2026 11:51:56 +0200 Subject: net: stmmac: resume PHY before hardware setup when opening the interface Since the referenced commit, changing the MTU on a running interface no longer disconnects and reconnects the PHY; __stmmac_release() merely stops phylink, which also suspends the PHY (BMCR power-down) when WoL is not enabled. __stmmac_open() then performs the DMA software reset in stmmac_hw_setup() before phylink_start() resumes the PHY again. IEEE 802.3 22.2.4.1.5 allows a PHY to stop its receive clock while powered down, and stmmac requires a running receive clock for the DMA software reset to complete (the phylink config sets mac_requires_rxc). On such setups, e.g. the RK3566-based Home Assistant Green with an RTL8211F-VD PHY in RGMII mode, any runtime MTU change now times out and leaves the interface dead: rk_gmac-dwmac fe010000.ethernet end0: Failed to reset the dma rk_gmac-dwmac fe010000.ethernet end0: stmmac_hw_setup: DMA engine initialization failed rk_gmac-dwmac fe010000.ethernet end0: __stmmac_open: Hw setup failed rk_gmac-dwmac fe010000.ethernet end0: failed reopening the interface after MTU change In the field this is triggered by NetworkManager applying an MTU while activating the connection, breaking networking entirely. The same regression has also been reported on i.MX8MP and reproduced on SoCFPGA based systems. Resume the PHY in __stmmac_open() before the hardware setup, making it the counterpart of the phylink_stop() in __stmmac_release(), like stmmac_resume() already does for the same reason. phylink_start() also resumes the PHY, but only after stmmac_hw_setup(), and it cannot be moved before the hardware setup since it may bring the link up immediately from a workqueue, racing with the initialization (see the comment in stmmac_resume()). For the regular ndo_open path the PHY has just been attached and is not suspended, in which case phylink_prepare_resume() does nothing. Fixes: db299a0c09e9 ("net: stmmac: move PHY handling out of __stmmac_open()/release()") Link: https://github.com/home-assistant/operating-system/issues/4858 Tested-by: Alexander Stein Signed-off-by: Stefan Agner Tested-by: Maxime Chevallier Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260803095156.132827-1-stefan@agner.ch Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 97b28cfa14c2..a71f0df26378 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -4134,6 +4134,15 @@ static int __stmmac_open(struct net_device *dev, dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs; memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); + /* The PHY is suspended when the interface is reopened without + * disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs + * to stop their receive clock while powered down, but the DMA + * software reset in stmmac_hw_setup() requires a running receive + * clock, and phylink_start() below resumes the PHY only after the + * hardware setup. Resume a suspended PHY here first. + */ + phylink_prepare_resume(priv->phylink); + stmmac_reset_queues_param(priv); ret = stmmac_hw_setup(dev); -- cgit v1.2.3 From 452636ea5410a96e02ebaaf80b21e3620b98e0dd Mon Sep 17 00:00:00 2001 From: Yangyu Chen Date: Sun, 2 Aug 2026 23:46:00 +0800 Subject: net: atlantic: free stranded TX buffers on ring deinit aq_vec_deinit() drains the TX rings with a single aq_ring_tx_clean() call, which frees at most AQ_CFG_TX_CLEAN_BUDGET (256) descriptors and stops at hw_head, which no longer moves once aq_vec_stop() has stopped the hardware and NAPI. Completed descriptors beyond the budget and everything still posted in [hw_head, sw_tail) keep their skb or xdp_frame when the interface goes down: aq_vec_ring_free() then frees the buffer ring and the references are lost for good. Today this is a silent memory leak on every interface down under TX/XDP_TX load. With the conversion of the RX path to page_pool posted for net-next it becomes much more visible: XDP_TX frames carry fragment references on the RX ring's page_pool, so a single stranded frame keeps the pool's inflight count above zero forever. page_pool_destroy() then never completes, the pool is leaked together with its pages, and "page_pool_release_retry() stalled pool shutdown" is warned every 60 seconds from that point on, on every ifdown, XDP detach or ring resize under XDP_TX load. Bring back aq_ring_tx_deinit() as it was before the removal and use it for teardown again, with one extension: TX rings can hold xdp_frames nowadays, so release those too. They are returned with xdp_return_frame() since this runs in process context. Fixes: eb36bedf28be ("net: aquantia: remove function aq_ring_tx_deinit") Cc: stable@vger.kernel.org # v4.11+ Reviewed-by: Sukhdeep Singh Signed-off-by: Yangyu Chen Acked-by: Mina Almasry Link: https://patch.msgid.link/tencent_EEDC35FAF2750A3A6A0B39BAE0E2C484860A@qq.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 29 ++++++++++++++++++++++++ drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 1 + drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 8ff07de2bd52..81685a4dc5a6 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -360,6 +360,35 @@ out: return !!budget; } +void aq_ring_tx_deinit(struct aq_ring_s *self) +{ + if (!self) + return; + + for (; self->sw_head != self->sw_tail; + self->sw_head = aq_ring_next_dx(self, self->sw_head)) { + struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; + struct device *ndev = aq_nic_get_dev(self->aq_nic); + + if (buff->is_mapped) { + if (buff->is_sop) { + dma_unmap_single(ndev, buff->pa, buff->len, + DMA_TO_DEVICE); + } else { + dma_unmap_page(ndev, buff->pa, buff->len, + DMA_TO_DEVICE); + } + } + + if (buff->is_eop) { + if (buff->skb) + dev_kfree_skb_any(buff->skb); + else if (buff->xdpf) + xdp_return_frame(buff->xdpf); + } + } +} + static void aq_rx_checksum(struct aq_ring_s *self, struct aq_ring_buff_s *buff, struct sk_buff *skb) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index a70b880ada67..6431cc62962f 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h @@ -202,6 +202,7 @@ void aq_ring_update_queue_state(struct aq_ring_s *ring); void aq_ring_queue_wake(struct aq_ring_s *ring); void aq_ring_queue_stop(struct aq_ring_s *ring); bool aq_ring_tx_clean(struct aq_ring_s *self); +void aq_ring_tx_deinit(struct aq_ring_s *self); int aq_xdp_xmit(struct net_device *dev, int num_frames, struct xdp_frame **frames, u32 flags); int aq_ring_rx_clean(struct aq_ring_s *self, diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index 2f9033ceed8c..05814fea0f5f 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c @@ -275,7 +275,7 @@ void aq_vec_deinit(struct aq_vec_s *self) for (i = 0U; self->tx_rings > i; ++i) { ring = self->ring[i]; - aq_ring_tx_clean(&ring[AQ_VEC_TX_ID]); + aq_ring_tx_deinit(&ring[AQ_VEC_TX_ID]); aq_ring_rx_deinit(&ring[AQ_VEC_RX_ID]); } -- cgit v1.2.3 From e8e7471ef686b6c002218fee9671cc61992ae01a Mon Sep 17 00:00:00 2001 From: Yangyu Chen Date: Sun, 2 Aug 2026 23:46:38 +0800 Subject: net: atlantic: free RX pages of consumed but not refilled buffers aq_ring_rx_deinit() only walks [sw_head, sw_tail), the region posted to hardware. Since the page reuse strategy was added, a cleaned RX buffer keeps its page (and its DMA mapping) in the ring for reuse, and refill is batched: aq_ring_rx_fill() returns early until AQ_CFG_RX_REFILL_THRES slots are free. Slots that were consumed but not yet reposted therefore sit in the complementary [sw_tail, sw_head) gap with a live page, and the deinit walk never visits them: up to a refill batch worth of pages and DMA mappings leak on every interface down. Walk the whole ring instead and release whatever is still there. Also bail out if the buffer ring is already gone: a partial aq_ptp_ring_alloc() failure frees the ring but leaves aq_nic set, so aq_ptp_ring_deinit() still gets here on the unwind path. Cc: stable@vger.kernel.org # v5.2+ Fixes: 46f4c29d9de6 ("net: aquantia: optimize rx performance by page reuse strategy") Reviewed-by: Sukhdeep Singh Signed-off-by: Yangyu Chen Acked-by: Mina Almasry Link: https://patch.msgid.link/tencent_607CBA8237DA438E36B844318B21538DE008@qq.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 81685a4dc5a6..e1193c6719d9 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -950,15 +950,29 @@ err_exit: void aq_ring_rx_deinit(struct aq_ring_s *self) { - if (!self) + unsigned int i; + + if (!self || !self->buff_ring) return; - for (; self->sw_head != self->sw_tail; - self->sw_head = aq_ring_next_dx(self, self->sw_head)) { - struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; + /* Release every page still owned by the ring. + * + * Walking [sw_head, sw_tail) is not enough: refill is batched + * (aq_ring_rx_fill() waits for AQ_CFG_RX_REFILL_THRES free slots), + * so slots that were cleaned but not yet reposted accumulate in the + * [sw_tail, sw_head) gap, and they keep their page for reuse. Walk + * the whole ring and release whatever is left. + */ + for (i = 0; i < self->size; i++) { + struct aq_ring_buff_s *buff = &self->buff_ring[i]; + + if (!buff->rxdata.page) + continue; aq_free_rxpage(&buff->rxdata, aq_nic_get_dev(self->aq_nic)); } + + self->sw_head = self->sw_tail; } void aq_ring_free(struct aq_ring_s *self) -- cgit v1.2.3 From 2cbd8a4e5e09aa232a1f8d56ce3d070b18ab2b10 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Sat, 1 Aug 2026 03:09:20 -0700 Subject: bnge: use int for bnge_fix_rings_count() return value bnge_fix_rings_count() returns 0 on success or a negative errno on failure However, bnge_adjust_rings() stores its return value in a u16 variable, causing negative error codes such as -ENOMEM to be converted to a large positive value. Use an int for the return code variable so that error values are preserved and propagated correctly. Fixes: 627c67f038d2 ("bng_en: Add resource management support") Signed-off-by: Alok Tiwari Reviewed-by: Bhargava Marreddy Link: https://patch.msgid.link/20260801100923.1498570-1-alok.a.tiwari@oracle.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_resc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c index 0e94f092813e..4711dd4945ff 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c @@ -163,7 +163,8 @@ static int bnge_adjust_rings(struct bnge_dev *bd, u16 *rx, u16 tx_chunks = bnge_num_tx_to_cp(bd, *tx); if (tx_chunks != *tx) { - u16 tx_saved = tx_chunks, rc; + u16 tx_saved = tx_chunks; + int rc; rc = bnge_fix_rings_count(rx, &tx_chunks, max_nq, sh); if (rc) -- cgit v1.2.3 From 1505b2cb6ae1c7e8ac0c6e4590a204ffc3ab2b24 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 31 Jul 2026 11:27:11 +0200 Subject: usbnet: cap max_mtu for drivers without bind callback usbnet_probe() initializes max_mtu to ETH_MAX_MTU and only caps it inside the if (info->bind) block. Drivers without a bind callback never enter this block, so max_mtu stays at ETH_MAX_MTU. QEMU's usb-net device (0x0525/0xa4a2) is claimed by the cdc_subset driver which has no bind callback. The guest accepts any MTU from DHCP (e.g. 65520 from passt), leading to TCP segments that exceed the device's 2048-byte receive buffer and are silently dropped. Initialize max_mtu to net->mtu at probe time and update it inside the bind block. Fixes: f77f0aee4da4 ("net: use core MTU range checking in USB NIC drivers") Cc: jarod@redhat.com Cc: stable@vger.kernel.org Link: https://gitlab.com/qemu-project/qemu/-/issues/3268 Link: https://bugs.passt.top/show_bug.cgi?id=189 Signed-off-by: Laurent Vivier Link: https://patch.msgid.link/20260731092711.857684-1-lvivier@redhat.com Signed-off-by: Jakub Kicinski --- drivers/net/usb/usbnet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 25518635b7b7..a19ecf718f36 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1794,7 +1794,7 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod) */ dev->hard_mtu = net->mtu + net->hard_header_len; net->min_mtu = 0; - net->max_mtu = ETH_MAX_MTU; + net->max_mtu = net->mtu; net->netdev_ops = &usbnet_netdev_ops; net->watchdog_timeo = TX_TIMEOUT_JIFFIES; @@ -1804,6 +1804,7 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod) // allow device-specific bind/init procedures // NOTE net->name still not usable ... if (info->bind) { + net->max_mtu = ETH_MAX_MTU; status = info->bind(dev, udev); if (status < 0) goto out1; -- cgit v1.2.3 From 5d1c224dd914579524a183a514c12b95095d12ce Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Mon, 3 Aug 2026 08:29:36 +0800 Subject: net: openvswitch: reallocate update replies for mismatched IDs ovs_flow_cmd_new() preallocates the optional reply skb before it takes ovs_mutex and before it knows which existing flow will be updated. That is normally fine because the skb is sized from the request flow identifier. That identifier also becomes the inserted flow's identifier. For updates, however, a request with a UFID may miss the UFID lookup and then fall back to the flow key lookup. That lookup can legitimately find an existing key-identified flow. UFIDs are optional and the flow key is the primary identifier. For echoed replies, ovs_flow_cmd_fill_info() writes the matched flow's identifier, not the request identifier used for the preallocation. A short request UFID can therefore leave too little room for the key identifier. The fill can then fail with -EMSGSIZE and hit the BUG_ON(error < 0) in the update path. Once the update target has been resolved, reallocate the reply skb if the matched flow needs a larger reply than the request identifier allowed. Do this before replacing the actions so the request can still fail cleanly if the rare extra allocation fails. Fixes: 74ed7ab9264c ("openvswitch: Add support for unique flow IDs.") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Reviewed-by: Ilya Maximets Link: https://patch.msgid.link/f7bbd3c30ce81a39156e226b3872d73abed21d2f.1785644623.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/openvswitch/datapath.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index eaf332b156d7..ae69b2cabab9 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1113,9 +1113,8 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) error = -EEXIST; goto err_unlock_ovs; } - /* The flow identifier has to be the same for flow updates. - * Look for any overlapping flow. - */ + + /* Look for any overlapping flow. */ if (unlikely(!ovs_flow_cmp(flow, &match))) { if (ovs_identifier_is_key(&flow->id)) flow = ovs_flow_tbl_lookup_exact(&dp->table, @@ -1127,6 +1126,30 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) goto err_unlock_ovs; } } + + if (unlikely(reply)) { + size_t cur, req; + + cur = ovs_flow_cmd_msg_size(acts, &new_flow->id, + ufid_flags); + req = ovs_flow_cmd_msg_size(acts, &flow->id, + ufid_flags); + if (cur < req) { + struct sk_buff *resized; + + resized = ovs_flow_cmd_alloc_info(acts, + &flow->id, + info, false, + ufid_flags); + if (IS_ERR(resized)) { + error = PTR_ERR(resized); + goto err_unlock_ovs; + } + kfree_skb(reply); + reply = resized; + } + } + /* Update actions. */ old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); -- cgit v1.2.3 From e7386770be1bf810bcd6af39d1e4bfeab3408430 Mon Sep 17 00:00:00 2001 From: Bobby Eshleman Date: Mon, 3 Aug 2026 16:47:29 -0700 Subject: net/mlx5e: fix BQL reset on SQ re-activation mlx5e_queue_start() deactivates and re-activates all channels but closes only the queue being restarted. mlx5e_activate_txqsq() then unconditionally calls netdev_tx_reset_queue(), zeroing the BQL counters of channels that kept their in-flight TX WQEs. The next completion then over-charges and trips the BUG_ON() in dql_completed(): kernel BUG at lib/dynamic_queue_limits.c:99! RIP: 0010:dql_completed+0x23d/0x280 Call Trace: mlx5e_poll_tx_cq+0x668/0xa60 mlx5e_napi_poll+0x5b/0x7b0 net_rx_action+0x15a/0x580 Reset BQL only when the SQ has no bytes in flight (sq->cc == sq->pc). In the case that reset is skipped, the outstanding WQEs will eventually complete and rebalance the dql. The dql->limit is carried across the reset. Fixes: b2588ea40ec9 ("net/mlx5e: Implement queue mgmt ops and single channel swap") Signed-off-by: Bobby Eshleman Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260803-mlx5-bql-v3-1-a30d4c66fe1d@meta.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 7d47a1da8b6b..f0407a850ea8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1939,8 +1939,10 @@ err_free_txqsq: void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq) { sq->txq = netdev_get_tx_queue(sq->netdev, sq->txq_ix); + /* Reset BQL only when the SQ has no bytes in flight. */ + if (sq->cc == sq->pc) + netdev_tx_reset_queue(sq->txq); set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - netdev_tx_reset_queue(sq->txq); netif_tx_start_queue(sq->txq); netif_queue_set_napi(sq->netdev, sq->txq_ix, NETDEV_QUEUE_TYPE_TX, sq->cq.napi); } -- cgit v1.2.3 From 1d0fc6c7ea49994b8ff50d02979d1e4207ec6c4f Mon Sep 17 00:00:00 2001 From: Shravya KN Date: Fri, 31 Jul 2026 12:09:33 -0700 Subject: bnxt_en: Move RSS table fill outside __bnxt_hwrm_vnic_set_rss() This is a refactor patch with no change in behavior. The caller will now fill the RSS table before calling __bnxt_hwrm_vnic_set_rss(). In the next patch, we'll add code to determine the default ring for the VNIC when we fill the RSS table. Reviewed-by: Kalesh AP Reviewed-by: Pavan Chebbi Signed-off-by: Shravya KN Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-2-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 004ef99f4530..4599b6e0d219 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6706,6 +6706,9 @@ static void bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic) bool no_rss = !(vnic->flags & BNXT_VNIC_RSS_FLAG); u16 i, j; + if (!vnic->rss_table) + return; + /* Fill the RSS indirection table with ring group ids */ for (i = 0, j = 0; i < HW_HASH_INDEX_SIZE; i++) { if (!no_rss) @@ -6745,13 +6748,8 @@ static void __bnxt_hwrm_vnic_set_rss(struct bnxt *bp, struct hwrm_vnic_rss_cfg_input *req, struct bnxt_vnic_info *vnic) { - if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { - bnxt_fill_hw_rss_tbl_p5(bp, vnic); - if (bp->flags & BNXT_FLAG_CHIP_P7) - req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT; - } else { - bnxt_fill_hw_rss_tbl(bp, vnic); - } + if (bp->flags & BNXT_FLAG_CHIP_P7) + req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT; if (bp->rss_hash_delta) { req->hash_type = cpu_to_le32(bp->rss_hash_delta); @@ -6803,6 +6801,7 @@ static int bnxt_hwrm_vnic_set_rss_p5(struct bnxt *bp, if (!set_rss) return hwrm_req_send(bp, req); + bnxt_fill_hw_rss_tbl_p5(bp, vnic); __bnxt_hwrm_vnic_set_rss(bp, req, vnic); ring_tbl_map = vnic->rss_table_dma_addr; nr_ctxs = bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings); @@ -10866,6 +10865,7 @@ static int __bnxt_setup_vnic(struct bnxt *bp, struct bnxt_vnic_info *vnic) } skip_rss_ctx: + bnxt_fill_hw_rss_tbl(bp, vnic); /* configure default vnic, ring grp */ rc = bnxt_hwrm_vnic_cfg(bp, vnic); if (rc) { -- cgit v1.2.3 From 53f01cd594e223aabb538d5288e60111523c96f2 Mon Sep 17 00:00:00 2001 From: Shravya KN Date: Fri, 31 Jul 2026 12:09:34 -0700 Subject: bnxt_en: Determine and store default RX ring in vnic structure Each VNIC has a default RX ring. The purpose of the default RX ring is to provide a destination for any packets that cannot be parsed by the RSS logic. Up until now, the default RX ring is always Ring 0. We neglected to take care of this default RX ring when adding the queue restart feature. If ring 0 (default ring) is re-started, it may now have a new FW ring ID after freeing the old one and allocating a new one. The VNIC now may have a stale default ring and it may generate an internal exception. This exception may appear in dmesg: FW reported unknown error type 10 The best way to resolve this issue is to use a more appropriate ring for the default ring instead of always ring 0. Ring 0 may not even be in the RSS table, especially on a new RSS context. This patch adds the logic to determine and store the proper default RX ring for a VNIC. For an RSS VNIC, the default ring is the lowest ring number in the RSS table. The next patch will add proper logic to update the VNIC if the default ring changes after queue restart. Reviewed-by: Kalesh AP Reviewed-by: Pavan Chebbi Signed-off-by: Shravya KN Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-3-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 33 ++++++++++++++++++++----------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4599b6e0d219..8b1f4e9fc780 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6704,25 +6704,36 @@ int bnxt_get_nr_rss_ctxs(struct bnxt *bp, int rx_rings) static void bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic) { bool no_rss = !(vnic->flags & BNXT_VNIC_RSS_FLAG); - u16 i, j; + u16 i, j, min_j = bp->rx_nr_rings - 1; if (!vnic->rss_table) - return; + goto skip_rss_tbl; /* Fill the RSS indirection table with ring group ids */ for (i = 0, j = 0; i < HW_HASH_INDEX_SIZE; i++) { if (!no_rss) j = bp->rss_indir_tbl[i]; + min_j = min(j, min_j); vnic->rss_table[i] = cpu_to_le16(vnic->fw_grp_ids[j]); } + +skip_rss_tbl: + if (vnic->rss_table && !no_rss) + vnic->default_rx_ring = min_j; + else if (vnic->flags & BNXT_VNIC_RFS_FLAG) + vnic->default_rx_ring = vnic->vnic_id - 1; + else if ((vnic->vnic_id == 1) && BNXT_CHIP_TYPE_NITRO_A0(bp)) + vnic->default_rx_ring = bp->rx_nr_rings - 1; + else + vnic->default_rx_ring = 0; } static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, struct bnxt_vnic_info *vnic) { + u16 tbl_size, i, min_j = bp->rx_nr_rings - 1; __le16 *ring_tbl = vnic->rss_table; struct bnxt_rx_ring_info *rxr; - u16 tbl_size, i; tbl_size = bnxt_get_rxfh_indir_size(bp->dev); @@ -6735,6 +6746,7 @@ static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, j = ethtool_rxfh_context_indir(vnic->rss_ctx)[i]; else j = bp->rss_indir_tbl[i]; + min_j = min(j, min_j); rxr = &bp->rx_ring[j]; ring_id = rxr->rx_ring_struct.fw_ring_id; @@ -6742,6 +6754,7 @@ static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, ring_id = bnxt_cp_ring_for_rx(bp, rxr); *ring_tbl++ = cpu_to_le16(ring_id); } + vnic->default_rx_ring = min_j; } static void @@ -6938,8 +6951,9 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, struct bnxt_vnic_info *vnic) return rc; if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { - struct bnxt_rx_ring_info *rxr = &bp->rx_ring[0]; + struct bnxt_rx_ring_info *rxr; + rxr = &bp->rx_ring[vnic->default_rx_ring]; req->default_rx_ring_id = cpu_to_le16(rxr->rx_ring_struct.fw_ring_id); req->default_cmpl_ring_id = @@ -6972,13 +6986,7 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, struct bnxt_vnic_info *vnic) req->cos_rule = cpu_to_le16(0xffff); } - if (vnic->flags & BNXT_VNIC_RSS_FLAG) - ring = 0; - else if (vnic->flags & BNXT_VNIC_RFS_FLAG) - ring = vnic->vnic_id - 1; - else if ((vnic->vnic_id == 1) && BNXT_CHIP_TYPE_NITRO_A0(bp)) - ring = bp->rx_nr_rings - 1; - + ring = vnic->default_rx_ring; grp_idx = bp->rx_ring[ring].bnapi->index; req->dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id); req->lb_rule = cpu_to_le16(0xffff); @@ -11171,6 +11179,9 @@ static int bnxt_setup_nitroa0_vnic(struct bnxt *bp) return rc; } + /* Setup the proper default RX ring */ + bnxt_fill_hw_rss_tbl(bp, vnic); + rc = bnxt_hwrm_vnic_cfg(bp, vnic); if (rc) { netdev_err(bp->dev, "Cannot allocate special vnic for NS2 A0: %x\n", diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 6335dfc14c98..44ea8fc7b766 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1334,6 +1334,7 @@ struct bnxt_vnic_info { #define BNXT_VNIC_RSSCTX_FLAG 0x40 struct ethtool_rxfh_context *rss_ctx; u32 vnic_id; + u16 default_rx_ring; }; struct bnxt_rss_ctx { -- cgit v1.2.3 From 0b137529a8997caf67190ca1d71ba8bbdb44fbfb Mon Sep 17 00:00:00 2001 From: Shravya KN Date: Fri, 31 Jul 2026 12:09:35 -0700 Subject: bnxt_en: Refresh VNIC default ring on queue restart if needed When a queue is restarted, refresh VNIC_CFG for all VNICs whose default RX ring is the restarted ring. This will eliminate this possible FW warning caused by a stale default ring in the VNIC: FW reported unknown error type 10 Fixes: 5ac066b7b062 ("bnxt_en: Fix queue start to update vnic RSS table") Reviewed-by: Kalesh AP Reviewed-by: Pavan Chebbi Signed-off-by: Shravya KN Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-4-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 8b1f4e9fc780..6deb8f348d07 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11098,6 +11098,11 @@ static int bnxt_set_vnic_mru_p5(struct bnxt *bp, struct bnxt_vnic_info *vnic, vnic->vnic_id, rc); return rc; } + if (rxr_id == vnic->default_rx_ring) { + rc = bnxt_hwrm_vnic_cfg(bp, vnic); + if (rc) + return rc; + } } vnic->mru = mru; bnxt_hwrm_vnic_update(bp, vnic, -- cgit v1.2.3 From c3faf548a00f4c17100cc9204746975fa46a73b9 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 31 Jul 2026 12:09:36 -0700 Subject: bnxt_en: Disable EOP for TPA on all chips to prevent data corruption EOP (End of frame padding) on the AGG ring may cause overlapping of zero padding at the end of one segment with the next segment's data. If Relaxed Ordering (RO) is enabled, the zero padding may overwrite valid data in the next segment and corrupt the data. Older chips (P5 and older) do not automatically disable RO when EOP is enabled. On some ARM systems, data corruption was reported on 57508 (P5) chips with RO enabled. Always disable EOP on all chips on the AGG rings when TPA is enabled to fix the data corruption. Fixes: bfcd8d791ec1 ("bnxt_en: Add fast path logic for TPA on 57500 chips.") Reviewed-by: Pavan Chebbi Reviewed-by: Kalesh AP Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-5-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 6deb8f348d07..25099077fe4f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4611,11 +4611,14 @@ static void bnxt_init_one_rx_agg_ring_rxbd(struct bnxt *bp, type = ((u32)rxr->rx_page_size << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_AGG_BD; - /* On P7, setting EOP will cause the chip to disable - * Relaxed Ordering (RO) for TPA data. Disable EOP for - * potentially higher performance with RO. + /* Disable EOP if TPA is enabled to prevent overlapping zero + * padding with the next segment's data. On P7_PLUS, EOP will + * automatically disable Relaxed Ordering (RO) to prevent + * potential data corruption (and may degrade performance). On + * older chips, RO will not be automatically disabled and may + * cause corruption. */ - if (BNXT_CHIP_P5_AND_MINUS(bp) || !(bp->flags & BNXT_FLAG_TPA)) + if (!(bp->flags & BNXT_FLAG_TPA)) type |= RX_BD_FLAGS_AGG_EOP; bnxt_init_rxbd_pages(ring, type); -- cgit v1.2.3 From 80eaf88efec33ac77ed7726d066c4f2f932cc329 Mon Sep 17 00:00:00 2001 From: Keegan Freyhof Date: Fri, 31 Jul 2026 12:09:37 -0700 Subject: bnxt_en: Fix PTP PPS setting bug The existing driver logic is always turning on PTP_CLK_REQ_PPS regardless of the "on" parameter passed to bnxt_ptp_enable(). During shutdown, PTP_CLK_REQ_PPS may be turned off and this bug will do the opposite and may trigger a PCIe PTM request TLP. On some systems this can trigger a PCIe AER. Fix it by properly configuring PTP_CLK_REQ_PPS based on the "on" parameter. Fixes: 9e518f25802c ("bnxt_en: 1PPS functions to configure TSIO pins") Reviewed-by: Pavan Chebbi Signed-off-by: Keegan Freyhof Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-6-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 5d41dc1bc782..b89293dfd2d1 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -495,12 +495,15 @@ static int bnxt_ptp_enable(struct ptp_clock_info *ptp_info, return rc; case PTP_CLK_REQ_PPS: /* Configure PHC PPS IN */ - rc = bnxt_ptp_cfg_pin(bp, 0, BNXT_PPS_PIN_PPS_IN); + pin_id = 0; + if (!on) + break; + rc = bnxt_ptp_cfg_pin(bp, pin_id, BNXT_PPS_PIN_PPS_IN); if (rc) return rc; rc = bnxt_ptp_cfg_event(bp, BNXT_PPS_EVENT_INTERNAL); if (!rc) - ptp->pps_info.pins[0].event = BNXT_PPS_EVENT_INTERNAL; + ptp->pps_info.pins[pin_id].event = BNXT_PPS_EVENT_INTERNAL; return rc; default: netdev_err(ptp->bp->dev, "Unrecognized PIN function\n"); -- cgit v1.2.3 From 26444eb71465c9934d9d418ef69c43f61185329b Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Thu, 23 Jul 2026 16:16:31 +0100 Subject: mm/vmalloc: acquire init_mm lock on huge vmap to avoid ptdump UAF Patch series "mm: fix UAF caused by race between ptdump and vmap pgtable freeing", v6. Kernel page table walkers fall into two broad categories - those ranges where no exclusion is required via walk_kernel_page_table_range_lockless() and those where exclusion is required via walk_kernel_page_table_range() or walk_page_range_debug(). The former category is used only by arm64 arch code operating on ranges it both wholly owns and does not concurrently write. The latter category consists of kernel page table walkers operating on ranges that are wholly owned (but which need exclusion against concurrent writers). The lock used for exclusion is the mmap lock, and for kernel ranges this is the mmap lock on init_mm. ptdump is a special case being both the only user of walk_page_range_debug(), and the only case in which it walks ranges it does not own. This presents a problem, as page tables may be freed under ptdump. And indeed there is a use-after-free bug in the kernel as a result, which this series addresses. vmap promotes page tables to huge leaf entries where possible, freeing the lower page table when it does. It does this with no meaningful locks held against concurrent ptdump walks. As a result, use-after-free can currently occur. This series addresses the issue by having the vmap huge promotion logic acquire the mmap read lock while both setting the huge page table entry and freeing the prior leaf page table. The ptdump code already acquires the mmap write lock, so by doing so we ensure that the ptdump walker only ever observes either the huge page table entry or the existing page table entry, and nothing is freed underneath it. A mitigation for this issue was already applied for arm64 in commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump"), which this series has to deal with carefully. This mitigation resolves the issue by acquiring the mmap read lock on init_mm on vmap page table free if a ptdump is in progress. However the fix in this series would cause a deadlock if we were to simply apply it for arm64 without also reverting the change. This is because vmap may acquire the read lock before ptdump attempts to acquire the write lock, which then gets queued, and rwsem starvation rules mean that the (unacknowledged) nested mmap read lock in the arm64 code would also block, meaning the original read lock is never released and thus deadlock. This series works around this by #ifndef CONFIG_ARM64'ing the mmap read lock in vmap logic, then partially reverting commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump"), keeping the enablement of huge vmap support, and removing the ifdeffery with the partial revert patch. There are related issues that are also addressed in this series: * x86 page attribute logic, specifically Change Page Attributes (CPA), implements a feature whereby huge ranges can be collapsed into huge leaf entries. This can similarly cause a UAF when done in parallel with a ptdump walk, so similarly acquire the init_mm mmap lock to avoid this. * The CPA logic allows concurrent page table manipulation and CPA collapse, meaning the former risks accessing a page table the latter frees. Fix this by acquiring mmap write lock on init_mm across the whole CPA collapse operation and read lock on the page table manipulation. * x86 and arm64 permit walks of non-kernel mm's (both allowing efi mm walks, and in x86's case arbitrary mm's), so we ensure kernel mappings remain stable by locking the init_mm as well as the mm being walked. The ordering of patches is established for both strict dependencies (the arm64 partial revert in particular has to be done after the vmap changes) and logical ones (the non-kernel mm fix only makes sense once the vmap/CPA fixes are in place). This patch (of 3): Currently there is a nasty race between ptdump and vmap when attempting to map a huge P4D, PUD or PMD entry: * ptdump walks kernel page table ranges it doesn't own. * When vmap maps ranges it tries to promotes existing ones to huge page tables in vmap_try_huge_[p4d,pud,pmd]() at P4D, PUD and PMD level, freeing the lower page table in [p4d,pud,pmd]_free_[pud,pmd,pte]_page() when it succeeds. Both of these things can happen at the same time and as a result ptdump can access a freed page table, resulting in a use-after-free and memory corruption. This is possible because while ptdump_walk_pgd() holds both the mem hotplug lock and the mmap write lock before invoking walk_page_range_debug(), vmap takes no relevant locks at all. Fix this by holding the mmap read lock in vmap_try_huge_*() when freeing page tables. The read lock is sufficient: ptdump is the only walker that must be excluded and it holds the mmap write lock. Other holders of the read lock may run concurrently, but each exclusively owns the range it operates on and cannot reach the page tables freed here. We also hold the lock while assigning the huge page table entry, which means page table walkers observe only the huge or non-huge page table entry. We use a trylock to prevent ptdump from blocking vmap making forward progress. This is fine because it's an optimisation in any case, and thus the vmap can safely proceed regardless. All other kernel page table walkers that touch vmalloc ranges either exclusively own the memory walked or acquire the mmap lock, so this correctly excludes those walkers. One wrinkle here is commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump"), which addresses the issue for arm64 only by explicitly acquiring the mmap read lock on kernel page table freeing should a concurrent ptdump be in progress. This is problematic as vmap may acquire the mmap read lock prior to ptdump attempting to acquire an mmap write lock, leading to a deadlock when the mmap read lock is slept upon on page table freeing due to rwsem anti-starvation. We work around this by predicating the mmap lock being taken on !CONFIG_ARM64 for the time being. With this patch applied, a follow up will partially revert commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump") and at that stage remove the arm64 ifdeffery. We also update walk_page_range_debug() to assert the mmap write lock unconditionally and update the comment here to reflect this change. The issue has existed as long as ptdump was available and vmap freed page tables when promoting to a huge leaf entry, that is, since commit b6bdb7517c3d ("mm/vmalloc: add interfaces to free unmapped page table") for huge ioremap, and commit 121e6f3258fe ("mm/vmalloc: hugepage vmalloc mappings") for huge vmalloc. Since the former is the earlier of the two we choose that for our Fixes tag. We also define a guard class for mmap_read_trylock() so we can use cleanup.h to make the scope handling cleaner in the implementation. This patch is based on work by David Carlier (linked), with gratitude! Link: https://lore.kernel.org/20260723-series-vmap-race-fix-v6-0-8cc77dcc0018@kernel.org Link: https://lore.kernel.org/20260723-series-vmap-race-fix-v6-1-8cc77dcc0018@kernel.org Fixes: b6bdb7517c3d ("mm/vmalloc: add interfaces to free unmapped page table") Signed-off-by: Lorenzo Stoakes (ARM) Reported-by: syzbot+fd95a72470f5a44e464c@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6a287988.39669fcc.33b062.00a0.GAE@google.com/T/ Link: https://lore.kernel.org/linux-mm/20260706203128.162335-1-devnexen@gmail.com/ Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: Dev Jain Acked-by: David Hildenbrand (Arm) Reviewed-by: Kiryl Shutsemau Cc: Cc: Andy Lutomirski Cc: "Borah, Chaitanya Kumar" Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Dave Hansen Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Liam R. Howlett Cc: Michal Hocko Cc: Peter Zijlstra Cc: Ryan Roberts Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Toshi Kani Cc: "Uladzislau Rezki (Sony)" Cc: Vlastimil Babka Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/mmap_lock.h | 1 + mm/pagewalk.c | 22 +++++++++++---------- mm/vmalloc.c | 49 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h index 04b8f61ece5d..6b5c2390cc30 100644 --- a/include/linux/mmap_lock.h +++ b/include/linux/mmap_lock.h @@ -621,6 +621,7 @@ static inline void mmap_read_unlock(struct mm_struct *mm) DEFINE_GUARD(mmap_read_lock, struct mm_struct *, mmap_read_lock(_T), mmap_read_unlock(_T)) +DEFINE_GUARD_COND(mmap_read_lock, _try, mmap_read_trylock(_T)) static inline void mmap_read_unlock_non_owner(struct mm_struct *mm) { diff --git a/mm/pagewalk.c b/mm/pagewalk.c index 3ae2586ff45b..bbcfd68d0907 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -678,6 +678,8 @@ int walk_kernel_page_table_range_lockless(unsigned long start, unsigned long end * will also not lock the PTEs for the pte_entry() callback. * * This is for debugging purposes ONLY. + * + * The mmap write lock must be held. */ int walk_page_range_debug(struct mm_struct *mm, unsigned long start, unsigned long end, const struct mm_walk_ops *ops, @@ -691,6 +693,16 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start, .no_vma = true }; + /* + * When walking userland page tables, an mmap write lock must be held to + * account for munmap() downgrading to an mmap read lock when tearing + * down page tables. + * + * When walking kernel page tables, an mmap write lock must also be held + * to account for page table freeing on vmap huge page mapping. + */ + mmap_assert_write_locked(mm); + /* For convenience, we allow traversal of kernel mappings. */ if (mm == &init_mm) return walk_kernel_page_table_range(start, end, ops, @@ -700,16 +712,6 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start, if (!check_ops_safe(ops)) return -EINVAL; - /* - * The mmap lock protects the page walker from changes to the page - * tables during the walk. However a read lock is insufficient to - * protect those areas which don't have a VMA as munmap() detaches - * the VMAs before downgrading to a read lock and actually tearing - * down PTEs/page tables. In which case, the mmap write lock should - * be held. - */ - mmap_assert_write_locked(mm); - return walk_pgd_range(start, end, &walk); } diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 1afca3568b9b..d5c4d2bb770b 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -43,6 +43,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -158,10 +159,24 @@ static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end, if (!IS_ALIGNED(phys_addr, PMD_SIZE)) return 0; - if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr)) - return 0; + if (!pmd_present(*pmd)) + return pmd_set_huge(pmd, phys_addr, prot); - return pmd_set_huge(pmd, phys_addr, prot); + /* + * Acquire the mmap read lock to exclude ptdump, which walks + * kernel page tables it does not own under the mmap write lock. + * + * Concurrent read lock holders are safe: each exclusively owns + * the range it operates on and cannot reach this page table. + */ +#ifndef CONFIG_ARM64 + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) +#endif + { + if (!pmd_free_pte_page(pmd, addr)) + return 0; + return pmd_set_huge(pmd, phys_addr, prot); + } } static int vmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end, @@ -210,10 +225,18 @@ static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end, if (!IS_ALIGNED(phys_addr, PUD_SIZE)) return 0; - if (pud_present(*pud) && !pud_free_pmd_page(pud, addr)) - return 0; + if (!pud_present(*pud)) + return pud_set_huge(pud, phys_addr, prot); - return pud_set_huge(pud, phys_addr, prot); + /* See comment in vmap_try_huge_pmd(). */ +#ifndef CONFIG_ARM64 + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) +#endif + { + if (!pud_free_pmd_page(pud, addr)) + return 0; + return pud_set_huge(pud, phys_addr, prot); + } } static int vmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end, @@ -262,10 +285,18 @@ static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end, if (!IS_ALIGNED(phys_addr, P4D_SIZE)) return 0; - if (p4d_present(*p4d) && !p4d_free_pud_page(p4d, addr)) - return 0; + if (!p4d_present(*p4d)) + return p4d_set_huge(p4d, phys_addr, prot); - return p4d_set_huge(p4d, phys_addr, prot); + /* See comment in vmap_try_huge_pmd(). */ +#ifndef CONFIG_ARM64 + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) +#endif + { + if (!p4d_free_pud_page(p4d, addr)) + return 0; + return p4d_set_huge(p4d, phys_addr, prot); + } } static int vmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end, -- cgit v1.2.3 From 27c32e5538344b13c1505a08861e04620c125d47 Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Thu, 23 Jul 2026 16:16:34 +0100 Subject: mm/ptdump: always stabilise against page table freeing using init_mm Previous commits have established the invariant that kernel page table freeing is performed while an mmap read lock on init_mm is held, which fixes races between ptdump and kernel page table freeing over init_mm. However, x86 and arm64 can perform a ptdump over an mm other than init_mm via ptdump_walk_pgd() and since kernel memory ranges are shared across non-kernel mm's, this means that the race still exists for these cases. Fix this by acquiring a nested mmap write lock for init_mm in ptdump_walk_pgd(). This is safe as we take this after mmap write locking the mm, and nothing acquires the init_mm lock first before locking an arbitrary mm, so no deadlock is possible. Also update walk_page_range_debug() to assert that init_mm is write locked, add a comment explaining why and remove some redundant code, and eliminate the unnecessary and confusing invocation of walk_kernel_page_table_range(). We can safely remove the non-NULL check for walk.mm, as the mmap lock asserts would NULL pointer deref if it was (and of course no callers do this). The first point at which ptdump can race kernel page table freeing is commit b6bdb7517c3d ("mm/vmalloc: add interfaces to free unmapped page table"), so we target this in the Fixes tag. Link: https://lore.kernel.org/20260723-series-vmap-race-fix-v6-4-8cc77dcc0018@kernel.org Fixes: b6bdb7517c3d ("mm/vmalloc: add interfaces to free unmapped page table") Signed-off-by: Lorenzo Stoakes (ARM) Reviewed-by: Mike Rapoport (Microsoft) Acked-by: David Hildenbrand (Arm) Reviewed-by: Kiryl Shutsemau Cc: Andy Lutomirski Cc: "Borah, Chaitanya Kumar" Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Dave Hansen Cc: David Carlier Cc: Dev Jain Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Liam R. Howlett Cc: Michal Hocko Cc: Peter Zijlstra Cc: Ryan Roberts Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Toshi Kani Cc: "Uladzislau Rezki (Sony)" Cc: Vlastimil Babka Cc: Will Deacon Cc: Signed-off-by: Andrew Morton --- mm/pagewalk.c | 14 +++++++++----- mm/ptdump.c | 7 +++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mm/pagewalk.c b/mm/pagewalk.c index bbcfd68d0907..5d87c632a255 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -702,12 +702,16 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start, * to account for page table freeing on vmap huge page mapping. */ mmap_assert_write_locked(mm); + /* + * x86, arm64 ptdump allow walks of efi mm's and x86 ptdump allows walks + * of arbitrary mm's. + * + * However, they both must also hold the init_mm lock to account for + * concurrent kernel page table freeing. + */ + mmap_assert_write_locked(&init_mm); - /* For convenience, we allow traversal of kernel mappings. */ - if (mm == &init_mm) - return walk_kernel_page_table_range(start, end, ops, - pgd, private); - if (start >= end || !walk.mm) + if (start >= end) return -EINVAL; if (!check_ops_safe(ops)) return -EINVAL; diff --git a/mm/ptdump.c b/mm/ptdump.c index 973020000096..5851096e6f65 100644 --- a/mm/ptdump.c +++ b/mm/ptdump.c @@ -178,11 +178,18 @@ void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm, pgd_t *pgd) get_online_mems(); mmap_write_lock(mm); + /* To stabilise kernel page tables we must hold the init_mm lock too. */ + if (mm != &init_mm) + mmap_write_lock_nested(&init_mm, SINGLE_DEPTH_NESTING); + while (range->start != range->end) { walk_page_range_debug(mm, range->start, range->end, &ptdump_ops, pgd, st); range++; } + + if (mm != &init_mm) + mmap_write_unlock(&init_mm); mmap_write_unlock(mm); put_online_mems(); -- cgit v1.2.3 From 9d3277b2c07ccc9508d648098b3bbb46c61b7f3c Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Thu, 23 Jul 2026 16:16:35 +0100 Subject: arm64: remove redundant concurrent ptdump UAF mitigation This partially reverts commit fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump"), retaining vmalloc-huge support but eliminating the now redundant mitigation against a race between huge vmap page table freeing and ptdump, as this issue has now been fixed at core. We also simultaneously remove the arm64 if-deffery when acquiring the mmap read lock upon vmap huge page table promotion as it is no longer required. Note that this patch relies on the preceding vmalloc patch, and should not be backported alone. Link: https://lore.kernel.org/20260723-series-vmap-race-fix-v6-5-8cc77dcc0018@kernel.org Fixes: fa93b45fd397 ("arm64: Enable vmalloc-huge with ptdump") Signed-off-by: Lorenzo Stoakes (ARM) Reviewed-by: Dev Jain Acked-by: Mike Rapoport (Microsoft) Acked-by: Kiryl Shutsemau (Meta) Acked-by: Will Deacon Reviewed-by: David Hildenbrand (Arm) Cc: Andy Lutomirski Cc: "Borah, Chaitanya Kumar" Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Dave Hansen Cc: David Carlier Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Liam R. Howlett Cc: Michal Hocko Cc: Peter Zijlstra Cc: Ryan Roberts Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: Toshi Kani Cc: "Uladzislau Rezki (Sony)" Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- arch/arm64/include/asm/ptdump.h | 2 -- arch/arm64/mm/mmu.c | 43 ++++------------------------------------- arch/arm64/mm/ptdump.c | 11 ++--------- mm/vmalloc.c | 15 +++----------- 4 files changed, 9 insertions(+), 62 deletions(-) diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h index 5b374a6ab34a..50a195eda8ed 100644 --- a/arch/arm64/include/asm/ptdump.h +++ b/arch/arm64/include/asm/ptdump.h @@ -7,8 +7,6 @@ #include -DECLARE_STATIC_KEY_FALSE(arm64_ptdump_lock_key); - #ifdef CONFIG_PTDUMP #include diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 18a8b0d3714e..d4de88770ecf 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -49,8 +49,6 @@ #define NO_CONT_MAPPINGS BIT(1) #define NO_EXEC_MAPPINGS BIT(2) /* assumes FEAT_HPDS is not used */ -DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key); - u64 kimage_voffset __ro_after_init; EXPORT_SYMBOL(kimage_voffset); @@ -1864,8 +1862,7 @@ int pmd_clear_huge(pmd_t *pmdp) return 1; } -static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr, - bool acquire_mmap_lock) +int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr) { pte_t *table; pmd_t pmd; @@ -1877,25 +1874,13 @@ static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr, return 1; } - /* See comment in pud_free_pmd_page for static key logic */ table = pte_offset_kernel(pmdp, addr); pmd_clear(pmdp); __flush_tlb_kernel_pgtable(addr); - if (static_branch_unlikely(&arm64_ptdump_lock_key) && acquire_mmap_lock) { - mmap_read_lock(&init_mm); - mmap_read_unlock(&init_mm); - } - pte_free_kernel(NULL, table); return 1; } -int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr) -{ - /* If ptdump is walking the pagetables, acquire init_mm.mmap_lock */ - return __pmd_free_pte_page(pmdp, addr, /* acquire_mmap_lock = */ true); -} - int pud_free_pmd_page(pud_t *pudp, unsigned long addr) { pmd_t *table; @@ -1911,36 +1896,16 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr) } table = pmd_offset(pudp, addr); - - /* - * Our objective is to prevent ptdump from reading a PMD table which has - * been freed. In this race, if pud_free_pmd_page observes the key on - * (which got flipped by ptdump) then the mmap lock sequence here will, - * as a result of the mmap write lock/unlock sequence in ptdump, give - * us the correct synchronization. If not, this means that ptdump has - * yet not started walking the pagetables - the sequence of barriers - * issued by __flush_tlb_kernel_pgtable() guarantees that ptdump will - * observe an empty PUD. - */ - pud_clear(pudp); - __flush_tlb_kernel_pgtable(addr); - if (static_branch_unlikely(&arm64_ptdump_lock_key)) { - mmap_read_lock(&init_mm); - mmap_read_unlock(&init_mm); - } - pmdp = table; next = addr; end = addr + PUD_SIZE; do { if (pmd_present(pmdp_get(pmdp))) - /* - * PMD has been isolated, so ptdump won't see it. No - * need to acquire init_mm.mmap_lock. - */ - __pmd_free_pte_page(pmdp, next, /* acquire_mmap_lock = */ false); + pmd_free_pte_page(pmdp, next); } while (pmdp++, next += PMD_SIZE, next != end); + pud_clear(pudp); + __flush_tlb_kernel_pgtable(addr); pmd_free(NULL, table); return 1; } diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c index 1c20144700d7..5a76c59b5ada 100644 --- a/arch/arm64/mm/ptdump.c +++ b/arch/arm64/mm/ptdump.c @@ -283,13 +283,6 @@ void note_page_flush(struct ptdump_state *pt_st) note_page(pt_st, 0, -1, pte_val(pte_zero)); } -static void arm64_ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm) -{ - static_branch_inc(&arm64_ptdump_lock_key); - ptdump_walk_pgd(st, mm, NULL); - static_branch_dec(&arm64_ptdump_lock_key); -} - void ptdump_walk(struct seq_file *s, struct ptdump_info *info) { unsigned long end = ~0UL; @@ -318,7 +311,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info) } }; - arm64_ptdump_walk_pgd(&st.ptdump, info->mm); + ptdump_walk_pgd(&st.ptdump, info->mm, NULL); } static void __init ptdump_initialize(void) @@ -360,7 +353,7 @@ bool ptdump_check_wx(void) } }; - arm64_ptdump_walk_pgd(&st.ptdump, &init_mm); + ptdump_walk_pgd(&st.ptdump, &init_mm, NULL); if (st.wx_pages || st.uxn_pages) { pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n", diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d5c4d2bb770b..f4fa227a8d7f 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -169,10 +169,7 @@ static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end, * Concurrent read lock holders are safe: each exclusively owns * the range it operates on and cannot reach this page table. */ -#ifndef CONFIG_ARM64 - scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) -#endif - { + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) { if (!pmd_free_pte_page(pmd, addr)) return 0; return pmd_set_huge(pmd, phys_addr, prot); @@ -229,10 +226,7 @@ static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end, return pud_set_huge(pud, phys_addr, prot); /* See comment in vmap_try_huge_pmd(). */ -#ifndef CONFIG_ARM64 - scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) -#endif - { + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) { if (!pud_free_pmd_page(pud, addr)) return 0; return pud_set_huge(pud, phys_addr, prot); @@ -289,10 +283,7 @@ static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end, return p4d_set_huge(p4d, phys_addr, prot); /* See comment in vmap_try_huge_pmd(). */ -#ifndef CONFIG_ARM64 - scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) -#endif - { + scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) { if (!p4d_free_pud_page(p4d, addr)) return 0; return p4d_set_huge(p4d, phys_addr, prot); -- cgit v1.2.3 From c2689266e5f70ecc960e64d40516518c038ae34e Mon Sep 17 00:00:00 2001 From: Ramin Moussavi Date: Mon, 27 Jul 2026 23:58:23 +0200 Subject: microblaze: restore the page alignment of swapper_pg_dir microblaze handles TLB misses in software, and the handler builds the address of the L1 entry by ORing the index into the page directory base instead of adding it (hw_exception_handler.S): bsrli r5, r3, PGDIR_SHIFT - 2 andi r5, r5, PAGE_SIZE - 4 /* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */ or r4, r4, r5 lwi r4, r4, 0 /* Get L1 entry */ The index is masked to the low 12 bits, so the OR only works if those bits of the base are zero -- which is exactly the assumption the comment states and the reason the masking of the base can be skipped. swapper_pg_dir had no alignment directive of its own. It was aligned because it followed empty_zero_page in head.S, and that one carried the .align 12: .section .data .global empty_zero_page .align 12 empty_zero_page: .space PAGE_SIZE .global swapper_pg_dir swapper_pg_dir: .space PAGE_SIZE Commit 6215d9f4470f ("arch, mm: consolidate empty_zero_page") removed empty_zero_page from head.S, and with it the .align 12 that -- despite sitting next to empty_zero_page -- was what page aligned swapper_pg_dir. Since then swapper_pg_dir lands wherever .data happens to put it, its low bits are no longer zero, and every kernel TLB miss ORs the index into a base with a nonzero offset. The resulting L1 lookups read the wrong words, no valid translation is ever installed, and the kernel spins in exceptions long before it can print anything. On qemu-system-microblazeel (petalogix-s3adsp1800) the console stays completely silent at 100% CPU; there is no oops and no guest error reported by qemu, which makes this awkward to diagnose. Give swapper_pg_dir the alignment it requires, rather than relying on a neighbour to provide it. microblaze has no noMMU variant left in mainline -- CONFIG_MMU is def_bool y and mmu_defconfig is the only defconfig -- so this is not a corner case: every mainline microblaze kernel since v7.1-rc1 fails to boot, including the v7.1 release. v7.0: swapper_pg_dir = 0xc05fd000 (aligned) v7.1-rc1: swapper_pg_dir = 0xc0603140 (offset 320) v7.1-rc1 + this fix: swapper_pg_dir = 0xc0604000 (aligned) next-20260726: swapper_pg_dir = 0xc0615140 (offset 320) next-20260726 + this fix: swapper_pg_dir = 0xc0616000 (aligned) Verified on qemu-system-microblazeel (petalogix-s3adsp1800) with mmu_defconfig and microblazeel gcc 12.5.0: v7.1-rc1 and next-20260726 both print nothing at all without the fix, and both boot to userspace with it. Link: https://lore.kernel.org/20260727215823.1422701-1-ramin.moussavi@yacoub.de Fixes: 6215d9f4470f ("arch, mm: consolidate empty_zero_page") Signed-off-by: Ramin Moussavi Cc: Michal Simek Cc: Mike Rapoport Cc: Signed-off-by: Andrew Morton --- arch/microblaze/kernel/head.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/microblaze/kernel/head.S b/arch/microblaze/kernel/head.S index 808019c3b7ac..9bd3e513c89b 100644 --- a/arch/microblaze/kernel/head.S +++ b/arch/microblaze/kernel/head.S @@ -39,6 +39,8 @@ #include .section .data +/* The MMU requires a page aligned page directory. */ +.align 12 .global swapper_pg_dir swapper_pg_dir: .space PAGE_SIZE -- cgit v1.2.3 From 86da3f7e1e609e1e8bfbab198af68467c5a015a5 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Mon, 27 Jul 2026 22:24:14 -0700 Subject: mm/filemap: __filemap_add_folio() restore index before retrying In __filemap_add_folio()'s split-a-conflict loop, xas_set_order() is applied repeatedly: each application modifies xas.xa_index, rounding it down according to the split_order attempted at that stage: and if all goes as intended, it eventually (or immediately) converges on an xas_try_split() to the required folio_order, with xas.xa_index now the same as index: then xas_store() puts the new folio into the xarray there. But if a new node was needed, and GFP_NOWAIT allocation did not get one, the lock is dropped, xas_nomem() used to allocate, and sequence retried. If (that part of) the xarray is unchanged when the lock is reacquired, no problem. But what if the conflict was meanwhile resolved by another thread (perhaps even doing the same thing, inserting a folio at that same index)? Isn't there a danger of now putting our folio into the xarray at an intermediate rounded-down index? With !folio_contains() bug to follow, when CONFIG_DEBUG_VM=y is checking for that. Fix this with an xas_set_order() to restore the original xas.xa_index at the bottom of the loop, so the retry does a full re-evaluation after reacquiring the lock, and cannot reach xas_store() with the wrong index. Production was suffering from rare SIGILLs and SIGSEGVs, executable text found a page away from where it belonged, !folio_contains() bug hit when debug enabled: symptoms not seen since this patch went in. Link: https://lore.kernel.org/562fbfa6-dd6d-0b6a-2461-ed2ff1173bc8@google.com Fixes: 200a89c159a7 ("mm/filemap: use xas_try_split() in __filemap_add_folio()") Signed-off-by: Hugh Dickins Acked-by: Kiryl Shutsemau (Meta) Reviewed-by: Matthew Wilcox (Oracle) Reviewed-by: Zi Yan Cc: Chris J Arges Cc: David Hildenbrand Cc: Jan Kara Cc: Kairui Song Cc: Signed-off-by: Andrew Morton --- mm/filemap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/filemap.c b/mm/filemap.c index 58eb9d240643..d721986d5f46 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -931,6 +931,12 @@ unlock: if (!xas_nomem(&xas, gfp)) break; + + /* + * Lock has been dropped: start again with the original index + * and order (but now with the memory reserved by xas_nomem()). + */ + xas_set_order(&xas, index, forder); } if (xas_error(&xas)) -- cgit v1.2.3 From 50124648db87fb63d9548b6d09deb6d2c6359dfa Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Wed, 29 Jul 2026 12:01:48 +0000 Subject: MAINTAINERS: update address for Brendan Jackman Switch my entry in MAINTAINERS and .mailmap to my @linux.dev email address Link: https://lore.kernel.org/20260729-email-change-v1-1-666ae7c2b7fc@google.com Signed-off-by: Brendan Jackman Signed-off-by: Brendan Jackman Acked-by: Mike Rapoport (Microsoft) Acked-by: Lorenzo Stoakes (ARM) Acked-by: Zi Yan Cc: David Hildenbrand Cc: Johannes Weiner Cc: Liam R. Howlett Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index ca6dc2575802..7324e1af59c2 100644 --- a/.mailmap +++ b/.mailmap @@ -170,6 +170,7 @@ Boris Brezillon Boris Brezillon Boris Brezillon Brendan Higgins +Brendan Jackman Brian Avery Brian Cain Brian Cain diff --git a/MAINTAINERS b/MAINTAINERS index 48b2baa1541f..d28e9f59e921 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17160,7 +17160,7 @@ M: Andrew Morton M: Vlastimil Babka R: Suren Baghdasaryan R: Michal Hocko -R: Brendan Jackman +R: Brendan Jackman R: Johannes Weiner R: Zi Yan L: linux-mm@kvack.org -- cgit v1.2.3 From 33192a26cddea7a7e4ca66e5c3eebd36fa8be2bb Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Thu, 30 Jul 2026 11:55:47 +0100 Subject: mm/huge_memory: fix huge_zero_pfn race Patch series "mm/huge_memory: fix huge_zero_pfn race", v2. There is a subtle race in the reference-counted huge_zero_folio implementation. The fast path atomic logic fails to account for the fact that the shrinker (which drops the final huge_zero_refcount pin) can overwrite huge_zero_pfn with the ~0UL sentinel value in shrink_huge_zero_folio_scan() after a racing get_huge_zero_folio() installed a valid value there. This results in huge_zero_folio being correctly set but huge_zero_pfn being set incorrectly and thus is_huge_zero_pfn() and consequently is_huge_zero_pmd() will misidentify the huge zero folio as being an ordinary THP folio. This can result in the huge zero folio being split and otherwise treated incorrectly. The solution to this is very subtle as there is an atomic fast path, and thus ordering in weakly ordered architectures has to be treated very carefully. The first commit fixes the issue by introducing a spinlock around huge_zero_[pfn, folio, refcount] write, with careful consideration paid to load/store ordering in the fast path. It is placed first and kept as small as possible so that it can be backported on its own. The second commit is a pure cleanup which reworks the CONFIG_PERSISTENT_HUGE_ZERO_FOLIO logic to better separate the persistent logic from the dynamically allocated one. This patch (of 2): If !CONFIG_PERSISTENT_HUGE_ZERO_FOLIO, the huge_zero_folio is refcounted by huge_zero_refcount and returned by mm_get_huge_zero_folio(). When the caller is done with the huge zero page, its reference count is decremented. Only a shrinker can set the reference count to zero. A race can unfortunately occur between a shrinker decrementing the reference count to zero and a concurrent page fault. This is because shrink_huge_zero_folio_scan() might, if very unlucky, be preempted between setting huge_zero_refcount to zero and writing an invalid value. During this time get_huge_zero_folio() could write to huge_zero_pfn before shrink_huge_zero_folio_scan() resumes. In this event the huge zero folio will be persistently misidentified causing the THP code path to be entered inappropriately for the huge zero folio: CPU 0 CPU 1 =======================================|================================= shrink_huge_zero_folio_scan() | atomic_cmpxchg() sets refcount to 0 | xchg() sets huge_zero_folio to NULL | get_huge_zero_folio() | | atomic_inc_not_zero() -> zero preempted for a long time | Allocate new huge zero folio | | Write valid huge_zero_folio v | Write valid huge_zero_pfn Overwrite huge_zero_pfn with ~0UL <--- Invalid overwrite! This results in is_huge_zero_pfn() and is_huge_zero_pmd() incorrectly returning false for a huge zero page which could result in issues like the huge zero folio being incorrectly split. Note that the issue is with huge_zero_pfn not huge_zero_folio, as get_huge_zero_folio() uses cmpxchg() gated on huge_zero_folio being NULL with a retry loop and shrink_huge_zero_folio_scan() uses xchg() to set huge_zero_folio. Fix the issue by introducing a spinlock, huge_zero_lock, to prevent concurrent write of huge_zero_folio, huge_zero_pfn and huge_zero_refcount. There needs to be significant care taken here to ensure correctness: The fast path in get_huge_zero_folio() uses atomic_inc_not_zero(), which is outside of the critical section, and means huge zero allocation is gated on zero huge_zero_refcount. The fast path doesn't use huge_zero_lock, so the critical section is irrelevant to it. So invariants are required - huge_zero_refcount MUST: * Only be set in the huge_zero_lock critical section to ensure serialisation of huge_zero_pfn, huge_zero_folio and huge_zero_refcount writes. * Be set non-zero only AFTER huge_zero_[pfn, folio] are set to valid values so installation of the huge zero folio on read page fault ensures concurrent is_huge_zero_*() calls correctly identify the huge zero folio. * Be set zero only BEFORE huge_zero_[pfn, folio] are set to NULL and ~0UL respectively, and atomically. Establish these by: * Only setting huge_zero_refcount to zero or an absolute value in the huge_zero_lock critical section in get_huge_zero_folio() and shrink_huge_zero_folio_scan(), and always updating atomically there and elsewhere. * Using atomic_set_release(&huge_zero_refcount) in get_huge_zero_folio() after huge_zero_[pfn, folio] are set. This is paired with atomic_inc_not_zero() to ensure atomic_inc_not_zero() only observes a non-zero value if huge_zero_[pfn, folio] are set. * Using atomic_cmpxchg() in shrink_huge_zero_folio_scan() (as before) to ensure that it is set zero only when equal to 1 and set atomically. * atomic_cmpxchg() being fully ordered ensures this is done prior to huge_zero_[folio, pfn] being set to NULL and ~0UL respectively. Eliminate the retry loop in get_huge_zero_folio() as the atomic_cmpxchg() in shrink_huge_zero_folio_scan() is now performed under the lock, and replace with an equally locked atomic_inc() to set the reference count should the caller be raced on huge zero folio installation. folio_put() naturally implies a full memory barrier so its ordering is maintained correctly. The huge zero folio also cannot be released except when the shrinker does so as it is non-LRU and non-rmappable. Note that only the huge zero shrinker (via shrink_huge_zero_folio_scan()) can actually set huge_zero_refcount to zero, which is the count of mm's which have at least one huge zero folio installed plus one shrinker pin. Additionally convert a BUG_ON() to a VM_WARN_ON_ONCE(). Link: https://lore.kernel.org/20260730-fix-refcounted-huge-zero-v2-0-c5d8a41b317f@kernel.org Link: https://lore.kernel.org/20260730-fix-refcounted-huge-zero-v2-1-c5d8a41b317f@kernel.org Fixes: 3b77e8c8cde5 ("mm/thp: make is_huge_zero_pmd() safe and quicker") Signed-off-by: Lorenzo Stoakes (ARM) Reported-by: Hengbin Zhang Closes: https://lore.kernel.org/linux-mm/20260727154001.4102341-1-uqbarz@gmail.com/ Suggested-by: David Hildenbrand (Arm) Acked-by: David Hildenbrand (Arm) Cc: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Hannes Reinecke Cc: Hugh Dickins Cc: Kiryl Shutsemau Cc: Lance Yang Cc: Liam R. Howlett Cc: Nico Pache Cc: Pankaj Raghav Cc: Ryan Roberts Cc: Yang Shi Cc: Zi Yan Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 58cabe6af33d..f43852ef9944 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include "internal.h" @@ -78,6 +79,7 @@ static unsigned long deferred_split_scan(struct shrinker *shrink, static bool split_underused_thp = true; static atomic_t huge_zero_refcount; +static DEFINE_SPINLOCK(huge_zero_lock); struct folio *huge_zero_folio __read_mostly; unsigned long huge_zero_pfn __read_mostly = ~0UL; unsigned long huge_anon_orders_always __read_mostly; @@ -224,7 +226,8 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma, static bool get_huge_zero_folio(void) { struct folio *zero_folio; -retry: + + /* Paired with atomic_set_release(). */ if (likely(atomic_inc_not_zero(&huge_zero_refcount))) return true; @@ -237,17 +240,22 @@ retry: } /* Ensure zero folio won't have large_rmappable flag set. */ folio_clear_large_rmappable(zero_folio); - preempt_disable(); - if (cmpxchg(&huge_zero_folio, NULL, zero_folio)) { - preempt_enable(); + + /* Paired with critical section in shrink_huge_zero_folio_scan(). */ + spin_lock(&huge_zero_lock); + if (huge_zero_folio) { + /* Somebody else already installed it. */ + atomic_inc(&huge_zero_refcount); + spin_unlock(&huge_zero_lock); folio_put(zero_folio); - goto retry; + return true; } + WRITE_ONCE(huge_zero_folio, zero_folio); WRITE_ONCE(huge_zero_pfn, folio_pfn(zero_folio)); + /* Paired with atomic_inc_not_zero(). +1 for shrinker pin. */ + atomic_set_release(&huge_zero_refcount, 2); + spin_unlock(&huge_zero_lock); - /* We take additional reference here. It will be put back by shrinker */ - atomic_set(&huge_zero_refcount, 2); - preempt_enable(); count_vm_event(THP_ZERO_PAGE_ALLOC); return true; } @@ -297,15 +305,22 @@ static unsigned long shrink_huge_zero_folio_count(struct shrinker *shrink, static unsigned long shrink_huge_zero_folio_scan(struct shrinker *shrink, struct shrink_control *sc) { - if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) == 1) { - struct folio *zero_folio = xchg(&huge_zero_folio, NULL); - BUG_ON(zero_folio == NULL); + struct folio *zero_folio; + + /* Paired with critical section in get_huge_zero_folio(). */ + scoped_guard(spinlock, &huge_zero_lock) { + /* Paired with atomic_inc_not_zero() in get_huge_zero_folio(). */ + if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) != 1) + return 0; + + zero_folio = huge_zero_folio; + VM_WARN_ON_ONCE(!zero_folio); + WRITE_ONCE(huge_zero_folio, NULL); WRITE_ONCE(huge_zero_pfn, ~0UL); - folio_put(zero_folio); - return HPAGE_PMD_NR; } - return 0; + folio_put(zero_folio); + return HPAGE_PMD_NR; } static struct shrinker *huge_zero_folio_shrinker; -- cgit v1.2.3 From 98bd3af0bb6bfdb0fb39cbfa05456acb374b691e Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Thu, 30 Jul 2026 11:55:48 +0100 Subject: mm/huge_memory: separate out CONFIG_PERSISTENT_HUGE_ZERO_FOLIO logic Rather than mixing the refcounted and non-refcounted CONFIG_PERSISTENT_HUGE_ZERO_FOLIO logic, separate the two out cleanly so it is clear what happens when this configuration option is set and what happens when it is not. Introduce HUGE_ZERO_UNSET_PFN to abstract the ~0UL assignment, only introduce the refcount, lock and shrinker if !CONFIG_PERSISTENT_HUGE_ZERO_FOLIO, abstract initialisation and teardown, abstract the huge zero folio allocation from refcounting. Also change a BUG_ON() to WARN_ON_ONCE() while we're at it. No functional change intended. Link: https://lore.kernel.org/20260730-fix-refcounted-huge-zero-v2-2-c5d8a41b317f@kernel.org Signed-off-by: Lorenzo Stoakes (ARM) Fixes: 3b77e8c8cde5 ("mm/thp: make is_huge_zero_pmd() safe and quicker") Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand (Arm) Cc: Dev Jain Cc: Hannes Reinecke Cc: Hengbin Zhang Cc: Hugh Dickins Cc: Kiryl Shutsemau Cc: Lance Yang Cc: Liam R. Howlett Cc: Nico Pache Cc: Pankaj Raghav Cc: Ryan Roberts Cc: Yang Shi Cc: Zi Yan Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 160 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 94 insertions(+), 66 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index f43852ef9944..f84fe7913d88 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -78,10 +78,15 @@ static unsigned long deferred_split_scan(struct shrinker *shrink, struct shrink_control *sc); static bool split_underused_thp = true; +#define HUGE_ZERO_UNSET_PFN (~0UL) +struct folio *huge_zero_folio __read_mostly; +unsigned long huge_zero_pfn __read_mostly = HUGE_ZERO_UNSET_PFN; +#ifndef CONFIG_PERSISTENT_HUGE_ZERO_FOLIO static atomic_t huge_zero_refcount; static DEFINE_SPINLOCK(huge_zero_lock); -struct folio *huge_zero_folio __read_mostly; -unsigned long huge_zero_pfn __read_mostly = ~0UL; +static struct shrinker *huge_zero_folio_shrinker; +#endif + unsigned long huge_anon_orders_always __read_mostly; unsigned long huge_anon_orders_madvise __read_mostly; unsigned long huge_anon_orders_inherit __read_mostly; @@ -223,23 +228,58 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma, return orders; } -static bool get_huge_zero_folio(void) +static struct folio *alloc_huge_zero_folio(void) { struct folio *zero_folio; - /* Paired with atomic_set_release(). */ - if (likely(atomic_inc_not_zero(&huge_zero_refcount))) - return true; - zero_folio = folio_alloc((GFP_TRANSHUGE | __GFP_ZERO | __GFP_ZEROTAGS) & ~__GFP_MOVABLE, HPAGE_PMD_ORDER); if (!zero_folio) { count_vm_event(THP_ZERO_PAGE_ALLOC_FAILED); - return false; + return NULL; + } + folio_clear_large_rmappable(zero_folio); /* Explicitly not rmappable. */ + return zero_folio; +} + +#ifdef CONFIG_PERSISTENT_HUGE_ZERO_FOLIO +static int __init huge_zero_init(void) +{ + huge_zero_folio = alloc_huge_zero_folio(); + if (!huge_zero_folio) { + pr_warn("Allocating persistent huge zero folio failed\n"); + } else { + huge_zero_pfn = folio_pfn(huge_zero_folio); + count_vm_event(THP_ZERO_PAGE_ALLOC); } - /* Ensure zero folio won't have large_rmappable flag set. */ - folio_clear_large_rmappable(zero_folio); + return 0; +} + +static void __init huge_zero_shrinker_exit(void) +{ +} + +struct folio *mm_get_huge_zero_folio(struct mm_struct *mm) +{ + return huge_zero_folio; +} + +void mm_put_huge_zero_folio(struct mm_struct *mm) +{ +} +#else +static bool get_huge_zero_folio(void) +{ + struct folio *zero_folio; + + /* Paired with atomic_set_release(). */ + if (likely(atomic_inc_not_zero(&huge_zero_refcount))) + return true; + + zero_folio = alloc_huge_zero_folio(); + if (unlikely(!zero_folio)) + return false; /* Paired with critical section in shrink_huge_zero_folio_scan(). */ spin_lock(&huge_zero_lock); @@ -266,33 +306,7 @@ static void put_huge_zero_folio(void) * Counter should never go to zero here. Only shrinker can put * last reference. */ - BUG_ON(atomic_dec_and_test(&huge_zero_refcount)); -} - -struct folio *mm_get_huge_zero_folio(struct mm_struct *mm) -{ - if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) - return huge_zero_folio; - - if (mm_flags_test(MMF_HUGE_ZERO_FOLIO, mm)) - return READ_ONCE(huge_zero_folio); - - if (!get_huge_zero_folio()) - return NULL; - - if (mm_flags_test_and_set(MMF_HUGE_ZERO_FOLIO, mm)) - put_huge_zero_folio(); - - return READ_ONCE(huge_zero_folio); -} - -void mm_put_huge_zero_folio(struct mm_struct *mm) -{ - if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) - return; - - if (mm_flags_test(MMF_HUGE_ZERO_FOLIO, mm)) - put_huge_zero_folio(); + WARN_ON_ONCE(atomic_dec_and_test(&huge_zero_refcount)); } static unsigned long shrink_huge_zero_folio_count(struct shrinker *shrink, @@ -316,14 +330,53 @@ static unsigned long shrink_huge_zero_folio_scan(struct shrinker *shrink, zero_folio = huge_zero_folio; VM_WARN_ON_ONCE(!zero_folio); WRITE_ONCE(huge_zero_folio, NULL); - WRITE_ONCE(huge_zero_pfn, ~0UL); + WRITE_ONCE(huge_zero_pfn, HUGE_ZERO_UNSET_PFN); } folio_put(zero_folio); return HPAGE_PMD_NR; } -static struct shrinker *huge_zero_folio_shrinker; +static int __init huge_zero_init(void) +{ + huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero"); + if (!huge_zero_folio_shrinker) { + shrinker_free(deferred_split_shrinker); + list_lru_destroy(&deferred_split_lru); + return -ENOMEM; + } + + huge_zero_folio_shrinker->count_objects = shrink_huge_zero_folio_count; + huge_zero_folio_shrinker->scan_objects = shrink_huge_zero_folio_scan; + shrinker_register(huge_zero_folio_shrinker); + return 0; +} + +static void __init huge_zero_shrinker_exit(void) +{ + shrinker_free(huge_zero_folio_shrinker); +} + +struct folio *mm_get_huge_zero_folio(struct mm_struct *mm) +{ + if (mm_flags_test(MMF_HUGE_ZERO_FOLIO, mm)) + return READ_ONCE(huge_zero_folio); + + if (!get_huge_zero_folio()) + return NULL; + + if (mm_flags_test_and_set(MMF_HUGE_ZERO_FOLIO, mm)) + put_huge_zero_folio(); + + return READ_ONCE(huge_zero_folio); +} + +void mm_put_huge_zero_folio(struct mm_struct *mm) +{ + if (mm_flags_test(MMF_HUGE_ZERO_FOLIO, mm)) + put_huge_zero_folio(); +} +#endif /* CONFIG_PERSISTENT_HUGE_ZERO_FOLIO */ #ifdef CONFIG_SYSFS static ssize_t enabled_show(struct kobject *kobj, @@ -987,39 +1040,14 @@ static int __init thp_shrinker_init(void) deferred_split_shrinker->scan_objects = deferred_split_scan; shrinker_register(deferred_split_shrinker); - if (IS_ENABLED(CONFIG_PERSISTENT_HUGE_ZERO_FOLIO)) { - /* - * Bump the reference of the huge_zero_folio and do not - * initialize the shrinker. - * - * huge_zero_folio will always be NULL on failure. We assume - * that get_huge_zero_folio() will most likely not fail as - * thp_shrinker_init() is invoked early on during boot. - */ - if (!get_huge_zero_folio()) - pr_warn("Allocating persistent huge zero folio failed\n"); - return 0; - } - - huge_zero_folio_shrinker = shrinker_alloc(0, "thp-zero"); - if (!huge_zero_folio_shrinker) { - shrinker_free(deferred_split_shrinker); - list_lru_destroy(&deferred_split_lru); - return -ENOMEM; - } - - huge_zero_folio_shrinker->count_objects = shrink_huge_zero_folio_count; - huge_zero_folio_shrinker->scan_objects = shrink_huge_zero_folio_scan; - shrinker_register(huge_zero_folio_shrinker); - - return 0; + return huge_zero_init(); } static void __init thp_shrinker_exit(void) { - shrinker_free(huge_zero_folio_shrinker); shrinker_free(deferred_split_shrinker); list_lru_destroy(&deferred_split_lru); + huge_zero_shrinker_exit(); } static int __init hugepage_init(void) -- cgit v1.2.3 From 4194140a51201e76c02c666ba83c07a81cefb6cc Mon Sep 17 00:00:00 2001 From: Danila Tikhonov Date: Sat, 1 Aug 2026 18:33:33 +0300 Subject: mailmap: map old addresses to Danila Tikhonov Map my old jiaxyga.com and ProtonMail addresses to the current mainlining.org address. Link: https://lore.kernel.org/20260801153333.296218-1-danila@mainlining.org Signed-off-by: Danila Tikhonov Cc: Jakub Kacinski Signed-off-by: Andrew Morton --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index 7324e1af59c2..086f81a5bc1c 100644 --- a/.mailmap +++ b/.mailmap @@ -233,6 +233,8 @@ Daniel Lezcano Daniel Lezcano Daniel Thompson Daniele Alessandrelli +Danila Tikhonov +Danila Tikhonov Danilo Krummrich David Brownell David Collins -- cgit v1.2.3 From a16fd3ad9d89b05475864da97327870464611736 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Mon, 3 Aug 2026 06:40:32 -0700 Subject: samples/damon/mtier: error out for zero quota goal target values Patch series "mm/damon: avoid division by zero from damos_quota_score()". DAMON_SAMPLE_MTIER and DAMON_LRU_SORT allow the user to trigger division by zero in damos_quota_score(). Avoid it by adding parameters validation checks. This patch (of 2): damos_quota_score() can trigger division by zero if the target_value is zero. DAMON_SAMPLE_MTIER lets users set the target_value via node0_mem_{used,free}_bp parameters. It doesn't guard zero value case, though. As a result, users can trigger division by zero. Fix the issue by returning an error when the user tries to start DAMON with zero node0_mem_{used,free}_bp parameter values. DAMON_SAMPLE_MTIER is just a sample module, but the consequence is quite bad. Also the zero node0_mem_free_bp parameter might look like a reasonable setup to some users. Hence, the issue might really happen in the real world. One reliable way to reproduce the issue is like below: # cd /sys/module/damon_sample_mtier/parameters # echo 4096 > node0_start_addr # echo 8192 > node0_end_addr # echo 8192 > node1_start_addr # echo 81920 > node1_end_addr # echo 0 > node0_mem_free_bp # echo Y > enabled # dmesg -w [...] [18792.235916] Oops: divide error: 0000 [#1] SMP NOPTI [...] [18792.242787] RIP: 0010:damos_quota_score+0x6f/0x480 [...] This issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260803134034.15217-1-sj@kernel.org Link: https://lore.kernel.org/20260803134034.15217-2-sj@kernel.org Link: https://lore.kernel.org/20260801202657.117135-1-sj@kernel.org [1] Fixes: c5e67d40a102 ("samples/damon/mtier: add parameters for node0 memory usage") Signed-off-by: SJ Park Cc: # 6.17.x Signed-off-by: Andrew Morton --- samples/damon/mtier.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c index 3785b0c7ffb1..dae6929f827c 100644 --- a/samples/damon/mtier.c +++ b/samples/damon/mtier.c @@ -156,6 +156,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) if (!scheme) goto free_out; damon_set_schemes(ctx, &scheme, 1); + /* zero target value causes division by zero in damos_quota_store() */ + if (!node0_mem_used_bp || !node0_mem_free_bp) + goto free_out; quota_goal = damos_new_quota_goal( promote ? DAMOS_QUOTA_NODE_MEM_USED_BP : DAMOS_QUOTA_NODE_MEM_FREE_BP, -- cgit v1.2.3 From 06befa61c427e74319781e6f35a364cfc32dbae8 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Mon, 3 Aug 2026 06:40:33 -0700 Subject: mm/damon/lru_sort: error out for >10000 active_mem_bp damos_quota_score() can trigger division by zero if the target value is zero. DAMON_LRU_SORT lets users set the target value for the hot memory scheme via active_mem_bp parameter. It avoids setting it as the target value if the parameter value is zero. However, it also sets the cold memory scheme with a target value that is calculated as '10000 - active_mem_bp + 2'. Hence, if a user sets active_mem_bp 10002, the cold memory scheme's quota goal target value can be zero. As a result, division by zero can be triggered. Fix by returning an error when the user tries to start DAMON with >10000 active_mem_bp parameter value. It makes no sense to set active_mem_bp with 10002. It also requires module parameters write permission to reproduce the issue. That said, the consequence is quite bad. One reliable way to reproduce the issue is like below: # cd /sys/module/damon_lru_sort/parameters # echo 1000 > wmarks_high # echo 995 > wmarks_mid # echo 0 > wmarks_low # echo 10002 > active_mem_bp # echo Y > enabled # dmesg -w [...] [ 597.421247] Oops: divide error: 0000 [#1] SMP NOPTI [ 597.428848] RIP: 0010:damos_quota_score+0x6f/0x480 This issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260803134034.15217-3-sj@kernel.org Link: https://lore.kernel.org/20260801213028.5127-1-sj@kernel.org [1] Fixes: 40d98d31cd70 ("mm/damon/lru_sort: support active:inactive memory ratio based auto-tuning") Signed-off-by: SJ Park Cc: # 7.0.x Signed-off-by: Andrew Morton --- mm/damon/lru_sort.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 32f41491b726..120b3fe3fc9d 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -233,6 +233,8 @@ static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme, if (!active_mem_bp) return 0; + if (10000 < active_mem_bp) + return -EINVAL; goal = damos_new_quota_goal(DAMOS_QUOTA_ACTIVE_MEM_BP, active_mem_bp); if (!goal) return -ENOMEM; -- cgit v1.2.3 From b9b6bad94c62cbccb9e0ad34635c49fc5f9c52cb Mon Sep 17 00:00:00 2001 From: SJ Park Date: Mon, 3 Aug 2026 06:46:44 -0700 Subject: mm/damon/reclaim: skip damon_call() if ctx has not started Patch series "mm/damon/{reclaim,lru_sort}: fix commit_inputs infinite hang". Writing 'Y' to commit_inputs parameters of DAMON_RECLAIM and DAMON_LRU_SORT before the modules were ever turned on causes infinite hang. Fix those. The issue was discovered [1] by Sashiko. This patch (of 2): DAMON_RECLAIM calls damon_call() for commit_inputs parameter user input if the DAMON context is initialized. The context could be initialized, but not yet successfully started. In the case, damon_call() could indefinitely hang. Read the comment on damon_call() for more detail. Fix the problem by memorizing if the DAMON context has ever successfully started, and skip damon_call() if it has not. This issue can easily be reproduced by writing Y to commit_inputs on a system that DAMON_RECLAIM was not turned on before. Link: https://lore.kernel.org/20260803134646.16640-1-sj@kernel.org Link: https://lore.kernel.org/20260803134646.16640-2-sj@kernel.org Link: https://lore.kernel.org/20260802173021.762-1-sj@kernel.org [1] Fixes: de3c60e1c831 ("mm/damon: add synchronous commit for commit_inputs") Signed-off-by: SJ Park Cc: Liew Rui Yan Cc: # 7.2.x Signed-off-by: Andrew Morton --- mm/damon/reclaim.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index 11b70d0a9a6f..a1a65b1270c8 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -276,6 +276,8 @@ static int damon_reclaim_commit_inputs_fn(void *arg) return damon_reclaim_apply_parameters(); } +static bool damon_reclaim_damon_has_started; + static int damon_reclaim_commit_inputs_store(const char *val, const struct kernel_param *kp) { @@ -296,11 +298,8 @@ static int damon_reclaim_commit_inputs_store(const char *val, if (!commit_inputs_request) return 0; - /* - * Skip damon_call() if ctx is not initialized to avoid - * NULL pointer dereference. - */ - if (!ctx) + /* Skip damon_call() if ctx has not successfully started. */ + if (!damon_reclaim_damon_has_started) return -EINVAL; err = damon_call(ctx, &control); @@ -347,6 +346,8 @@ static int damon_reclaim_turn(bool on) err = damon_start(&ctx, 1, true); if (err) return err; + if (!damon_reclaim_damon_has_started) + damon_reclaim_damon_has_started = true; return damon_call(ctx, &call_control); } -- cgit v1.2.3 From 0f1868310347f99f1b80d5c6a613ddc747288355 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Mon, 3 Aug 2026 06:46:45 -0700 Subject: mm/damon/lru_sort: skip damon_call() if ctx has not started DAMON_LRU_SORT calls damon_call() for commit_inputs parameter user input if the DAMON context is initialized. The context could be initialized, but not yet successfully started. In the case, damon_call() could indefinitely hang. Read the comment on damon_call() for more detail. Fix the problem by memorizing if the DAMON context has ever successfully started, and skip damon_call() if it has not. This issue can easily be reproduced by writing Y to the commit_inputs parameter file on a system that DAMON_LRU_SORT has not turned on before. Link: https://lore.kernel.org/20260803134646.16640-3-sj@kernel.org Fixes: de3c60e1c831 ("mm/damon: add synchronous commit for commit_inputs") Signed-off-by: SJ Park Cc: Liew Rui Yan Cc: # 7.2.x Signed-off-by: Andrew Morton --- mm/damon/lru_sort.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 120b3fe3fc9d..729d94b8d77e 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -351,6 +351,8 @@ static int damon_lru_sort_commit_inputs_fn(void *arg) return damon_lru_sort_apply_parameters(); } +static bool damon_lru_sort_damon_has_started; + static int damon_lru_sort_commit_inputs_store(const char *val, const struct kernel_param *kp) { @@ -371,11 +373,8 @@ static int damon_lru_sort_commit_inputs_store(const char *val, if (!commit_inputs_request) return 0; - /* - * Skip damon_call() if ctx is not initialized to avoid - * NULL pointer dereference. - */ - if (!ctx) + /* Skip damon_call() if ctx has not successfully started. */ + if (!damon_lru_sort_damon_has_started) return -EINVAL; err = damon_call(ctx, &control); @@ -426,6 +425,8 @@ static int damon_lru_sort_turn(bool on) err = damon_start(&ctx, 1, true); if (err) return err; + if (!damon_lru_sort_damon_has_started) + damon_lru_sort_damon_has_started = true; return damon_call(ctx, &call_control); } -- cgit v1.2.3 From 8db4bab826ccc9ec10fa41736a48031cd338d392 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Thu, 23 Jul 2026 00:48:13 +0800 Subject: mm/page_table_check: skip special zero mappings page_table_check_set() and page_table_check_clear() account mappings based on PageAnon(). Shared zero-page PTEs and huge zero PMDs are special mappings, but page_table_check can still account them as file-backed pages. An unprivileged process can populate enough zero mappings to overflow file_map_count and hit the existing BUG_ON(). The PTE path can do this with the shared zero page, and the PMD path can do the same with huge zero mappings. Skip special zero mappings in the user page-table accounting paths. Keep the PTE-side pte_special() check, and identify huge zero PMDs from the mapped folio instead of pmd_special(). That covers architectures where pmd_special() is a no-op without adding huge_zero_pfn checks to the generic counter helpers. Link: https://lore.kernel.org/cover.1784717203.git.zhilinz@nebusec.ai Link: https://lore.kernel.org/e94478e4fb7912fb7e8ebebed5ce85d00dc9a69d.1784717203.git.zhilinz@nebusec.ai Fixes: df4e817b7108 ("mm: page table check") Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Reported-by: Vega Cc: Pasha Tatashin Assisted-by: Codex:gpt-5.4 Cc: Signed-off-by: Andrew Morton --- mm/page_table_check.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/mm/page_table_check.c b/mm/page_table_check.c index 53a8997ec043..2403f5a11410 100644 --- a/mm/page_table_check.c +++ b/mm/page_table_check.c @@ -151,18 +151,29 @@ void __page_table_check_pte_clear(struct mm_struct *mm, unsigned long addr, if (&init_mm == mm) return; - if (pte_user_accessible_page(mm, addr, pte)) + if (pte_user_accessible_page(mm, addr, pte) && !pte_special(pte)) page_table_check_clear(pte_pfn(pte), PAGE_SIZE >> PAGE_SHIFT); } EXPORT_SYMBOL(__page_table_check_pte_clear); +static inline bool page_table_check_huge_zero_pmd(pmd_t pmd) +{ + unsigned long pfn = pmd_pfn(pmd); + + if (!pfn_valid(pfn)) + return false; + + return is_huge_zero_folio(page_folio(pfn_to_page(pfn))); +} + void __page_table_check_pmd_clear(struct mm_struct *mm, unsigned long addr, pmd_t pmd) { if (&init_mm == mm) return; - if (pmd_user_accessible_page(mm, addr, pmd)) + if (pmd_user_accessible_page(mm, addr, pmd) && + !page_table_check_huge_zero_pmd(pmd)) page_table_check_clear(pmd_pfn(pmd), PMD_SIZE >> PAGE_SHIFT); } EXPORT_SYMBOL(__page_table_check_pmd_clear); @@ -208,7 +219,7 @@ void __page_table_check_ptes_set(struct mm_struct *mm, unsigned long addr, for (i = 0; i < nr; i++) __page_table_check_pte_clear(mm, addr + PAGE_SIZE * i, ptep_get(ptep + i)); - if (pte_user_accessible_page(mm, addr, pte)) + if (pte_user_accessible_page(mm, addr, pte) && !pte_special(pte)) page_table_check_set(pte_pfn(pte), nr, pte_write(pte)); } EXPORT_SYMBOL(__page_table_check_ptes_set); @@ -238,7 +249,8 @@ void __page_table_check_pmds_set(struct mm_struct *mm, unsigned long addr, for (i = 0; i < nr; i++) __page_table_check_pmd_clear(mm, addr + PMD_SIZE * i, *(pmdp + i)); - if (pmd_user_accessible_page(mm, addr, pmd)) + if (pmd_user_accessible_page(mm, addr, pmd) && + !page_table_check_huge_zero_pmd(pmd)) page_table_check_set(pmd_pfn(pmd), stride * nr, pmd_write(pmd)); } EXPORT_SYMBOL(__page_table_check_pmds_set); -- cgit v1.2.3 From aca1f2d5de17e138bc6c4859126b77e516b82541 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sat, 25 Jul 2026 11:14:19 +0100 Subject: mm/huge_memory: initialise workingset state before folio split xas_try_split() adds __GFP_ACCOUNT for page-cache xa_nodes, but __folio_split() leaves the xa_state's xa_lru unset. That lets a live, memcg-charged xa_node exist without being linked into the mapping's shadow_nodes list_lru; when reclaim later walks the list_lru it trips VM_WARN_ON(!css_is_dying()). Use mapping_set_update() to install both the workingset update callback and the shadow_nodes list_lru on the xa_state. Link: https://lore.kernel.org/20260725101419.3938406-1-matt@readmodwrite.com Fixes: 58729c04cf10 ("mm/huge_memory: add buddy allocator like (non-uniform) folio_split()") Signed-off-by: Matt Fleming Reported-by: syzbot+c5b060ce82921a2fd500@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c5b060ce82921a2fd500 Reviewed-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Baolin Wang Cc: Barry Song Cc: Dave Chinner Cc: Dev Jain Cc: Kairui Song Cc: Lance Yang Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Muchun Song Cc: Nico Pache Cc: Roman Gushchin Cc: Ryan Roberts Cc: Shakeel Butt Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index f84fe7913d88..5d94fa4c74fd 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4076,7 +4076,7 @@ static int __folio_split(struct folio *folio, unsigned int new_order, gfp_t gfp; mapping = folio->mapping; - min_order = mapping_min_folio_order(folio->mapping); + min_order = mapping_min_folio_order(mapping); if (new_order < min_order) { ret = -EINVAL; goto out; @@ -4090,6 +4090,8 @@ static int __folio_split(struct folio *folio, unsigned int new_order, goto out; } + mapping_set_update(&xas, mapping); + if (split_type == SPLIT_TYPE_UNIFORM) { xas_set_order(&xas, folio->index, new_order); xas_split_alloc(&xas, folio, old_order, gfp); -- cgit v1.2.3 From 5deb65c34e682e7c5f5df417a70e223e8fcc5f5a Mon Sep 17 00:00:00 2001 From: liyouhong Date: Sun, 26 Jul 2026 09:48:15 +0800 Subject: mm/damon/ops-common: putback folios on invalid migrate nid damon_pa_migrate() and damos_va_migrate() isolate folios into a local list and then call damon_migrate_pages(). When target_nid is invalid (including the scheme default NUMA_NO_NODE / -1), damon_migrate_pages() returns early without putting the folios back to the LRU. Callers then discard the list head while those folios remain isolated with an extra reference taken by folio_isolate_lru(). The pages stay off the LRU for as long as the mapping exists (anon active+inactive counts drop while RSS does not), and the leftover references can pin the pages after the mapping is gone. Put the folios back on the invalid-nid path so ignored migration requests still return them to the LRU. Link: https://lore.kernel.org/20260726014815.1280757-1-dayou5941@163.com Fixes: 7e6c3130690a ("mm/damon/ops-common: ignore migration request to invalid nodes") Assisted-by: Cursor:grok-4.5 Reviewed-by: SJ Park Signed-off-by: liyouhong Cc: Signed-off-by: Andrew Morton --- mm/damon/ops-common.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 6bdd1cfd3863..9c178c175832 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -392,8 +392,15 @@ unsigned long damon_migrate_pages(struct list_head *folio_list, int target_nid) return nr_migrated; if (target_nid < 0 || target_nid >= MAX_NUMNODES || - !node_state(target_nid, N_MEMORY)) + !node_state(target_nid, N_MEMORY)) { + while (!list_empty(folio_list)) { + struct folio *folio = lru_to_folio(folio_list); + + list_del(&folio->lru); + folio_putback_lru(folio); + } return nr_migrated; + } noreclaim_flag = memalloc_noreclaim_save(); -- cgit v1.2.3 From 1ec0e6b6f7321feb769f50d2f094a0aa6c2eda63 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Tue, 28 Jul 2026 07:04:03 -0700 Subject: mm/damon: adjust isolated pages stat for DAMOS_MIGRATE_{HOT,COLD} Callers of migrate_pages() should adjust NR_MIGRATED_{ANON,FILE} for isolations and putback of the folios. That for migration succeeded folios is done by migrate_pages(), in migrate_folio_done(). That for MR_DEMOTION reason is an exception though. DAMOS_MIGRATE_{HOT,COLD} call migrate_pages() but mistakenly not doing the stat adjustment. As a result, use of DAMOS_MIGRATE_{HOT,COLD} could corrupt the stat. It could confuse too_many_isolated(), make compaction and reclaim to behave in unexpected ways. The stat corruption can be reproduced and confirmed using DAMON user-space tool [1] on NUMA systems, like below. $ numactl --hardware available: 2 nodes (0-1) [...] $ sudo ./damo start --damos_action migrate_hot 1 $ sudo cat /proc/sys/vm/stat_refresh $ sudo dmesg [...] [ 80.215554] vmstat_refresh: nr_isolated_anon -5578 [ 80.216842] vmstat_refresh: nr_isolated_file -34400 This issue was discovered [2] by Sashiko. Link: https://lore.kernel.org/20260728140404.94476-1-sj@kernel.org Link: https://github.com/damonitor/damo [1] Link: https://lore.kernel.org/20260726164356.87940-1-sj@kernel.org [2] Fixes: b51820ebea65 ("mm/damon/paddr: introduce DAMOS_MIGRATE_COLD action for demotion") Signed-off-by: SJ Park Cc: Honggyu Kim Cc: Hyeongtak Ji Cc: # 6.11.x Signed-off-by: Andrew Morton --- mm/damon/ops-common.c | 4 ++++ mm/damon/paddr.c | 2 ++ mm/damon/vaddr.c | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 9c178c175832..0bcad6b1e5b9 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -375,6 +375,8 @@ keep: while (!list_empty(folio_list)) { folio = lru_to_folio(folio_list); list_del(&folio->lru); + node_stat_sub_folio(folio, NR_ISOLATED_ANON + + folio_is_file_lru(folio)); folio_putback_lru(folio); } @@ -397,6 +399,8 @@ unsigned long damon_migrate_pages(struct list_head *folio_list, int target_nid) struct folio *folio = lru_to_folio(folio_list); list_del(&folio->lru); + node_stat_sub_folio(folio, NR_ISOLATED_ANON + + folio_is_file_lru(folio)); folio_putback_lru(folio); } return nr_migrated; diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index 5c2da45f988c..f7613ce279a2 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -350,6 +350,8 @@ static unsigned long damon_pa_migrate(struct damon_region *r, if (!folio_isolate_lru(folio)) goto put_folio; + node_stat_add_folio(folio, NR_ISOLATED_ANON + + folio_is_file_lru(folio)); list_add(&folio->lru, &folio_list); put_folio: addr += folio_size(folio); diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index e73ec1ce016e..2c1c1952c008 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -649,7 +649,8 @@ static void damos_va_migrate_dests_add(struct folio *folio, isolate: if (!folio_isolate_lru(folio)) return; - + node_stat_add_folio(folio, NR_ISOLATED_ANON + + folio_is_file_lru(folio)); list_add(&folio->lru, &migration_lists[i]); } -- cgit v1.2.3 From aa2e13ae8d3cbe2c15ef4f7e971b2de0832794aa Mon Sep 17 00:00:00 2001 From: Qing Luo Date: Tue, 4 Aug 2026 10:55:14 +0800 Subject: sctp: fix addip_serial increment on ASCONF_ACK allocation failure In sctp_process_asconf(), when sctp_make_asconf_ack() fails to allocate the ASCONF_ACK chunk due to memory pressure, the code jumps to the done label where asoc->peer.addip_serial is unconditionally incremented. This leaves the peer's ASCONF (serial N) unacknowledged while the local endpoint now expects serial N+1. When the peer retransmits serial N, it falls into the serial < addip_serial + 1 branch , which attempts to look up a cached ACK for serial N. No cached ACK exists since the allocation failed, so the retransmission is silently discarded. The peer eventually times out and ABORTs the association. Move the addip_serial increment inside the if (asconf_ack) block so that the serial number is only advanced when the ASCONF_ACK is successfully created and cached. This way, on allocation failure, the serial number is unchanged and the peer's retransmitted ASCONF will be correctly re-processed. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Qing Luo Acked-by: Xin Long Link: https://patch.msgid.link/20260804025514.241767-1-l1138897701@163.com Signed-off-by: Jakub Kicinski --- net/sctp/sm_make_chunk.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0ae30c3c8913..e25612e9d082 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3336,12 +3336,11 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, goto done; } done: - asoc->peer.addip_serial++; - /* If we are sending a new ASCONF_ACK hold a reference to it in assoc * after freeing the reference to old asconf ack if any. */ if (asconf_ack) { + asoc->peer.addip_serial++; sctp_chunk_hold(asconf_ack); list_add_tail(&asconf_ack->transmitted_list, &asoc->asconf_ack_list); -- cgit v1.2.3 From 6058f0fea10f3caf63a435677358d1b8e9325114 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 25 Jun 2026 22:17:50 -0700 Subject: Input: synaptics-rmi4 - fix F55 transmitter electrode count typo During F55 sensor detection, the transmitter (TX) electrode count was incorrectly assigned the value of the receiver (RX) electrode count due to copy-paste typos. This incorrect value was then propagated to the driver data and used by F54 to determine the diagnostics report size. On devices with more RX than TX electrodes, this inflated the perceived TX count, leading to incorrect report size calculations and potential out-of-bounds buffer accesses. Fix the typos by correctly assigning the TX electrode counts. Fixes: 6adba43fd222 ("Input: synaptics-rmi4 - add support for F55 sensor tuning") Fixes: c762cc68b6a1 ("Input: synaptics-rmi4 - propagate correct number of rx and tx electrodes to F54") Reported-by: sashiko-bot@kernel.org Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260626051802.4033172-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f55.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/rmi4/rmi_f55.c b/drivers/input/rmi4/rmi_f55.c index 488adaca4dd0..a0877d32a914 100644 --- a/drivers/input/rmi4/rmi_f55.c +++ b/drivers/input/rmi4/rmi_f55.c @@ -54,10 +54,10 @@ static int rmi_f55_detect(struct rmi_function *fn) f55->num_tx_electrodes = f55->qry[F55_NUM_TX_OFFSET]; f55->cfg_num_rx_electrodes = f55->num_rx_electrodes; - f55->cfg_num_tx_electrodes = f55->num_rx_electrodes; + f55->cfg_num_tx_electrodes = f55->num_tx_electrodes; drv_data->num_rx_electrodes = f55->cfg_num_rx_electrodes; - drv_data->num_tx_electrodes = f55->cfg_num_rx_electrodes; + drv_data->num_tx_electrodes = f55->cfg_num_tx_electrodes; if (f55->qry[F55_PHYS_CHAR_OFFSET] & F55_CAP_SENSOR_ASSIGN) { int i, total; -- cgit v1.2.3 From dc76c3c8e8ad09362b8c1561f3928288c15cba2e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 25 Jun 2026 22:17:51 -0700 Subject: Input: synaptics-rmi4 - zero report size on F54 work error In rmi_f54_work(), if an error occurs during report request or command verification, the code jumped directly to the 'error' label, bypassing the 'abort' label where f54->report_size was normally zeroed out. This left f54->report_size containing its previous successful payload size. If a user then altered the V4L2 format to a smaller size, and a subsequent run failed, rmi_f54_buffer_queue() would copy the stale, larger payload size into the shrunken V4L2 buffer, causing a heap buffer overflow. Fix this by merging the 'abort' and 'error' labels into a single 'out' exit path, and ensuring that f54->report_size is always set to 0 on failure by checking for error and zeroing the local report_size first. Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics") Cc: stable@vger.kernel.org Reported-by: sashiko-bot@kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260626051802.4033172-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f54.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 61909e1a39e2..8eac320c43e3 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -545,7 +545,7 @@ static void rmi_f54_work(struct work_struct *work) dev_err(&fn->dev, "Bad report size, report type=%d\n", f54->report_type); error = -EINVAL; - goto error; /* retry won't help */ + goto out; /* retry won't help */ } /* @@ -556,7 +556,7 @@ static void rmi_f54_work(struct work_struct *work) &command); if (error) { dev_err(&fn->dev, "Failed to read back command\n"); - goto error; + goto out; } if (command & F54_GET_REPORT) { if (time_after(jiffies, f54->timeout)) { @@ -564,7 +564,7 @@ static void rmi_f54_work(struct work_struct *work) error = -ETIMEDOUT; } report_size = 0; - goto error; + goto out; } rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Get report command completed, reading data\n"); @@ -579,7 +579,7 @@ static void rmi_f54_work(struct work_struct *work) fifo, sizeof(fifo)); if (error) { dev_err(&fn->dev, "Failed to set fifo start offset\n"); - goto abort; + goto out; } error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr + @@ -588,16 +588,16 @@ static void rmi_f54_work(struct work_struct *work) if (error) { dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n", __func__, size, error); - goto abort; + goto out; } } -abort: - f54->report_size = error ? 0 : report_size; -error: +out: if (error) report_size = 0; + f54->report_size = report_size; + if (report_size == 0 && !error) { queue_delayed_work(f54->workqueue, &f54->work, msecs_to_jiffies(1)); -- cgit v1.2.3 From 49c5adc2b7d6e43c5cf033e1c86fdb9c16ababb1 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Thu, 25 Jun 2026 22:17:52 -0700 Subject: Input: synaptics-rmi4 - bound the F54 report size to the allocated buffer rmi_f54_work() reads a diagnostics report from the device into f54->report_data, sizing the transfer with rmi_f54_get_report_size(): report_size = rmi_f54_get_report_size(f54); ... for (i = 0; i < report_size; i += F54_REPORT_DATA_SIZE) { int size = min(F54_REPORT_DATA_SIZE, report_size - i); ... rmi_read_block(.., f54->report_data + i, size); } report_data is allocated once at probe from F54's own electrode counts (array3_size(f54->num_tx_electrodes, f54->num_rx_electrodes, sizeof(u16))), but rmi_f54_get_report_size() computes the size from drv_data->num_*_electrodes when those are set, i.e. from the F55 function's electrode counts. Both counts come straight from device queries (F54 and F55 each report up to 255 electrodes) and nothing constrains the F55 counts to the F54 ones. A malicious or malfunctioning RMI4 device that reports larger F55 electrode counts than its F54 counts makes report_size exceed the allocation, so the read loop writes past report_data (and the V4L2 dequeue memcpy() then reads past it). On conforming hardware the F55 configured electrodes are a subset of the F54 physical electrodes, so report_size never exceeds the buffer and well-behaved devices are unaffected. Record the allocation size and reject a report that does not fit, mirroring the existing zero-size check. Fixes: c762cc68b6a1 ("Input: synaptics-rmi4 - propagate correct number of rx and tx electrodes to F54") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260626051802.4033172-3-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f54.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 8eac320c43e3..75839a54656b 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -104,6 +104,7 @@ struct f54_data { enum rmi_f54_report_type report_type; u8 *report_data; + size_t max_report_size; int report_size; bool is_busy; @@ -548,6 +549,13 @@ static void rmi_f54_work(struct work_struct *work) goto out; /* retry won't help */ } + if (report_size > f54->max_report_size) { + dev_err(&fn->dev, "Report size %d exceeds buffer size %zu\n", + report_size, f54->max_report_size); + error = -EINVAL; + goto out; + } + /* * Need to check if command has completed. * If not try again later. @@ -678,8 +686,8 @@ static int rmi_f54_probe(struct rmi_function *fn) rx = f54->num_rx_electrodes; tx = f54->num_tx_electrodes; - f54->report_data = devm_kzalloc(&fn->dev, - array3_size(tx, rx, sizeof(u16)), + f54->max_report_size = array3_size(tx, rx, sizeof(u16)); + f54->report_data = devm_kzalloc(&fn->dev, f54->max_report_size, GFP_KERNEL); if (f54->report_data == NULL) return -ENOMEM; -- cgit v1.2.3 From fbfd76746adc16d64be29ff113f673b70bc3f5c2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 25 Jun 2026 22:17:54 -0700 Subject: Input: synaptics-rmi4 - block s_input when F54 queue is busy Changing the input (diagnostic report type) mid-stream changes the report size. Since V4L2 buffers are allocated based on the size at stream start, changing the input while streaming could lead to a heap buffer overflow if the new size is larger than the allocated buffers. Prevent this by blocking VIDIOC_S_INPUT with -EBUSY if the V4L2 queue is busy (streaming). Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics") Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Reviewed-by: Hans Verkuil Link: https://patch.msgid.link/20260626051802.4033172-5-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f54.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 75839a54656b..345646612032 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -445,7 +445,12 @@ static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) static int rmi_f54_vidioc_s_input(struct file *file, void *priv, unsigned int i) { - return rmi_f54_set_input(video_drvdata(file), i); + struct f54_data *f54 = video_drvdata(file); + + if (vb2_is_busy(&f54->queue)) + return -EBUSY; + + return rmi_f54_set_input(f54, i); } static int rmi_f54_vidioc_g_input(struct file *file, void *priv, -- cgit v1.2.3 From 69ee44e1a23be62318189dc4b37fa4ad94053269 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Wed, 5 Aug 2026 10:34:41 +0900 Subject: ALSA: usb-audio: fix OOB write on Type II inbound URBs data_ep_set_params() sizes each URB transfer buffer before it adds the Format Type II transfer delimiter: u->packets = urb_packs; u->buffer_size = maxsize * u->packets; if (fmt->fmt_type == UAC_FORMAT_TYPE_II) u->packets++; /* for transfer delimiter */ u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); buffer_size is computed from the pre-increment packet count and never recomputed, so for a Type II endpoint the buffer is one packet short of the packet count the URB is built with. prepare_inbound_urb() then lays out one iso frame per packet and never consults buffer_size: offs = 0; for (i = 0; i < urb_ctx->packets; i++) { urb->iso_frame_desc[i].offset = offs; urb->iso_frame_desc[i].length = ep->curpacksize; offs += ep->curpacksize; } urb->transfer_buffer_length = offs; urb->number_of_packets = urb_ctx->packets; The last descriptor therefore points one packet past the end of the transfer buffer, where the host controller writes device data on every inbound transfer. prepare_silent_urb() and prepare_playback_urb() bound their fill loops by ctx->buffer_size, so only capture is affected. fmt_type comes from the device's audio streaming descriptors, so any device advertising a Type II capture format hits this once userspace sets hw_params on the stream. KASAN on 7.2.0-rc5 (arm64) with a dummy_hcd/raw-gadget device, one report per inbound transfer: BUG: KASAN: slab-out-of-bounds in dummy_timer Write of size 64 at addr ffff0000186171c0 by task cons02/166 __asan_memcpy dummy_timer hrtimer_run_softirq Allocated by task 166: usb_alloc_coherent snd_usb_endpoint_set_params The buggy address is located 0 bytes to the right of allocated 64-byte region [ffff000018617180, ffff0000186171c0) Compute buffer_size after the delimiter packet has been accounted for, and bound the fill loop by buffer_size, as prepare_silent_urb() already does on the outbound side. This grows every Type II URB allocation by one maxsize packet. Discovered by XBOW, triaged by Baul Lee Fixes: 8fdff6a319e7 ("ALSA: snd-usb: implement new endpoint streaming model") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260805013441.38245-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index dca06ba1c67e..a1d449f2a342 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -385,13 +385,15 @@ static int prepare_inbound_urb(struct snd_usb_endpoint *ep, case SND_USB_ENDPOINT_TYPE_DATA: offs = 0; for (i = 0; i < urb_ctx->packets; i++) { + if (offs + ep->curpacksize > urb_ctx->buffer_size) + break; urb->iso_frame_desc[i].offset = offs; urb->iso_frame_desc[i].length = ep->curpacksize; offs += ep->curpacksize; } urb->transfer_buffer_length = offs; - urb->number_of_packets = urb_ctx->packets; + urb->number_of_packets = i; break; case SND_USB_ENDPOINT_TYPE_SYNC: @@ -1243,10 +1245,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) u->index = i; u->ep = ep; u->packets = urb_packs; - u->buffer_size = maxsize * u->packets; if (fmt->fmt_type == UAC_FORMAT_TYPE_II) u->packets++; /* for transfer delimiter */ + u->buffer_size = maxsize * u->packets; u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); if (!u->urb) goto out_of_memory; -- cgit v1.2.3 From 2ca1eea3cd17930daffe9e429a7c89232036ec24 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Wed, 5 Aug 2026 10:34:45 +0900 Subject: ALSA: usx2y: bound the hwdep mmap fault offset snd_us428ctls_vm_fault() turns the faulting page offset into a kernel address with no bound of any kind: offset = vmf->pgoff << PAGE_SHIFT; vaddr = (char *)(...)->us428ctls_sharedmem + offset; page = virt_to_page(vaddr); get_page(page); vmf->page = page; return 0; snd_us428ctls_mmap() checks only the length of the mapping, never the offset, and us428ctls_sharedmem is a single page from alloc_pages_exact(). For a character device file_mmap_size_max() returns ULONG_MAX, so the mm layer imposes no ceiling either. Every page offset above zero resolves to a struct page outside the object, and the handler installs it into the caller's address space read-write; the vma is not marked read-only. The caller picks the page frame with a single mmap() argument and gets read-write access to a page of kernel memory it does not own; an offset that lands in an unpopulated vmemmap region oopses instead. A process that can open the hwdep node of an attached US-X2Y reaches this after loading the FPGA image through the same node; no capability check is involved. On 7.2.0-rc5 (arm64), mmap() with a large offset: Unable to handle kernel paging request at virtual address fffffdffc45d5ac8 pc : snd_us428ctls_vm_fault+0x68/0x140 [snd_usb_usx2y] Call trace: snd_us428ctls_vm_fault+0x68/0x140 [snd_usb_usx2y] __do_fault __handle_mm_fault handle_mm_fault el0_da Reject any offset outside the shared region. The pcm hwdep handler in usx2yhwdeppcm.c computes its address the same way and needs the same bound. Discovered by XBOW, triaged by Baul Lee Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260805013445.38283-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/usx2y/usX2Yhwdep.c | 2 ++ sound/usb/usx2y/usx2yhwdeppcm.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index 4d7925184826..7f153a2fcdd8 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -25,6 +25,8 @@ static vm_fault_t snd_us428ctls_vm_fault(struct vm_fault *vmf) void *vaddr; offset = vmf->pgoff << PAGE_SHIFT; + if (offset >= US428_SHAREDMEM_PAGES) + return VM_FAULT_SIGBUS; vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset; page = virt_to_page(vaddr); get_page(page); diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index 7c90214485d9..f1d5b4b797ad 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -672,6 +672,8 @@ static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf) void *vaddr; offset = vmf->pgoff << PAGE_SHIFT; + if (offset >= USX2Y_HWDEP_PCM_PAGES) + return VM_FAULT_SIGBUS; vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset; vmf->page = virt_to_page(vaddr); get_page(vmf->page); -- cgit v1.2.3 From ff8da20b6f47c48d46e47f93f7a59e2d56ee9107 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 4 Aug 2026 22:09:10 +0200 Subject: Revert "thermal/drivers/hwmon: Cleanup coding style a bit" Revert commit 030a48b0f6ce ("thermal/drivers/hwmon: Cleanup coding style a bit") that introduced a use-after-free into the error path of thermal_add_hwmon_sysfs() by removing a valid check from it. Link: https://lore.kernel.org/linux-hwmon/20260803183915.4ED7D1F000E9@smtp.kernel.org/ Cc: All applicable Signed-off-by: Rafael J. Wysocki Reviewed-by: Lukasz Luba Link: https://patch.msgid.link/5123895.31r3eYUQgx@rafael.j.wysocki --- drivers/thermal/thermal_hwmon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 597c33c8a555..ec73d03a1e60 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -212,7 +212,8 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) if (new_hwmon_device) hwmon_device_unregister(hwmon->device); free_mem: - kfree(hwmon); + if (new_hwmon_device) + kfree(hwmon); return result; } -- cgit v1.2.3 From bc734d167ac5a280455046b7286764e7bb776fa6 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Wed, 5 Aug 2026 17:15:59 +0800 Subject: ASoC: amd: yc: Add DMI quirk for Xiaomi RedmiBook 16 2025 Add a DMI quirk for the Xiaomi RedmiBook 16 2025 (AMD) fixing the issue where the internal microphone was not detected. Link: https://github.com/thesofproject/linux/issues/5860 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260805091600.318018-1-zhangheng@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index b62dd77b588d..78cf082e0d95 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -857,7 +857,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6500RE_M6500RE"), } }, - + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "XIAOMI"), + DMI_MATCH(DMI_BOARD_NAME, "TM2423"), + } + }, {} }; -- cgit v1.2.3 From 1b0e5c7a4d90a1390616e8264d5ca50f700fde3d Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Wed, 5 Aug 2026 17:16:00 +0800 Subject: ASoC: amd: yc: Add DMI quirk for MSI Raider A18 HX A7VHG Add a DMI quirk for the MSI Raider A18 HX A7VHG fixing the issue where the internal microphone was not detected. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221574 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260805091600.318018-2-zhangheng@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 78cf082e0d95..14b970f16020 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -535,6 +535,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Crosshair A16 HX D7WFKG"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Raider A18 HX A7VHG"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From d74aac116cfb2058b15df53996d23232b310f7ff Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 11:24:11 +0300 Subject: ASoC: cs35l41: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). cs35l41_reg[] lists CS35L41_BSTCVRT_PEAK_CUR (0x3808) after CS35L41_BSTCVRT_COEFF (0x3810) and CS35L41_BSTCVRT_SLOPE_LBST (0x3814), so the binary search does not find those two entries. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 5f2f539901b0 ("ASoC: cs35l41: Correct handling of some registers in the cache") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Charles Keepax Reviewed-by: Richard Fitzgerald Link: https://patch.msgid.link/20260805082413.26174-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41-lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 1702f26049d3..b0fa80705be7 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -25,9 +25,9 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, { CS35L41_TST_FS_MON0, 0x00020016 }, + { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A }, { CS35L41_BSTCVRT_COEFF, 0x00002424 }, { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 }, - { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A }, { CS35L41_SP_ENABLES, 0x00000000 }, { CS35L41_SP_RATE_CTRL, 0x00000028 }, { CS35L41_SP_FORMAT, 0x18180200 }, -- cgit v1.2.3 From f39a68ed08bb6eef0ae711b41d645ee5e9448c09 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 11:24:12 +0300 Subject: ASoC: cs35l45: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). cs35l45_defaults[] lists the DSP1_RX*_RATE and DSP1_TX*_RATE registers (0x02b80080 - 0x02b802b8) in the middle of the table, ahead of entries with much lower addresses, so the binary search does not find 36 of its 73 entries. regcache_reg_needs_sync() then cannot compare those against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: 74b14e2850a3 ("ASoC: cs35l45: DSP Support") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Charles Keepax Reviewed-by: Richard Fitzgerald Link: https://patch.msgid.link/20260805082413.26174-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l45-tables.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c index d2ecc7b3f619..764dbaa35042 100644 --- a/sound/soc/codecs/cs35l45-tables.c +++ b/sound/soc/codecs/cs35l45-tables.c @@ -66,22 +66,6 @@ static const struct reg_default cs35l45_defaults[] = { { CS35L45_ASPTX3_INPUT, 0x00000020 }, { CS35L45_ASPTX4_INPUT, 0x00000028 }, { CS35L45_ASPTX5_INPUT, 0x00000048 }, - { CS35L45_DSP1_RX1_RATE, 0x00000001 }, - { CS35L45_DSP1_RX2_RATE, 0x00000001 }, - { CS35L45_DSP1_RX3_RATE, 0x00000001 }, - { CS35L45_DSP1_RX4_RATE, 0x00000001 }, - { CS35L45_DSP1_RX5_RATE, 0x00000001 }, - { CS35L45_DSP1_RX6_RATE, 0x00000001 }, - { CS35L45_DSP1_RX7_RATE, 0x00000001 }, - { CS35L45_DSP1_RX8_RATE, 0x00000001 }, - { CS35L45_DSP1_TX1_RATE, 0x00000001 }, - { CS35L45_DSP1_TX2_RATE, 0x00000001 }, - { CS35L45_DSP1_TX3_RATE, 0x00000001 }, - { CS35L45_DSP1_TX4_RATE, 0x00000001 }, - { CS35L45_DSP1_TX5_RATE, 0x00000001 }, - { CS35L45_DSP1_TX6_RATE, 0x00000001 }, - { CS35L45_DSP1_TX7_RATE, 0x00000001 }, - { CS35L45_DSP1_TX8_RATE, 0x00000001 }, { CS35L45_DSP1RX1_INPUT, 0x00000008 }, { CS35L45_DSP1RX2_INPUT, 0x00000009 }, { CS35L45_DSP1RX3_INPUT, 0x00000018 }, @@ -114,6 +98,22 @@ static const struct reg_default cs35l45_defaults[] = { { CS35L45_GPIO1_CTRL1, 0x81000001 }, { CS35L45_GPIO2_CTRL1, 0x81000001 }, { CS35L45_GPIO3_CTRL1, 0x81000001 }, + { CS35L45_DSP1_RX1_RATE, 0x00000001 }, + { CS35L45_DSP1_RX2_RATE, 0x00000001 }, + { CS35L45_DSP1_RX3_RATE, 0x00000001 }, + { CS35L45_DSP1_RX4_RATE, 0x00000001 }, + { CS35L45_DSP1_RX5_RATE, 0x00000001 }, + { CS35L45_DSP1_RX6_RATE, 0x00000001 }, + { CS35L45_DSP1_RX7_RATE, 0x00000001 }, + { CS35L45_DSP1_RX8_RATE, 0x00000001 }, + { CS35L45_DSP1_TX1_RATE, 0x00000001 }, + { CS35L45_DSP1_TX2_RATE, 0x00000001 }, + { CS35L45_DSP1_TX3_RATE, 0x00000001 }, + { CS35L45_DSP1_TX4_RATE, 0x00000001 }, + { CS35L45_DSP1_TX5_RATE, 0x00000001 }, + { CS35L45_DSP1_TX6_RATE, 0x00000001 }, + { CS35L45_DSP1_TX7_RATE, 0x00000001 }, + { CS35L45_DSP1_TX8_RATE, 0x00000001 }, }; static bool cs35l45_readable_reg(struct device *dev, unsigned int reg) -- cgit v1.2.3 From e4fe3e046524e5de3c04c6eef3743780cbdc231c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 11:24:13 +0300 Subject: ASoC: cs4265: sort the register default table reg_defaults must be sorted by ascending register address, as regcache_lookup_reg() locates entries in it with bsearch(). See commit fd80df352ba1 ("regcache: Add support for sorting defaults arrays"). cs4265_reg_defaults[] lists CS4265_INT_MASK (0x0e), CS4265_STATUS_MODE_MSB (0x0f) and CS4265_STATUS_MODE_LSB (0x10) after CS4265_SPDIF_CTL1 (0x11) and CS4265_SPDIF_CTL2 (0x12), so the binary search does not find those three entries. regcache_reg_needs_sync() then cannot compare them against their default and reports that a sync is needed, so they are written to the device on every regcache_sync() even when they were never touched. Sort the table by register address. Fixes: fb6f806967f6 ("ASoC: Add support for the CS4265 CODEC") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Reviewed-by: Charles Keepax Reviewed-by: Richard Fitzgerald Link: https://patch.msgid.link/20260805082413.26174-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs4265.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 286d5ca29854..0b7d6826f4e9 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -46,11 +46,11 @@ static const struct reg_default cs4265_reg_defaults[] = { { CS4265_DAC_CHA_VOL, 0x00 }, { CS4265_DAC_CHB_VOL, 0x00 }, { CS4265_DAC_CTL2, 0xC0 }, - { CS4265_SPDIF_CTL1, 0x00 }, - { CS4265_SPDIF_CTL2, 0x00 }, { CS4265_INT_MASK, 0x00 }, { CS4265_STATUS_MODE_MSB, 0x00 }, { CS4265_STATUS_MODE_LSB, 0x00 }, + { CS4265_SPDIF_CTL1, 0x00 }, + { CS4265_SPDIF_CTL2, 0x00 }, }; static bool cs4265_readable_register(struct device *dev, unsigned int reg) -- cgit v1.2.3 From 5004889551dfaf7f4b2cda8ed213d234bb8ebf82 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 3 Aug 2026 10:33:34 +0200 Subject: s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Both CPRB alloc functions in zcrypt_ccamisc.c and zcrypt_ep11misc.c did not round up the memory allocation to a multiple of 4 bytes as it is needed by the zcrypt layer to process the CPRBs. Now the alloc_and_prep_cprbmem() and alloc_cprbmem() functions guarantee that the base CPRB struct and a possible parameter block are aligned to a 4-byte boundary and the backing memory allocation is rounded up to the next multiple of 4 byte. Also the free_cprbmem() is updated and scrubs the rounded up amount of memory. Fixes: 9bdb5f7e8369 ("s390/zcrypt: Introduce cprb mempool for cca misc functions") Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Cc: stable@vger.kernel.org # 6.16+ Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_ccamisc.c | 20 ++++++++++++++------ drivers/s390/crypto/zcrypt_ep11misc.c | 16 +++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 86d2ee78c9f4..d4ce6352b5b2 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -267,6 +268,10 @@ EXPORT_SYMBOL(cca_check_sececckeytoken); * block, reply CPRB and reply param block and fill in values * for the common fields. Returns 0 on success or errno value * on failure. + * It is guaranteed that request and a possible param block + * are aligned to a 4 byte boundary. Furthermore if a param + * block is used, the memory allocated for this is rounded up to + * the next multiple of 4 bytes. */ static int alloc_and_prep_cprbmem(size_t paramblen, u8 **p_cprb_mem, @@ -275,7 +280,8 @@ static int alloc_and_prep_cprbmem(size_t paramblen, u32 xflags) { u8 *cprbmem = NULL; - size_t cprbplusparamblen = sizeof(struct CPRBX) + paramblen; + size_t cprbplusparamblen = + ALIGN(sizeof(struct CPRBX), 4) + ALIGN(paramblen, 4); size_t len = 2 * cprbplusparamblen; struct CPRBX *preqcblk, *prepcblk; @@ -302,10 +308,10 @@ static int alloc_and_prep_cprbmem(size_t paramblen, memcpy(preqcblk->func_id, "T2", 2); preqcblk->rpl_msgbl = cprbplusparamblen; if (paramblen) { - preqcblk->req_parmb = - ((u8 __user *)preqcblk) + sizeof(struct CPRBX); - preqcblk->rpl_parmb = - ((u8 __user *)prepcblk) + sizeof(struct CPRBX); + preqcblk->req_parmb = ((u8 __user *)preqcblk) + + ALIGN(sizeof(struct CPRBX), 4); + preqcblk->rpl_parmb = ((u8 __user *)prepcblk) + + ALIGN(sizeof(struct CPRBX), 4); } *p_cprb_mem = cprbmem; @@ -323,8 +329,10 @@ static int alloc_and_prep_cprbmem(size_t paramblen, */ static void free_cprbmem(void *mem, size_t paramblen, bool scrub, u32 xflags) { + size_t cprblen = ALIGN(sizeof(struct CPRBX), 4) + ALIGN(paramblen, 4); + if (mem && scrub) - memzero_explicit(mem, 2 * (sizeof(struct CPRBX) + paramblen)); + memzero_explicit(mem, 2 * cprblen); if (xflags & ZCRYPT_XFLAG_NOMEMALLOC) mempool_free(mem, cprb_mempool); diff --git a/drivers/s390/crypto/zcrypt_ep11misc.c b/drivers/s390/crypto/zcrypt_ep11misc.c index 3dda9589f2b9..2d900ffc5068 100644 --- a/drivers/s390/crypto/zcrypt_ep11misc.c +++ b/drivers/s390/crypto/zcrypt_ep11misc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -355,21 +356,24 @@ EXPORT_SYMBOL(ep11_check_aes_key); /* * Allocate and prepare ep11 cprb plus additional payload. + * It is guaranteed that the memory is aligned to a 4 byte boundary. + * Furthermore the memory allocation is rounded up to the next + * multiple of 4 bytes (with taking the payload_len into account). */ static void *alloc_cprbmem(size_t payload_len, u32 xflags) { - size_t len = sizeof(struct ep11_cprb) + payload_len; + size_t memlen = ALIGN(sizeof(struct ep11_cprb) + payload_len, 4); struct ep11_cprb *cprb = NULL; if (xflags & ZCRYPT_XFLAG_NOMEMALLOC) { - if (len <= CPRB_MEMPOOL_ITEM_SIZE) + if (memlen <= CPRB_MEMPOOL_ITEM_SIZE) cprb = mempool_alloc_preallocated(cprb_mempool); } else { - cprb = kmalloc(len, GFP_KERNEL); + cprb = kmalloc(memlen, GFP_KERNEL); } if (!cprb) return NULL; - memset(cprb, 0, len); + memset(cprb, 0, memlen); cprb->cprb_len = sizeof(struct ep11_cprb); cprb->cprb_ver_id = 0x04; @@ -385,8 +389,10 @@ static void *alloc_cprbmem(size_t payload_len, u32 xflags) */ static void free_cprbmem(void *mem, size_t payload_len, bool scrub, u32 xflags) { + size_t memlen = ALIGN(sizeof(struct ep11_cprb) + payload_len, 4); + if (mem && scrub) - memzero_explicit(mem, sizeof(struct ep11_cprb) + payload_len); + memzero_explicit(mem, memlen); if (xflags & ZCRYPT_XFLAG_NOMEMALLOC) mempool_free(mem, cprb_mempool); -- cgit v1.2.3 From 35ac6754ba6da45fd193f5ce3f665f7490b830d5 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 3 Aug 2026 10:33:35 +0200 Subject: s390/zcrypt: Improve CCA CPRB length and overflow checks The xcrb_msg_to_type6cprb_msgx() function lacks proper input validation, creating security vulnerabilities: 1. Integer overflow after CEIL4 alignment: Signed int variables could overflow during 4-byte boundary alignment, causing undersized buffer allocations or incorrect bounds checking. 2. Missing minimum size validation: The CPRBX structure is copied from userspace without verifying sufficient buffer length. Undersized buffers cause uninitialized memory access when reading structure fields like cprbx.cprb_len and cprbx.domain. 3. Arithmetic overflow in sum calculations: Adding control block and data block sizes could overflow, bypassing size checks and enabling buffer overflows. Fix by using size_t for length calculations, adding U32_MAX boundary checks after alignment, validating minimum control block size before copying from userspace, and detecting sum calculation overflows. Fixes: e2c6d91eb8b1 ("s390/zcrypt: Rework domain processing within zcrypt device driver") Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Cc: stable@vger.kernel.org # 7.1+ Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_msgtype6.c | 79 ++++++++++++++++------------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index 40f72cdf284d..3e19e79d747c 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -342,49 +342,39 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, }; } __packed * msg = ap_msg->msg; - int rcblen = CEIL4(xcrb->request_control_blk_length); - int req_sumlen, resp_sumlen; - char *req_data = ap_msg->msg + sizeof(struct type6_hdr) + rcblen; - char *function_code; + size_t req_cblen, rep_cblen, req_sumlen, rep_sumlen; + char *function_code, *req_data; - if (CEIL4(xcrb->request_control_blk_length) < - xcrb->request_control_blk_length) - return -EINVAL; /* overflow after alignment*/ - - /* length checks */ - ap_msg->len = sizeof(struct type6_hdr) + - CEIL4(xcrb->request_control_blk_length) + - xcrb->request_data_length; + /* request length and overflow checks */ + if (xcrb->request_control_blk_length < sizeof(struct CPRBX)) + return -EINVAL; + req_cblen = CEIL4((size_t)xcrb->request_control_blk_length); + if (req_cblen > U32_MAX) + return -EINVAL; + req_sumlen = req_cblen + xcrb->request_data_length; + if (req_sumlen > U32_MAX) + return -EINVAL; + ap_msg->len = sizeof(struct type6_hdr) + req_sumlen; if (ap_msg->len > ap_msg->bufsize) return -EINVAL; - - /* - * Overflow check - * sum must be greater (or equal) than the largest operand - */ - req_sumlen = CEIL4(xcrb->request_control_blk_length) + - xcrb->request_data_length; - if ((CEIL4(xcrb->request_control_blk_length) <= - xcrb->request_data_length) ? + if (req_cblen <= xcrb->request_data_length ? req_sumlen < xcrb->request_data_length : - req_sumlen < CEIL4(xcrb->request_control_blk_length)) { + req_sumlen < req_cblen) { return -EINVAL; } - if (CEIL4(xcrb->reply_control_blk_length) < - xcrb->reply_control_blk_length) - return -EINVAL; /* overflow after alignment*/ - - /* - * Overflow check - * sum must be greater (or equal) than the largest operand - */ - resp_sumlen = CEIL4(xcrb->reply_control_blk_length) + - xcrb->reply_data_length; - if ((CEIL4(xcrb->reply_control_blk_length) <= - xcrb->reply_data_length) ? - resp_sumlen < xcrb->reply_data_length : - resp_sumlen < CEIL4(xcrb->reply_control_blk_length)) { + /* reply length and overflow checks */ + if (xcrb->reply_control_blk_length < sizeof(struct CPRBX)) + return -EINVAL; + rep_cblen = CEIL4((size_t)xcrb->reply_control_blk_length); + if (rep_cblen > U32_MAX) + return -EINVAL; + rep_sumlen = rep_cblen + xcrb->reply_data_length; + if (rep_sumlen > U32_MAX) + return -EINVAL; + if (rep_cblen <= xcrb->reply_data_length ? + rep_sumlen < xcrb->reply_data_length : + rep_sumlen < rep_cblen) { return -EINVAL; } @@ -393,7 +383,7 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, memcpy(msg->hdr.agent_id, &xcrb->agent_ID, sizeof(xcrb->agent_ID)); msg->hdr.tocardlen1 = xcrb->request_control_blk_length; if (xcrb->request_data_length) { - msg->hdr.offset2 = msg->hdr.offset1 + rcblen; + msg->hdr.offset2 = msg->hdr.offset1 + req_cblen; msg->hdr.tocardlen2 = xcrb->request_data_length; } msg->hdr.fromcardlen1 = xcrb->reply_control_blk_length; @@ -404,8 +394,8 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, xcrb->request_control_blk_addr, xcrb->request_control_blk_length)) return -EFAULT; - if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) > - xcrb->request_control_blk_length) + /* copy subfunction code into AP msg type 6 function code field */ + if (msg->cprbx.cprb_len > req_cblen - sizeof(msg->hdr.function_code)) return -EINVAL; function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len; memcpy(msg->hdr.function_code, function_code, @@ -437,10 +427,13 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, } /* copy data block */ - if (xcrb->request_data_length && - z_copy_from_user(userspace, req_data, xcrb->request_data_address, - xcrb->request_data_length)) - return -EFAULT; + if (xcrb->request_data_length) { + req_data = ap_msg->msg + sizeof(struct type6_hdr) + req_cblen; + if (z_copy_from_user(userspace, req_data, + xcrb->request_data_address, + xcrb->request_data_length)) + return -EFAULT; + } return 0; } -- cgit v1.2.3 From 17ac0bc866fc624cd05f022dcd8b730c0af11bb1 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 3 Aug 2026 10:33:36 +0200 Subject: s390/zcrypt: Improve EP11 CPRB length and overflow checks The xcrb_msg_to_type6_ep11cprb_msgx() function lacks proper input validation, creating security vulnerabilities: 1. Missing minimum size validation: The ep11_cprb structure and subsequent payload fields (pld_tag, pld_lenfmt) are copied from userspace without verifying sufficient buffer length. 2. Arithmetic overflow in length calculations: CEIL4 alignment could overflow, bypassing size checks and enabling buffer overflows. 3. The payload is asn1 encoded but the function just uses a simple c struct overlay to access some fields of the payload. Fix by using size_t for length calculations, adding U32_MAX boundary checks after alignment, and validating minimum request size and minimum reply size before copying from userspace. Do a very simple asn1 parsing of the payload up to the function value field. Fixes: e2c6d91eb8b1 ("s390/zcrypt: Rework domain processing within zcrypt device driver") Signed-off-by: Harald Freudenberger Cc: stable@vger.kernel.org # 7.1+ Reviewed-by: Holger Dengler Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_msgtype6.c | 152 +++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 39 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index 3e19e79d747c..7e1f76c935ee 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "ap_bus.h" #include "zcrypt_api.h" @@ -34,6 +35,9 @@ #define CEXXC_RESPONSE_TYPE_XCRB 1 #define CEXXC_RESPONSE_TYPE_EP11 2 +/* smallest possible EP11 payload size */ +#define MIN_EP11_PAYLOAD_SIZE 5 + MODULE_AUTHOR("IBM Corporation"); MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \ "Copyright IBM Corp. 2001, 2023"); @@ -438,12 +442,59 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, return 0; } +/* + * Simple asn1 int reader/decoder helper function + * Returns number of bytes processed or < 0 on failure + * Only accepts int length values of 1, 2 or 4. + */ +static inline int asn1_int_decode(const u8 *buf, size_t intlen, u32 *u) +{ + switch (intlen) { + case 1: + *u = (u32)(*buf); + return 1; + case 2: + *u = (u32)get_unaligned_be16(buf); + return 2; + case 4: + *u = (u32)get_unaligned_be32(buf); + return 4; + default: + return -EINVAL; + } +} + +/* + * Simple asn1 length parse helper function + * Returns number of bytes processed or < 0 on failure + * Only accepts length encoded within the length octet + * or for long form 1, 2 or 4 octet length bytes. + */ +static inline int asn1_length_decode(const u8 *buf, size_t buflen, u32 *u) +{ + int i; + + if (buflen < 1) + return -EINVAL; + + if (*buf < 128) { + *u = (u32)(*buf & 0x7F); + return 1; + } + + i = *buf & 0x7F; + if (--buflen < i) + return -EINVAL; + i = asn1_int_decode(++buf, i, u); + + return i < 0 ? i : i + 1; +} + static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap_msg, struct ep11_urb *xcrb, unsigned int *fcode, unsigned int *domain) { - unsigned int lfmt; static struct type6_hdr static_type6_ep11_hdr = { .type = 0x06, .rqid = {0x00, 0x01}, @@ -455,34 +506,32 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap struct { struct type6_hdr hdr; union { - struct { - struct ep11_cprb cprbx; - unsigned char pld_tag; /* fixed value 0x30 */ - unsigned char pld_lenfmt; /* length format */ - } __packed; + struct ep11_cprb cprbx; DECLARE_FLEX_ARRAY(u8, userdata); }; } __packed * msg = ap_msg->msg; - struct pld_hdr { - unsigned char func_tag; /* fixed value 0x4 */ - unsigned char func_len; /* fixed value 0x4 */ - unsigned int func_val; /* function ID */ - unsigned char dom_tag; /* fixed value 0x4 */ - unsigned char dom_len; /* fixed value 0x4 */ - unsigned int dom_val; /* domain id */ - } __packed * payload_hdr = NULL; - - if (CEIL4(xcrb->req_len) < xcrb->req_len) - return -EINVAL; /* overflow after alignment*/ + size_t req_len, rep_len, pld_len; + unsigned char *pld; + int offs = 0, i; + unsigned int u; - /* length checks */ - ap_msg->len = sizeof(struct type6_hdr) + CEIL4(xcrb->req_len); + /* request length and overflow checks */ + if (xcrb->req_len < sizeof(struct ep11_cprb) + MIN_EP11_PAYLOAD_SIZE) + return -EINVAL; + req_len = CEIL4(xcrb->req_len); + if (req_len < xcrb->req_len || req_len > U32_MAX) + return -EINVAL; + ap_msg->len = sizeof(struct type6_hdr) + req_len; if (ap_msg->len > ap_msg->bufsize) return -EINVAL; - if (CEIL4(xcrb->resp_len) < xcrb->resp_len) - return -EINVAL; /* overflow after alignment*/ + /* reply length and overflow checks */ + if (xcrb->resp_len < sizeof(struct ep11_cprb)) + return -EINVAL; + rep_len = CEIL4(xcrb->resp_len); + if (rep_len < xcrb->resp_len || rep_len > U32_MAX) + return -EINVAL; /* prepare type6 header */ msg->hdr = static_type6_ep11_hdr; @@ -491,26 +540,51 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap /* Import CPRB data from the ioctl input parameter */ if (z_copy_from_user(userspace, msg->userdata, - (char __force __user *)xcrb->req, xcrb->req_len)) { + (char __force __user *)xcrb->req, xcrb->req_len)) return -EFAULT; - } - if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/ - switch (msg->pld_lenfmt & 0x03) { - case 1: - lfmt = 2; - break; - case 2: - lfmt = 3; - break; - default: - return -EINVAL; - } - } else { - lfmt = 1; /* length format #1 */ - } - payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt); - *fcode = payload_hdr->func_val & 0xFFFF; + pld = msg->userdata + sizeof(struct ep11_cprb); + pld_len = msg->cprbx.payload_len; + if (pld_len != xcrb->req_len - sizeof(struct ep11_cprb)) + return -EINVAL; + /* + * At this point pld_len is always >= MIN_EP11_PAYLOAD_SIZE + * and the smallest supported asn1 payload is: + * payload tag (1 octet) + * payload length (1-5 octets) + * function tag (1 octet) + * function length (1-5 octets) + * function value (1-4 octets) + */ + + /* payload tag */ + if (pld[offs++] != 0x30) + return -EINVAL; + /* payload length field */ + i = asn1_length_decode(pld + offs, pld_len - offs, &u); + if (i < 0) + return -EINVAL; + offs += i; + if (offs >= pld_len || u > pld_len - offs) + return -EINVAL; + /* function tag */ + if (pld[offs++] != 0x04) + return -EINVAL; + /* function length */ + if (offs >= pld_len) + return -EINVAL; + i = asn1_length_decode(pld + offs, pld_len - offs, &u); + if (i < 0) + return -EINVAL; + offs += i; + if (offs >= pld_len || u > pld_len - offs) + return -EINVAL; + /* function value */ + i = asn1_int_decode(pld + offs, u, &u); + if (i < 0) + return -EINVAL; + offs += i; + *fcode = 0xFFFF & u; /* enable special processing based on the cprbs flags special bit */ if (msg->cprbx.flags & 0x20) -- cgit v1.2.3 From 0864a163783bff109b548266921829ea794edc93 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 3 Aug 2026 10:33:37 +0200 Subject: s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing The zcrypt_msgtype6_send_ep11_cprb() function uses fragile struct overlays to access and modify the domain field in the EP11 CPRB payload, creating maintainability and security concerns: 1. Struct overlay approach (pld_hdr) assumes fixed payload structure and doesn't validate the actual ASN.1 encoding. 2. Complex length format detection logic is error-prone and doesn't properly validate bounds at each parsing step. 3. Direct struct member access bypasses proper ASN.1 validation. Fix by replacing struct overlays with explicit ASN.1 parsing that validates each field (payload tag/length, function tag/length/value, optional domain tag/length/value) with proper bounds checking at every step. Add asn1_int_encode() helper function to safely write integer values with correct endianness conversion. This makes the code consistent with the validation pattern introduced with the rework of the xcrb_msg_to_type6_ep11cprb_msgx() function. Fixes: e2c6d91eb8b1 ("s390/zcrypt: Rework domain processing within zcrypt device driver") Signed-off-by: Harald Freudenberger Cc: stable@vger.kernel.org # 7.1+ Reviewed-by: Holger Dengler Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_msgtype6.c | 127 +++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 31 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index 7e1f76c935ee..e79e68b4dd41 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -1226,6 +1226,28 @@ int prep_ep11_ap_msg(bool userspace, struct ep11_urb *xcrb, func_code, domain); } +/* + * Simple asn1 int writer/encoder helper function + * Returns number of bytes processed or < 0 on failure + * Only accepts int length values of 1, 2 or 4. + */ +static inline int asn1_int_encode(u8 *buf, size_t intlen, u32 u) +{ + switch (intlen) { + case 1: + *buf = (u8)u; + return 1; + case 2: + put_unaligned_be16((u16)u, buf); + return 2; + case 4: + put_unaligned_be32((u32)u, buf); + return 4; + default: + return -EINVAL; + } +} + /* * The request distributor calls this function if it picked the CEX4P * device to handle a send_ep11_cprb request. @@ -1238,51 +1260,94 @@ static long zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue * struct ap_message *ap_msg) { int rc; - unsigned int lfmt; struct ap_response_type *resp_type = &ap_msg->response; struct { struct type6_hdr hdr; struct ep11_cprb cprbx; - unsigned char pld_tag; /* fixed value 0x30 */ - unsigned char pld_lenfmt; /* payload length format */ } __packed * msg = ap_msg->msg; - struct pld_hdr { - unsigned char func_tag; /* fixed value 0x4 */ - unsigned char func_len; /* fixed value 0x4 */ - unsigned int func_val; /* function ID */ - unsigned char dom_tag; /* fixed value 0x4 */ - unsigned char dom_len; /* fixed value 0x4 */ - unsigned int dom_val; /* domain id */ - } __packed * payload_hdr = NULL; /* * The target domain field within the cprb body/payload block will be * replaced by the usage domain for non-management commands only. * Therefore we check the first bit of the 'flags' parameter for * management command indication. - * 0 - non management command - * 1 - management command */ - if (!((msg->cprbx.flags & 0x80) == 0x80)) { - msg->cprbx.target_id = (unsigned int) - AP_QID_QUEUE(zq->queue->qid); - - if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/ - switch (msg->pld_lenfmt & 0x03) { - case 1: - lfmt = 2; - break; - case 2: - lfmt = 3; - break; - default: + if (!(msg->cprbx.flags & 0x80)) { + int i, offs = 0; + size_t pld_len; + u8 *pld; + u32 u; + + /* update target field in ep11_cprb */ + msg->cprbx.target_id = (u32)AP_QID_QUEUE(zq->queue->qid); + + /* ptr and length to payload */ + pld = ap_msg->msg + + sizeof(struct type6_hdr) + sizeof(struct ep11_cprb); + pld_len = msg->cprbx.payload_len; + if (pld_len < MIN_EP11_PAYLOAD_SIZE) + return -EINVAL; + + /* + * Parse the asn1 payload, at least we have + * pld tag (1 octet) + * payload length (1-5 octets) + * function tag (1 octet) + * function length (1-5 octets) + * function value (1-4 octets) + * ----- optional fields ----- + * domain tag (1 octet) + * domain length (1-5 octets) + * domain value (1-4 octets) + * ... maybe much more data ... + */ + + /* payload tag */ + if (pld[offs++] != 0x30) + return -EINVAL; + /* payload length field */ + i = asn1_length_decode(pld + offs, pld_len - offs, &u); + if (i < 0) + return -EINVAL; + offs += i; + if (offs >= pld_len || u > pld_len - offs) + return -EINVAL; + /* function tag */ + if (pld[offs++] != 0x04) + return -EINVAL; + /* function length */ + if (offs >= pld_len) + return -EINVAL; + i = asn1_length_decode(pld + offs, pld_len - offs, &u); + if (i < 0) + return -EINVAL; + offs += i; + if (u > pld_len - offs) + return -EINVAL; + /* skip over the function value */ + offs += u; + /* is there some payload left which could hold a domain value ? */ + if (offs < pld_len && pld_len - offs >= 3) { + /* domain tag */ + if (pld[offs++] != 0x04) return -EINVAL; - } - } else { - lfmt = 1; /* length format #1 */ + /* domain length */ + i = asn1_length_decode(pld + offs, pld_len - offs, &u); + if (i < 0) + return -EINVAL; + offs += i; + if (offs >= pld_len || u > pld_len - offs) + return -EINVAL; + /* + * pld[offs] is now at the start of the domain value + * with the value sprawled in u octets. + */ + i = asn1_int_encode(pld + offs, u, + AP_QID_QUEUE(zq->queue->qid)); + if (i < 0) + return -EINVAL; + offs += i; } - payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt); - payload_hdr->dom_val = AP_QID_QUEUE(zq->queue->qid); } /* -- cgit v1.2.3 From eb363254472493e3458156fc11fd56dca92f4333 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 3 Aug 2026 10:33:38 +0200 Subject: s390/zcrypt: Pad trailing CCA or EP11 message with zeros The both functions xcrb_msg_to_type6cprb_msgx() and xcrb_msg_to_type6_ep11cprb_msgx() copy the user space message into a kernel buffer based on the message length. But on further processing the message is supposed to be 4 byte length adjusted. Thus up to 3 bytes of uninitialized kernel memory are forwarded to further processing steps and may unwanted expose kernel memory to the crypto card firmware. This patch contains code to pad the gap between user space copied message and message buffer length sent down to further processing of the CCA or EP11 message to zeros. Fixes: e2c6d91eb8b1 ("s390/zcrypt: Rework domain processing within zcrypt device driver") Signed-off-by: Harald Freudenberger Reviewed-by: Holger Dengler Cc: stable@vger.kernel.org # 7.1+ Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_msgtype6.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index e79e68b4dd41..3df1d676de5d 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -398,6 +398,10 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, xcrb->request_control_blk_addr, xcrb->request_control_blk_length)) return -EFAULT; + /* pad tail with 0 up to req_cblen */ + if (xcrb->request_control_blk_length < req_cblen) + memset(msg->userdata + xcrb->request_control_blk_length, + 0, req_cblen - xcrb->request_control_blk_length); /* copy subfunction code into AP msg type 6 function code field */ if (msg->cprbx.cprb_len > req_cblen - sizeof(msg->hdr.function_code)) return -EINVAL; @@ -542,6 +546,10 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap if (z_copy_from_user(userspace, msg->userdata, (char __force __user *)xcrb->req, xcrb->req_len)) return -EFAULT; + /* pad tail with 0 up to req_len */ + if (xcrb->req_len < req_len) + memset(msg->userdata + xcrb->req_len, 0, + req_len - xcrb->req_len); pld = msg->userdata + sizeof(struct ep11_cprb); pld_len = msg->cprbx.payload_len; -- cgit v1.2.3 From 478a1c3abebfc717db0d1281a9cdd7befafee542 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 3 Aug 2026 17:37:08 -0700 Subject: mm: fix incorrect flush address in direct page table reclaim When zap_pte_range reclaims a page table, it does: pte_free_tlb(tlb, pmd_pgtable(pmdval), addr); and this is unconditionally wrong: if this code executes, addr *always* points one past the end of the range covered by the table. The addr parameter is used to flush the TLB (really the paging-structure-cache) to drop references to the to-be-freed table, and any architecture that cares about the parameter will flush the wrong address. (But they'll still free the correct page). I think it's worth contemplating why the kernel works at all. If we hit the offending line of code, we will first clear the PMD entry (line 1954, zap_empty_pte_table), then we will issue pending flushes if force_flush is set (tlb_flush_mmu_tlbonly(tlb)), then we will skip the retry on line 1979 (phew!), and then we will do the offending pte_free_tlb call. *Or* we will clear the PMD entry immediately before pte_free_tlb (line 1983, zap_pte_table_if_empty). If we have any pending flushes (i.e. we actually zapped any last-level entries) at the time we clear the PMD entry, then the flush really ought to flush all references to the table (Linus certainly seems to think it will on all architectures [0]). The condition under which we have no accumulated flushes at the time of the clear is very complex (the whole zap_pte_range function has absurdly complex control flow). If we do hit the bad case, then we will end up clearing the PMD entry after the last time the range is flushed, and any CPU is free to cache a reference to the (empty) page table. If this happens due to an ordinary read or write, it would segfault, so it would be rare. But the cache could be speculatively filled as well. Then we'll flush the wrong address and then free and possibly reuse the table. On x86, even flushing the wrong address works on non-KPTI Intel systems because INVLPG flushes *all* paging-structure-caches, not just the ones for the target address. But INVPCID does not, and flush_tlb_one_user will use INVPCID if it's available. And then we're toast. AMD systems are more susceptible: we set the EFER.TCE bit, which makes even INVLPG only flush the target address. I think this might fix an issue in ripgrep reported here: https://github.com/BurntSushi/ripgrep/issues/3494 [0] https://lore.kernel.org/all/CA+55aFzBggoXtNXQeng5d_mRoDnaMBE5Y+URs+PHR67nUpMtaw@mail.gmail.com/T/#u Signed-off-by: Andy Lutomirski Fixes: 4c640eb4181c ("mm: move pte table reclaim code to memory.c") Cc: Liam Howlett Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: stable@vger.kernel.org Acked-by: Vlastimil Babka (SUSE) Acked-by: David Hildenbrand (Arm) Acked-by: Michal Hocko Acked-by: Qi Zheng Signed-off-by: Linus Torvalds --- mm/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory.c b/mm/memory.c index ff338c2abe92..6b8280cfc1db 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1981,7 +1981,7 @@ retry: if (can_reclaim_pt) { if (direct_reclaim || zap_pte_table_if_empty(mm, pmd, start, &pmdval)) { - pte_free_tlb(tlb, pmd_pgtable(pmdval), addr); + pte_free_tlb(tlb, pmd_pgtable(pmdval), start); mm_dec_nr_ptes(mm); } } -- cgit v1.2.3 From 8786d74bf50e6797b6f655eb381ef6b25451161f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 25 Jun 2026 22:17:55 -0700 Subject: Input: synaptics-rmi4 - propagate F54 worker errors to V4L2 queue Previously, rmi_f54_buffer_queue() waited for the worker thread to finish but ignored whether it succeeded. If the worker failed (e.g., due to a timeout or register read failure), the queue thread would silently return success, delivering stale or uninitialized memory to userspace. Add a 'report_error' field to struct f54_data to store the worker's exit status. Check this field in rmi_f54_buffer_queue() after the worker finishes, and mark the buffer as VB2_BUF_STATE_ERROR if an error occurred. Fixes: 3a762dbd5347 ("[media] Input: synaptics-rmi4 - add support for F54 diagnostics") Reported-by: sashiko-bot@kernel.org Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260626051802.4033172-6-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_f54.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 345646612032..6c6cdec7da9e 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -106,6 +106,7 @@ struct f54_data { u8 *report_data; size_t max_report_size; int report_size; + int report_error; bool is_busy; struct mutex status_mutex; @@ -340,6 +341,12 @@ static void rmi_f54_buffer_queue(struct vb2_buffer *vb) mutex_lock(&f54->data_mutex); } + if (f54->report_error) { + dev_err(&f54->fn->dev, "Error acquiring report: %d\n", f54->report_error); + state = VB2_BUF_STATE_ERROR; + goto data_done; + } + ptr = vb2_plane_vaddr(vb, 0); if (!ptr) { dev_err(&f54->fn->dev, "Error acquiring frame ptr\n"); @@ -610,6 +617,7 @@ out: report_size = 0; f54->report_size = report_size; + f54->report_error = error; if (report_size == 0 && !error) { queue_delayed_work(f54->workqueue, &f54->work, -- cgit v1.2.3 From 31a420a822ff92e2090bd5d65efe8e34e2d6d9b8 Mon Sep 17 00:00:00 2001 From: Luxiao Xu Date: Tue, 4 Aug 2026 22:29:01 +0800 Subject: bpf: Check sk_state before sk_protocol in bpf_tcp_*_syncookie bpf_tcp_gen_syncookie and bpf_tcp_check_syncookie accept a socket pointer 'sk' with argument type ARG_PTR_TO_BTF_ID_SOCK_COMMON. However, they access sk->sk_protocol without validating whether 'sk' represents a full socket. Fix this issue by checking sk->sk_state != TCP_LISTEN before inspecting sk->sk_protocol in both bpf_tcp_gen_syncookie and bpf_tcp_check_syncookie. Since mini-sockets are never in the TCP_LISTEN state, the condition short-circuits and prevents dereferencing fullsock-specific fields. Fixes: 399040847084 ("bpf: add helper to check for a valid SYN cookie") Fixes: 70d66244317e ("bpf: add bpf_tcp_gen_syncookie helper") Reported-by: Vega Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei Signed-off-by: Daniel Borkmann Reviewed-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://lore.kernel.org/bpf/6218aa3534d0d2d3f448fde70a8dc2769d7a8201.1785823138.git.rakukuip@gmail.com --- net/core/filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index 11bb0d236822..16845987b244 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -7684,7 +7684,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len return -EINVAL; /* sk_listener() allows TCP_NEW_SYN_RECV, which makes no sense here. */ - if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) + if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP) return -EINVAL; if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) @@ -7757,7 +7757,7 @@ BPF_CALL_5(bpf_tcp_gen_syncookie, struct sock *, sk, void *, iph, u32, iph_len, if (unlikely(!sk || th_len < sizeof(*th) || th_len != th->doff * 4)) return -EINVAL; - if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) + if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP) return -EINVAL; if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) -- cgit v1.2.3 From a0ab2ba83e35159d81cec830a92e885ecf8139be Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Mon, 3 Aug 2026 14:17:38 +0800 Subject: tcp: fix TFO max_qlen accounting across reuseport migration A listener's TCP_FASTOPEN max_qlen stops being accurate and lets through far more pending Fast Open requests than it was configured for. This only shows up with SO_REUSEPORT listener migration, where closing a listener hands its still-pending TFO children over to a surviving one. fastopenq.qlen is charged in tcp_fastopen_create_child() when the child is created and uncharged in reqsk_fastopen_remove() when the handshake completes. The uncharge follows rsk_listener of the request the child points at, and inet_reqsk_clone() has repointed the child at a new request owned by the new listener, so the ++ and the -- land on two different sockets. The new listener's qlen drifts negative and its limit no longer binds. Charge the new listener during migration, like reqsk_queue_migrated() already does for queue->young and queue->qlen. Fixes: 54b92e841937 ("tcp: Migrate TCP_ESTABLISHED/TCP_SYN_RECV sockets in accept queues.") Signed-off-by: Jiayuan Chen Reviewed-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260803061739.134737-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv4/inet_connection_sock.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 56902bba5483..6257459bcee2 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -943,11 +943,23 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req, nreq->rsk_listener = sk; - /* We need not acquire fastopenq->lock - * because the child socket is locked in inet_csk_listen_stop(). - */ - if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener) + if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener) { + struct fastopen_queue *fastopenq; + + /* reqsk_fastopen_remove() will uncharge nreq->rsk_listener, + * that is @sk, so charge it here. Unlike the listener + * being closed, @sk is live and needs its lock. + */ + fastopenq = &inet_csk(sk)->icsk_accept_queue.fastopenq; + spin_lock_bh(&fastopenq->lock); + fastopenq->qlen++; + spin_unlock_bh(&fastopenq->lock); + + /* We need not acquire fastopenq->lock + * because the child socket is locked in inet_csk_listen_stop(). + */ rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq); + } return nreq; } -- cgit v1.2.3 From 8aecf0bbcc72605592134c917c222207d8f63ab0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 4 Aug 2026 09:33:28 +0000 Subject: netfilter: nf_flow_table: drop existing skb dst before skb_dst_set_noref() Incoming skbs passing through netfilter flowtable offload hooks (or XFRM offload path) might already carry a ref-counted dst_entry assigned during earlier RX or routing steps. Calling skb_dst_set_noref() when skb already holds a ref-counted dst overwrites skb->_skb_refdst, leaking the previous dst_entry reference count and triggering a DEBUG_NET_WARN_ON_ONCE assertion in skb_dst_check_unset(): WARNING: at skb_dst_check_unset include/linux/skbuff.h:1170 WARNING: at skb_dst_set_noref include/linux/skbuff.h:1234 WARNING: at nf_flow_offload_ip_hook+0xf6c/0x2b60 net/netfilter/nf_flow_table_ip.c:864 Drop any existing dst_entry reference with skb_dst_drop(skb) before setting the non-referenced flowtable destination. Fixes: 2a79fd3908ac ("netfilter: nf_flow_table: attach dst to skbs") Reported-by: syzbot+76d4e3a055aec3b007ec@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a71b141.9511d2ce.1fc5b9.033b.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Pablo Neira Ayuso Link: https://patch.msgid.link/20260804093328.1831847-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/netfilter/nf_flow_table_ip.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 0b78decce8a9..c9e332fafcb5 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -310,6 +310,7 @@ static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb, struct dst_entry *dst) { skb_orphan(skb); + skb_dst_drop(skb); skb_dst_set_noref(skb, dst); dst_output(state->net, state->sk, skb); return NF_STOLEN; @@ -861,6 +862,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, return NF_DROP; } xmit.dest = neigh->ha; + skb_dst_drop(skb); skb_dst_set_noref(skb, &rt->dst); break; case FLOW_OFFLOAD_XMIT_DIRECT: @@ -1178,6 +1180,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, return NF_DROP; } xmit.dest = neigh->ha; + skb_dst_drop(skb); skb_dst_set_noref(skb, &rt->dst); break; case FLOW_OFFLOAD_XMIT_DIRECT: -- cgit v1.2.3 From 5f26a690e8efa54315e4922368daf54e0b8f5515 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 2 Aug 2026 09:23:34 +0000 Subject: mac802154: fix netdev use-after-free in beacon worker mac802154_beacon_worker() reads local->beacon_req under RCU and derives the sub-interface from the request, but then drops the RCU read lock and continues to use both sdata and the embedded wpan_dev. mac802154_stop_beacons_locked() cancels only pending beacon work, clears local->beacon_req and frees the request. A beacon worker that is already running can therefore continue after interface teardown and dereference the freed netdev private area. The scan worker already pins the netdev before leaving RCU. Apply the same lifetime rule to the beacon worker: take a netdev reference while the request is still protected by RCU, and release it on all paths that continue after the reference is acquired. Fixes: 3accf4762734 ("mac802154: Handle basic beaconing") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zihan Xi Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/e9a3909c7a6281967961773ca841e860b8ecf40e.1785596603.git.zihanx@nebusec.ai Signed-off-by: Jakub Kicinski --- net/mac802154/scan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 65089826ff59..005338f89b75 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -415,6 +415,7 @@ void mac802154_beacon_worker(struct work_struct *work) container_of(work, struct ieee802154_local, beacon_work.work); struct cfg802154_beacon_request *beacon_req; struct ieee802154_sub_if_data *sdata; + netdevice_tracker dev_tracker; struct wpan_dev *wpan_dev; u8 interval; int ret; @@ -427,12 +428,14 @@ void mac802154_beacon_worker(struct work_struct *work) } sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(beacon_req->wpan_dev); + netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC); /* Wait an arbitrary amount of time in case we cannot use the device */ if (local->suspended || !ieee802154_sdata_running(sdata)) { rcu_read_unlock(); queue_delayed_work(local->mac_wq, &local->beacon_work, msecs_to_jiffies(1000)); + netdev_put(sdata->dev, &dev_tracker); return; } @@ -450,6 +453,7 @@ void mac802154_beacon_worker(struct work_struct *work) if (interval < IEEE802154_ACTIVE_SCAN_DURATION) queue_delayed_work(local->mac_wq, &local->beacon_work, local->beacon_interval); + netdev_put(sdata->dev, &dev_tracker); } int mac802154_stop_beacons_locked(struct ieee802154_local *local, -- cgit v1.2.3 From afa58b7384913c8773d837acdb07b035690ec5d2 Mon Sep 17 00:00:00 2001 From: Henry Martin Date: Mon, 3 Aug 2026 12:36:18 +0800 Subject: net/ncsi: fix heap OOB read in NCSI_CMD_SEND_CMD payload length ncsi_send_cmd_nl() takes the number of bytes to copy from the attacker-controlled ncsi_pkt_hdr.length field of the in-band packet header, while the source buffer is the NCSI_ATTR_DATA netlink attribute whose readable size is nla_len() - sizeof(ncsi_pkt_hdr). The two length sources are never cross-checked: only nla_len() >= sizeof(struct ncsi_pkt_hdr) is enforced. With hdr->length set larger than the attribute payload (up to 65535 against at most 2032 readable bytes), ncsi_cmd_handler_oem() copies past the end of the netlink attribute buffer with unsafe_memcpy(), leaking up to ~64KB of kernel heap memory into the transmitted NCSI command packet. The destination skb is sized by the declared payload, so the write side does not overflow - this is a pure OOB read / information leak, reachable with CAP_NET_ADMIN on systems with a registered NCSI device (e.g. OpenBMC on Aspeed BMC SoCs, where NET_NCSI=y is standard). Reject commands whose declared payload extends past the end of the data attribute. The issue was found by the autokbug dynamic kernel fuzzer at Tencent Yunding Lab. Fixes: 9771b8ccdfa6 ("net/ncsi: Extend NC-SI Netlink interface to allow user space to send NC-SI command") Reported-by: Henry Martin Signed-off-by: Henry Martin Link: https://patch.msgid.link/20260803043618.3210301-1-bsdhenrymartin@gmail.com Signed-off-by: Jakub Kicinski --- net/ncsi/ncsi-netlink.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c index 2f872d064396..8cc538358f6a 100644 --- a/net/ncsi/ncsi-netlink.c +++ b/net/ncsi/ncsi-netlink.c @@ -461,6 +461,10 @@ static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info) nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN; nca.info = info; nca.payload = ntohs(hdr->length); + if (nca.payload > len - sizeof(*hdr)) { + ret = -EINVAL; + goto out_netlink; + } nca.data = data + sizeof(*hdr); ret = ncsi_xmit_cmd(&nca); -- cgit v1.2.3 From f803c086399da277b5d0ff36a107d0f162751800 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Mon, 3 Aug 2026 14:12:33 +0800 Subject: ip6_tunnel: clear skb2->cb[] in ip6ip6_err() ip6ip6_err() clones an outer IPv6 ICMP error skb, pulls it to the quoted inner IPv6 packet, and then passes the clone to icmpv6_send(). The clone still carries the outer packet's inet6_skb_parm in skb->cb. If the outer packet had a Home Address Option, IP6CB(skb2)->dsthao remains non-zero after skb_pull(). icmpv6_send() later calls mip6_addr_swap(), which uses that stale dsthao offset against the quoted inner packet. A malformed inner destination-options header can then make the HAO lookup and address swap run past the end of the quoted packet and corrupt skb_shared_info. Clear skb2->cb[] before pulling the quoted inner IPv6 packet so the reply path does not reuse metadata left by the outer IPv6 stack. Fixes: e490d1d85cf5 ("[IPV6] IP6TUNNEL: Split out generic routine in ip6ip6_err().") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/fe1a5e765fbca88d69391887f0ed26a19e3e4d39.1785736562.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_tunnel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index bf8e40af60b0..ebf83f090376 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -684,6 +684,9 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (!skb2) return 0; + /* Remove debris left by outer IPv6 stack. */ + memset(IP6CB(skb2), 0, sizeof(*IP6CB(skb2))); + skb_dst_drop(skb2); skb_pull(skb2, offset); skb_reset_network_header(skb2); -- cgit v1.2.3 From 3874892dd27d5387aa9a06f58d9060f18f351d24 Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Sun, 2 Aug 2026 15:46:12 -0700 Subject: net: tap: set skb->dev before parsing virtio net header in tap_get_user_xdp() The commit 4f61f133f354 ("net: tap: NULL pointer derefence in dev_parse_header_protocol when skb->dev is null") fixed a crash in tap_get_user() by assigning skb->dev before calling tun_vnet_hdr_to_skb(). This is required because virtio_net_hdr_to_skb() may invoke dev_parse_header_protocol(), which dereferences skb->dev. Without the assignment, a NULL pointer dereference can occur. However, tap_get_user_xdp() still parses the virtio-net header before assigning skb->dev. When the vhost TX path passes an XDP buffer containing a GSO virtio-net header but the protocol is set to zero on purpose, tun_vnet_hdr_to_skb() can reach dev_parse_header_protocol() while skb->dev is still NULL, resulting in a crash. Fix this by looking up the tap device and assigning skb->dev before calling tun_vnet_hdr_to_skb(), matching the ordering already used in tap_get_user(). Preserve the existing RCU read-side critical section across dev_queue_xmit(). Fixes: 924a9bc362a5 ("net: check if protocol extracted by virtio_net_hdr_set_proto is correct") Cc: stable@vger.kernel.org Signed-off-by: Dongli Zhang Reviewed-by: Willem de Bruijn Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260802224612.264563-1-dongli.zhang@oracle.com Signed-off-by: Jakub Kicinski --- drivers/net/tap.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/tap.c b/drivers/net/tap.c index fae115915c8e..5d2d34d24ce8 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1074,10 +1074,21 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) skb_reset_mac_header(skb); skb->protocol = eth_hdr(skb)->h_proto; + rcu_read_lock(); + tap = rcu_dereference(q->tap); + if (!tap) { + kfree_skb(skb); + rcu_read_unlock(); + return 0; + } + skb->dev = tap->dev; + if (vnet_hdr_len) { err = tun_vnet_hdr_to_skb(q->flags, skb, gso); - if (err) + if (err) { + rcu_read_unlock(); goto err_kfree; + } } /* Move network header to the right position for VLAN tagged packets */ @@ -1085,15 +1096,8 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - rcu_read_lock(); - tap = rcu_dereference(q->tap); - if (tap) { - skb->dev = tap->dev; - skb_probe_transport_header(skb); - dev_queue_xmit(skb); - } else { - kfree_skb(skb); - } + skb_probe_transport_header(skb); + dev_queue_xmit(skb); rcu_read_unlock(); return 0; -- cgit v1.2.3 From 8ae344eb540af3f457179b52bc6061416752485c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 31 Jul 2026 22:19:06 +0800 Subject: net: prestera: validate firmware header length prestera_fw_hdr_parse() reads the firmware header before checking that the firmware image contains that header. Reject images shorter than struct prestera_fw_header before decoding the magic and version fields. Fixes: 4c2703dfd7fabb ("net: marvell: prestera: Add PCI interface support") Signed-off-by: Pengpeng Hou Acked-by: Elad Nachman Link: https://patch.msgid.link/20260731141500.1-prestera-v2-pengpeng@iscas.ac.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/prestera/prestera_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_pci.c b/drivers/net/ethernet/marvell/prestera/prestera_pci.c index 2989a77e3b42..1ad0e62a8433 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_pci.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_pci.c @@ -684,6 +684,9 @@ static int prestera_fw_hdr_parse(struct prestera_fw *fw) struct prestera_fw_header *hdr; u32 magic; + if (fw->bin->size < sizeof(*hdr)) + return -EINVAL; + hdr = (struct prestera_fw_header *)fw->bin->data; magic = be32_to_cpu(hdr->magic_number); -- cgit v1.2.3 From c27e360545373b7aee9862a5beef3b9fb3df0c25 Mon Sep 17 00:00:00 2001 From: Hidayath Khan Date: Thu, 30 Jul 2026 14:42:27 +0200 Subject: dibs: initialise dibs->lock in dibs_dev_alloc() dibs->lock is initialised by dibs_dev_add(), but a dibs device can already take interrupts before that call: ism_probe() runs ism_dev_init(), and hence request_irq(), before it calls dibs_dev_add(). No client can have registered a dmb at that point, so no dmb interrupt can occur, but a GID event interrupt can, and ism_handle_irq() takes dibs->lock unconditionally on entry, before it inspects anything else. Initialise the lock in dibs_dev_alloc() instead, so that it is valid as soon as a driver can publish the device to its interrupt handler. Fixes: cc21191b584c ("dibs: Move data path to dibs layer") Cc: stable@vger.kernel.org Reviewed-by: Alexandra Winter Signed-off-by: Hidayath Khan Link: https://patch.msgid.link/20260730124227.167829-1-hidayath@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/dibs/dibs_main.c | 2 +- include/linux/dibs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dibs/dibs_main.c b/drivers/dibs/dibs_main.c index 14c3e2d84902..4c26fd06973f 100644 --- a/drivers/dibs/dibs_main.c +++ b/drivers/dibs/dibs_main.c @@ -138,6 +138,7 @@ struct dibs_dev *dibs_dev_alloc(void) dibs = kzalloc_obj(*dibs); if (!dibs) return dibs; + spin_lock_init(&dibs->lock); dibs->dev.release = dibs_dev_release; dibs->dev.class = &dibs_class; device_initialize(&dibs->dev); @@ -186,7 +187,6 @@ int dibs_dev_add(struct dibs_dev *dibs) int i, ret; max_dmbs = dibs->ops->max_dmbs(); - spin_lock_init(&dibs->lock); dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL); if (!dibs->dmb_clientid_arr) return -ENOMEM; diff --git a/include/linux/dibs.h b/include/linux/dibs.h index c75607f8a5cf..d3e0777f25ae 100644 --- a/include/linux/dibs.h +++ b/include/linux/dibs.h @@ -439,7 +439,7 @@ static inline void *dibs_get_priv(struct dibs_dev *dev, /** * dibs_dev_alloc() - allocate and reference device structure * - * The following fields will be valid upon successful return: dev + * The following fields will be valid upon successful return: dev, lock * NOTE: Use put_device(dibs_get_dev(@dibs)) to give up your reference instead * of freeing @dibs @dev directly once you have successfully called this * function. -- cgit v1.2.3 From 44871eadd07a7f004aa00cb87399461eea08c630 Mon Sep 17 00:00:00 2001 From: Jijie Shao Date: Tue, 4 Aug 2026 21:05:54 +0800 Subject: MAINTAINERS: add myself as a maintainer for Hisilicon Network Subsystem I am already listed as a maintainer for the HNS3 and HIBMCGE drivers, but not for the broader Hisilicon Network Subsystem entry, whose file pattern covers drivers/net/ethernet/hisilicon/ (e.g. the legacy hns driver). As a result, patches to those files are not CC'd to me. Add myself alongside Jian Shen to help maintain these legacy Hisilicon ethernet drivers and ensure patches in this tree are routed to me. Signed-off-by: Jijie Shao Acked-by: Jian Shen Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260804130554.871716-1-shaojijie@huawei.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0d7987278c07..a77cf599bac2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11743,6 +11743,7 @@ F: drivers/net/ethernet/hisilicon/hibmcge/ HISILICON NETWORK SUBSYSTEM DRIVER M: Jian Shen +M: Jijie Shao L: netdev@vger.kernel.org S: Maintained W: http://www.hisilicon.com -- cgit v1.2.3 From 3abd29c61d2ef37c4102cf755b18be53bb9dbea6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 3 Aug 2026 18:41:49 -0700 Subject: Input: evdev - sanitize event type index when fetching event masks The user-supplied event type index passed to EVIOCGMASK / EVIOCSMASK ioctls is used to index the static counts array in evdev_get_mask_cnt() and client evmasks array in evdev_get_mask(). While the event type is architecturally bounded by EV_CNT, speculative execution may mispredict bounds checks and perform out-of-bounds loads. Sanitize the event type index in evdev_get_mask_cnt() branchlessly using array_index_mask_nospec(). This clamps the index to 0 for safe array access and forces the returned count to 0 speculatively when the index is out of bounds. We do not need additional array_index_nospec() calls in evdev_get_mask() because evdev_get_mask_cnt() speculatively forces the count (and resulting xfer_size) to 0 for out-of-bounds types, preventing any speculative memory access to client evmasks array. Reported-by: "Wagenaar, C.C.J. (Chris)" Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.6-flash Acked-by: Greg Kroah-Hartman Link: https://patch.msgid.link/anFCAfvxwXB5eJF1@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 531db70aa9f9..3a718d600006 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "input-compat.h" @@ -67,8 +68,10 @@ static size_t evdev_get_mask_cnt(unsigned int type) [EV_SND] = SND_CNT, [EV_FF] = FF_CNT, }; + unsigned long mask = array_index_mask_nospec(type, EV_CNT); - return (type < EV_CNT) ? counts[type] : 0; + /* Returns 0 for out-of-bounds types, including speculatively */ + return counts[type & mask] & mask; } /* requires the buffer lock to be held */ -- cgit v1.2.3 From e0d2b5902c5c0d3943af95728753aae165ed1346 Mon Sep 17 00:00:00 2001 From: "Christian A. Ehrhardt" Date: Tue, 23 Jun 2026 20:19:42 +0200 Subject: drm/panthor: Check VMA boundaries for PMD mappings When checking a different patch[1] sashiko AI pointed out that panthor needs the same fix[2]: In the ->huge_fault handler do not install a PMD huge page mapping if the huge page exceeds the boundaries of the VMA. [1] https://lore.kernel.org/lkml/20260622215718.1532689-1-lk@c--e.de/ [2] https://sashiko.dev/#/patchset/20260622215718.1532689-1-lk%40c--e.de Cc: Boris Brezillon Cc: Steven Price Cc: Liviu Dudau Fixes: 68cbf96b1e9b ("drm/panthor: Part ways with drm_gem_shmem_object") Signed-off-by: Christian A. Ehrhardt Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://lore.kernel.org/lkml/20260622215718.1532689-1-lk@c--e.de/ Link: https://patch.msgid.link/20260623181942.1536598-1-lk@c--e.de Signed-off-by: Boris Brezillon --- drivers/gpu/drm/panthor/panthor_gem.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index a1e2eb1ca7bb..54535bae2b0c 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -802,9 +802,13 @@ static vm_fault_t insert_page(struct vm_fault *vmf, unsigned int order, struct p } else if (order == PMD_ORDER) { unsigned long pfn = page_to_pfn(page); unsigned long paddr = pfn << PAGE_SHIFT; + struct vm_area_struct *vma = vmf->vma; + unsigned long start = ALIGN_DOWN(vmf->address, PMD_SIZE); + unsigned long end = start + PMD_SIZE; + bool in_range = vma->vm_start <= start && end <= vma->vm_end; bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK); - if (aligned && + if (aligned && in_range && folio_test_pmd_mappable(page_folio(page))) { pfn &= PMD_MASK >> PAGE_SHIFT; return vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE); -- cgit v1.2.3 From b8a39a09ae4eaae04309e1e38ed6a1101d967496 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 4 Aug 2026 15:20:48 +0000 Subject: net: remove WARN_ON_ONCE() from sk_mc_loop() sk_mc_loop() can be called for sockets that are neither AF_INET nor AF_INET6 (e.g. AF_PACKET sockets when sending packets via raw/packet socket over virtual devices such as VRF or ipvlan). In such cases, sk_family is not AF_INET/AF_INET6 and sk_mc_loop() falls through the switch statement and triggers WARN_ON_ONCE(1). Non-INET sockets do not support IP_MULTICAST_LOOP or IPV6_MULTICAST_LOOP options, so loopback should default to true without generating a warning. Fixes: f60e5990d9c1 ("ipv6: protect skb->sk accesses from recursive dereference inside the stack") Reported-by: syzbot+22c3218a6fa219e47321@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a72024c.13623e66.bdc14.0019.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260804152048.2134341-1-edumazet@google.com Signed-off-by: Paolo Abeni --- net/core/sock.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/sock.c b/net/core/sock.c index ffa73594c13c..1ad41904db25 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -779,7 +779,6 @@ bool sk_mc_loop(const struct sock *sk) return inet6_test_bit(MC6_LOOP, sk); #endif } - WARN_ON_ONCE(1); return true; } EXPORT_SYMBOL(sk_mc_loop); -- cgit v1.2.3 From 185a4caeecabc150106deda1da170b09f2ad803f Mon Sep 17 00:00:00 2001 From: Sidraya Jayagond Date: Mon, 3 Aug 2026 09:07:01 +0200 Subject: net/smc: fix TOCTOU race between smc_listen_out() and listener close smc_listen_out() reads lsmc->sk.sk_state without the listener lock, then acquires lock_sock_nested() only after the check passes. This opens a window where smc_close_active() can transition the listener to SMC_CLOSED, call smc_close_cleanup_listen() to drain the accept queue, and release the lock, all between the lockless read and the delayed lock acquisition: smc_listen_work (smc_hs_wq) smc_close_active() ------------------------------- ------------------------- release_sock(child) if (sk_state == SMC_LISTEN) TRUE lock_sock(listener) sk_state = SMC_CLOSED smc_close_cleanup_listen() release_sock(listener) flush_work(tcp_listen_work) lock_sock_nested(listener) smc_accept_enqueue(listener, child) /* child enqueued on dead listener */ smc_close_active() flushes only tcp_listen_work. Work items already dispatched onto smc_hs_wq for the CLC handshake continue running unguarded. smc_accept_enqueue() takes a sock_hold() on the child that is never released, so the child smc_sock, its clcsock, and the reference all leak. A remote peer that opens TCP connections while the server calls close() can exhaust kernel memory. Move lock_sock_nested() to before the sk_state check so that the test and the enqueue are atomic under the listener lock. Fixes: fd57770dd198 ("net/smc: wait for pending work before clcsock release_sock") Reviewed-by: Mahanta Jambigi Signed-off-by: Sidraya Jayagond Reviewed-by: Breno Leitao Reviewed-by: Dust Li Link: https://patch.msgid.link/20260803070701.126339-1-sidraya@linux.ibm.com Signed-off-by: Paolo Abeni --- net/smc/af_smc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index b5db69073e20..00403175b740 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -1931,11 +1931,12 @@ static void smc_listen_out(struct smc_sock *new_smc) atomic_dec(&lsmc->queued_smc_hs); release_sock(newsmcsk); /* lock in smc_listen_work() */ + lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING); if (lsmc->sk.sk_state == SMC_LISTEN) { - lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING); smc_accept_enqueue(&lsmc->sk, newsmcsk); release_sock(&lsmc->sk); } else { /* no longer listening */ + release_sock(&lsmc->sk); smc_close_non_accepted(newsmcsk); } -- cgit v1.2.3 From 9ed3d974a26644ad57b3d4d067e279188b2038b6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 5 Aug 2026 18:51:00 +0100 Subject: regcache: Use a consistent sort for defaults table When we look up registers in the defaults table we use a binary search, and we have a regcache_sort_defaults() API to help drivers that constuct their defaults tables on the fly. Unfortunately the lookup and the sort don't use the same comparison function, and to make matters worse the comparison function used during lookups is written for signed register numbers rather than the unsigned ones we actually have so can produce suprising results when some of the addresses have the top bit set. Standardise on the more explicitly coded function to ensure consistent results. Reviewed-by: Peter Ujfalusi Reviewed-by: Charles Keepax Tested-by: Charles Keepax Link: https://patch.msgid.link/20260805-regmap-regcache-sort-v1-1-162186aad8b9@kernel.org Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index aa8f2efed779..480bc76f9a02 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -727,14 +727,6 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, return -1; } -static int regcache_default_cmp(const void *a, const void *b) -{ - const struct reg_default *_a = a; - const struct reg_default *_b = b; - - return _a->reg - _b->reg; -} - int regcache_lookup_reg(struct regmap *map, unsigned int reg) { struct reg_default key; @@ -744,7 +736,7 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg) key.def = 0; r = bsearch(&key, map->reg_defaults, map->num_reg_defaults, - sizeof(struct reg_default), regcache_default_cmp); + sizeof(struct reg_default), regcache_defaults_cmp); if (r) return r - map->reg_defaults; -- cgit v1.2.3 From 4b05ccb17f92268384d483221a577fccfc291c7a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 5 Aug 2026 16:22:50 +0300 Subject: regcache: Sort the local copy of an unsorted reg_defaults array regcache_lookup_reg() bsearch()es the reg_defaults array, which requires it to be sorted by ascending register address. Entries following a descending step are never found, so regcache_reg_needs_sync() reports that they need a sync and they are written to the device on every regcache_sync() even when they were never touched. Detect the misordering while reg_defaults is validated against the register stride and sort the local copy. The check needs no new loop and sort() only runs for the affected drivers, which are also warned about. Note that sort() is not stable, so for arrays with duplicated register addresses it remains unspecified which entry is found. Signed-off-by: Peter Ujfalusi Reviewed-by: Charles Keepax Tested-by: Charles Keepax Link: https://patch.msgid.link/20260805132250.2637-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 480bc76f9a02..623db7a1f3bd 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -123,6 +123,8 @@ static void regcache_hw_exit(struct regmap *map) int regcache_init(struct regmap *map, const struct regmap_config *config) { + bool sort_defaults = false; + unsigned int reg_prev = 0; int count = 0; int ret; int i; @@ -149,10 +151,16 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) return -EINVAL; } - for (i = 0; i < config->num_reg_defaults; i++) + for (i = 0; i < config->num_reg_defaults; i++) { if (config->reg_defaults[i].reg % map->reg_stride) return -EINVAL; + if (reg_prev > config->reg_defaults[i].reg) + sort_defaults = true; + + reg_prev = config->reg_defaults[i].reg; + } + for (i = 0; i < ARRAY_SIZE(cache_types); i++) if (cache_types[i]->type == map->cache_type) break; @@ -186,6 +194,13 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) sizeof(*map->reg_defaults), GFP_KERNEL); if (!tmp_buf) return -ENOMEM; + + /* regcache_lookup_reg() bsearch()es this array */ + if (sort_defaults) { + dev_warn(map->dev, + "Driver needs fixing: Unsorted reg_defaults, sorting the copy\n"); + regcache_sort_defaults(tmp_buf, map->num_reg_defaults); + } map->reg_defaults = tmp_buf; } else if (map->num_reg_defaults_raw) { count = regcache_count_cacheable_registers(map); -- cgit v1.2.3 From f684c514f7965385dae21f2535f99938e73ec1af Mon Sep 17 00:00:00 2001 From: Ahmed Naseef Date: Tue, 4 Aug 2026 15:35:11 +0400 Subject: net: phy: mediatek: fix TX blink masks using the RX bits MTK_GPHY_LED_TX_BLINK_SET and MTK_2P5GPHY_LED_TX_BLINK_SET are built from the RX blink bits instead of the TX ones, so both TX masks are identical to their RX counterparts. The TX bits they should be using, MTK_PHY_LED_BLINK_{10,100,1000,2500}TX, are otherwise only referenced by the per-speed branch of mtk_phy_led_hw_ctrl_set(). A TX trigger selected without a link trigger therefore programs the RX blink bits, and the LED blinks on received traffic. The masks are also used to decode the blink register in mtk_phy_led_hw_ctrl_get(), which as a result cannot tell the two triggers apart: an RX-only configuration reads back as RX and TX, and a TX-only configuration reads back as neither. Fixes: 7f9c320c98db ("net: phy: mediatek: Move LED helper functions into mtk phy lib") Cc: stable@vger.kernel.org Signed-off-by: Ahmed Naseef Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260804113511.3371248-1-naseefkm@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/phy/mediatek/mtk.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/mediatek/mtk.h b/drivers/net/phy/mediatek/mtk.h index 320f76ffa81f..79e09727b81b 100644 --- a/drivers/net/phy/mediatek/mtk.h +++ b/drivers/net/phy/mediatek/mtk.h @@ -53,15 +53,15 @@ #define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ MTK_PHY_LED_BLINK_100RX | \ MTK_PHY_LED_BLINK_10RX) -#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ - MTK_PHY_LED_BLINK_100RX | \ - MTK_PHY_LED_BLINK_10RX) +#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000TX | \ + MTK_PHY_LED_BLINK_100TX | \ + MTK_PHY_LED_BLINK_10TX) #define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \ MTK_GPHY_LED_ON_SET) #define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ MTK_GPHY_LED_RX_BLINK_SET) -#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ +#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500TX | \ MTK_GPHY_LED_TX_BLINK_SET) #define MTK_PHY_LED_STATE_FORCE_ON 0 -- cgit v1.2.3 From e3775cbffaf0e61fda4808a06963a6bc3fcddf82 Mon Sep 17 00:00:00 2001 From: Swark Yang Date: Wed, 5 Aug 2026 22:06:22 -0700 Subject: spi: dt-bindings: snps,dw-apb-ssi: Document Axiado AX3005 The Axiado AX3005 SPI controller is software-compatible with the Synopsys DesignWare APB SSI 1.01a programming interface. Document the "axiado,ax3005-spi" SoC-specific compatible with "snps,dwc-ssi-1.01a" as its fallback. This preserves identification of the AX3005 integration while allowing the controller to use the existing DesignWare APB SSI support. Signed-off-by: Swark Yang Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260805-upstrea-ax3005-spi-v4-v4-1-5b8bc11cb841@axiado.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index 95a5bd894e93..7f698195cc43 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -53,6 +53,11 @@ properties: - sophgo,sg2042-spi - thead,th1520-spi - const: snps,dw-apb-ssi + - description: Vendor controllers compatible with v1.01a + items: + - enum: + - axiado,ax3005-spi + - const: snps,dwc-ssi-1.01a - description: Vendor controllers which use snps,dwc-ssi-2.00a as fallback items: - enum: -- cgit v1.2.3 From 7e2d693af0d4c05bddccb3541a0aabd69f4cb244 Mon Sep 17 00:00:00 2001 From: "Sergey V. Frolov" Date: Tue, 4 Aug 2026 15:04:48 +0300 Subject: net: octeontx2-pf: Fix UB in shift operation In function otx2_get_egress_burst_cfg, when the parameter `burst` is 255 and the max mantissa is 255 (0xFFULL), `burst_exp` is set to `ilog2(255) - 1`, which equals 6. This results in an unsigned wrap-around when calculating `(1ULL << (*burst_exp - 7))`, since `*burst_exp - 7` becomes -1, which makes the shift operand 0xFFFFFFFF. This value is greater than the width of the left operand. According to standard 6.5.7 p.3: "The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined." Fix the off-by-one boundary condition. Add a WARN_ON(*burst_exp < 7) before the else branch as an explicit safeguard. This ensures that if max_mantissa ever changes in a way that reintroduces this condition, it will be immediately caught at runtime rather than silently triggering UB. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: e638a83f167e ("octeontx2-pf: TC_MATCHALL egress ratelimiting offload") Signed-off-by: Sergey V. Frolov Cc: stable@vger.kernel.org Reviewed-by: Ratheesh Kannoth Reviewed-by: Sunil Goutham Link: https://patch.msgid.link/20260804120446.1955448-1-Sergey.V.Frolov@kaspersky.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index 0b46ec29e64e..039fd47ebf52 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -54,10 +54,12 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, if (burst) { *burst_exp = ilog2(burst) ? ilog2(burst) - 1 : 0; tmp = burst - rounddown_pow_of_two(burst); - if (burst < max_mantissa) + if (burst <= max_mantissa) { *burst_mantissa = tmp * 2; - else + } else { + WARN_ON(*burst_exp < 7); *burst_mantissa = tmp / (1ULL << (*burst_exp - 7)); + } } else { *burst_exp = MAX_BURST_EXPONENT; *burst_mantissa = max_mantissa; -- cgit v1.2.3 From dedd34b0f2310e28c5f6d4875cfbf4b7ed821c01 Mon Sep 17 00:00:00 2001 From: Zijie Huang Date: Sat, 1 Aug 2026 21:42:33 +0800 Subject: net/sched: reject overly deep qdisc hierarchies Deep qdisc hierarchies can lead to excessive recursion in qdisc tree walkers and exhaust the kernel stack. The existing loop check does not cover the create-and-graft path, so a hierarchy can still be extended by creating a new child qdisc below an already deep parent. Store the hierarchy depth in struct Qdisc and update it when qdiscs are grafted. Reject new child qdiscs once the parent is already at the maximum allowed depth. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Suggested-by: Jamal Hadi Salim Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zijie Huang Signed-off-by: Ren Wei Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/1e9ab39597423fd5d13cfaaf52279b8ee3d9fc3c.1785434373.git.milkory@outlook.com Acked-by: Jamal Hadi Salim Signed-off-by: Paolo Abeni --- include/net/sch_generic.h | 1 + net/sched/sch_api.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 45a1e8c78222..cbc248776511 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -99,6 +99,7 @@ struct Qdisc { struct hlist_node hash; u32 handle; u32 parent; + int depth; struct netdev_queue *dev_queue; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 668bcd60d183..65b35528d125 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1114,6 +1114,9 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, unsigned int i, num_q, ingress; struct netdev_queue *dev_queue; + if (new) + new->depth = 0; + ingress = 0; num_q = dev->num_tx_queues; if ((q && q->flags & TCQ_F_INGRESS) || @@ -1211,9 +1214,15 @@ skip: NL_SET_ERR_MSG(extack, "STAB not supported on a non root"); return -EINVAL; } + if (new && parent->depth >= 7) { + NL_SET_ERR_MSG(extack, "Qdisc hierarchy is too deep"); + return -E2BIG; + } err = cops->graft(parent, cl, new, &old, extack); if (err) return err; + if (new) + new->depth = parent->depth + 1; notify_and_destroy(net, skb, n, classid, old, new, extack); } return 0; -- cgit v1.2.3 From d8eb9a06b922418598eac38fb9772ea4e9ef4273 Mon Sep 17 00:00:00 2001 From: Zijie Huang Date: Sat, 1 Aug 2026 21:42:34 +0800 Subject: selftests/tc-testing: add qdisc hierarchy depth tests Add tdc coverage for the qdisc hierarchy depth limit. The tests verify that the deepest allowed hierarchy can still be created and that adding another child qdisc below it is rejected. Signed-off-by: Zijie Huang Assisted-by: Codex:gpt-5.4 Signed-off-by: Ren Wei Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/5cc2d26a7c8e553759cdd29a3116f843fabc25ba.1785434373.git.milkory@outlook.com Acked-by: Jamal Hadi Salim Signed-off-by: Paolo Abeni --- .../tc-testing/tc-tests/qdiscs/depth.json | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tools/testing/selftests/tc-testing/tc-tests/qdiscs/depth.json diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/depth.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/depth.json new file mode 100644 index 000000000000..48f8683f0d1a --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/depth.json @@ -0,0 +1,74 @@ +[ + { + "id": "ad43", + "name": "Create qdisc hierarchy at maximum depth", + "category": [ + "qdisc", + "depth" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle 1: htb default 1", + "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2: htb default 1", + "$TC class add dev $DUMMY parent 2: classid 2:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 2:1 handle 3: htb default 1", + "$TC class add dev $DUMMY parent 3: classid 3:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 3:1 handle 4: htb default 1", + "$TC class add dev $DUMMY parent 4: classid 4:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 4:1 handle 5: htb default 1", + "$TC class add dev $DUMMY parent 5: classid 5:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 5:1 handle 6: htb default 1", + "$TC class add dev $DUMMY parent 6: classid 6:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 6:1 handle 7: htb default 1", + "$TC class add dev $DUMMY parent 7: classid 7:1 htb rate 10mbit" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 7:1 handle 8: htb default 1", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc htb 8: parent 7:1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] + }, + { + "id": "c519", + "name": "Reject qdisc hierarchy above maximum depth", + "category": [ + "qdisc", + "depth" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle 1: htb default 1", + "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2: htb default 1", + "$TC class add dev $DUMMY parent 2: classid 2:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 2:1 handle 3: htb default 1", + "$TC class add dev $DUMMY parent 3: classid 3:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 3:1 handle 4: htb default 1", + "$TC class add dev $DUMMY parent 4: classid 4:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 4:1 handle 5: htb default 1", + "$TC class add dev $DUMMY parent 5: classid 5:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 5:1 handle 6: htb default 1", + "$TC class add dev $DUMMY parent 6: classid 6:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 6:1 handle 7: htb default 1", + "$TC class add dev $DUMMY parent 7: classid 7:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 7:1 handle 8: htb default 1", + "$TC class add dev $DUMMY parent 8: classid 8:1 htb rate 10mbit" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 8:1 handle 9: htb default 1", + "expExitCode": "2", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc htb 9:", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] + } +] -- cgit v1.2.3 From 26505e1b5b546e2fa9a0296b951ca158460c72d8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 23 Jul 2026 10:15:22 +0200 Subject: KVM: SVM: make svm_flush_tlb_gva do a full asid flush if NPT enabled Red Hat is seeing multiple reports of Windows memory corruptions (and consequent BSODs) with hv-tlbflush=on, on AMD processors only. The crashes, while extremely rare, happen even with a stock configuration, but with Driver Verifier enabled they can be detected after approximately 200 VM hours. In particular, Alexander Lougovski measured the following: - on AMD Turin, 15 crashes in 3300 VM hours - on AMD Milan, 2 crashes in 500 VM hours (there are fewer hours here due to the host being smaller) - on Intel Sapphire Rapids, 0 crashes in 8000 VM hours - on AMD Turin with full TLB flush (not exactly this patch but similar), no crashes in ~2 weeks of run time which should also be ~7000 VM hours For Turin, the microcode version was 0x0b002162, which (assuming this is the same issue) should not be affected by the problem listed in https://knowledge.broadcom.com/external/article/419026/bsod-on-virtual-machines-running-on-amd.html; on the other hand that problem should not apply to earlier processors. AMD has not provided any information or analysis yet, and when we asked we didn't know yet that it reproduced on Milan as well. As to the workload, Alexander threw more or less everything at the same time at the VM: - a full Windows Defender scan every 30 minutes - a disk I/O job - a loop doing repeated mmap of system files (mostly to hope that it triggers some consistency check in the Windows memory manager) - SQL Express 2022 + StressDB (1.6M rows), with the host doing queries (75% write/25% read) via sqlcmd Driver Verifier is able to detect BSODs more or less at the same time as the pages are freed. They mostly happen in the Windows Defender filter driver, but occasionally also in the networking stack (e.g., afd.sys) or elsewhere in the filesystem stack (e.g., fltmgr.sys). The flush is issued from kvm_hv_vcpu_flush_tlb(), which receives the cross-CPU requests from the Hyper-V TLB flush hypercalls via a kfifo and is invoked by the KVM_REQ_HV_TLB_FLUSH request. The mechanism is the same for both Intel and AMD, and the handler for both vendors is a simple INVVPID(ADDR)/INVLPGA instruction. Because the request is handled on the destination CPU, there is a question of what happens if the VM is migrated across physical CPUs. In that case, the INVLPGA instruction would use a stale svm->vmcb->control.asid; but if anything that might do an *unnecessary* flush (on an asid that's being used for another VM) and then pre_svm_run() would force a full TLB rebuild. So, for lack of better ideas, this patch forces a full ASID bump in svm_flush_tlb_gva(). To avoid paying the price on Intel and also to avoid unnecessary loops on AMD, the flush_tlb_gva op now returns whether it did a full flush or not; kvm_hv_vcpu_flush_tlb() takes note and exits its loops immediately. While there is an obvious performance impact, about half of the benefit from Hyper-V tlbflush is preserved (10% vs. 20% on the SQL Server workload). kvm_mmu_invalidate_addr() is the only other caller of the flush_tlb_gva op. The change would have a performance impact on every intercepted INVLPG and, for nested SVM, on every L1 INVLPGA. For INVLPGA specifically, this covers the same suspected issue but for nested hypervisors, so it is correct to apply the workaround; for INVLPG on shadow paging, instead, the impact would be stronger and, due to lack of data, for now the use of INVLPGA is left in place in svm_flush_tlb_gva(). Analyzed-by: Vitaly Kuznetsov Analyzed-by: Alexander Lougovski Signed-off-by: Paolo Bonzini --- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/hyperv.c | 7 ++++--- arch/x86/kvm/mmu/mmu.c | 2 +- arch/x86/kvm/svm/svm.c | 25 +++++++++++++++++++------ arch/x86/kvm/vmx/main.c | 4 ++-- arch/x86/kvm/vmx/vmx.c | 2 +- arch/x86/kvm/vmx/x86_ops.h | 2 +- 7 files changed, 29 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 5f6c1ce9673b..6db5b5f79df9 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1858,7 +1858,7 @@ struct kvm_x86_ops { * Can potentially get non-canonical addresses through INVLPGs, which * the implementation may choose to ignore if appropriate. */ - void (*flush_tlb_gva)(struct kvm_vcpu *vcpu, gva_t addr); + void (*flush_tlb_gva)(struct kvm_vcpu *vcpu, gva_t addr, bool *full); /* * Flush any TLB entries created by the guest. Like tlb_flush_gva(), diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index fd4eb1e561f7..d8736b5dfcd3 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -1974,6 +1974,7 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu) u64 entries[KVM_HV_TLB_FLUSH_FIFO_SIZE]; int i, j, count; gva_t gva; + bool full = false; if (!tdp_enabled || !hv_vcpu) return -EINVAL; @@ -1982,7 +1983,7 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu) count = kfifo_out(&tlb_flush_fifo->entries, entries, KVM_HV_TLB_FLUSH_FIFO_SIZE); - for (i = 0; i < count; i++) { + for (i = 0; i < count && !full; i++) { if (entries[i] == KVM_HV_TLB_FLUSHALL_ENTRY) goto out_flush_all; @@ -1991,11 +1992,11 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu) * pages to flush. */ gva = entries[i] & PAGE_MASK; - for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1; j++) { + for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1 && !full; j++) { if (is_noncanonical_invlpg_address(gva + j * PAGE_SIZE, vcpu)) continue; - kvm_x86_call(flush_tlb_gva)(vcpu, gva + j * PAGE_SIZE); + kvm_x86_call(flush_tlb_gva)(vcpu, gva + j * PAGE_SIZE, &full); } ++vcpu->stat.tlb_flush; diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 66e69d2a41b3..c9e4739b26d7 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -6652,7 +6652,7 @@ void kvm_mmu_invalidate_addr(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, if (is_noncanonical_invlpg_address(addr, vcpu)) return; - kvm_x86_call(flush_tlb_gva)(vcpu, addr); + kvm_x86_call(flush_tlb_gva)(vcpu, addr, NULL); } if (!mmu->sync_spte) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index d0971685034b..60495447f0ca 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4227,18 +4227,31 @@ static void svm_flush_tlb_all(struct kvm_vcpu *vcpu) svm_flush_tlb_asid(vcpu); } -static void svm_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t gva) +static void svm_flush_tlb_guest(struct kvm_vcpu *vcpu) { - struct vcpu_svm *svm = to_svm(vcpu); + kvm_register_mark_dirty(vcpu, VCPU_REG_ERAPS); - invlpga(gva, svm->vmcb->control.asid); + svm_flush_tlb_asid(vcpu); } -static void svm_flush_tlb_guest(struct kvm_vcpu *vcpu) +static void svm_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t gva, bool *full) { - kvm_register_mark_dirty(vcpu, VCPU_REG_ERAPS); + struct vcpu_svm *svm = to_svm(vcpu); - svm_flush_tlb_asid(vcpu); + /* + * INVLPGA has had errata on Genoa and Turin, and even on older + * generations there were reports of Windows BSODs if INVLPGA + * was used for Hyper-V tlbflush. Use it only for shadow paging + * where it seems to be okay. + */ + if (!npt_enabled) { + invlpga(gva, svm->vmcb->control.asid); + return; + } + + svm_flush_tlb_guest(vcpu); + if (full) + *full = true; } static inline void sync_cr8_to_lapic(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c index 83d9921277ea..f204a0fc0a57 100644 --- a/arch/x86/kvm/vmx/main.c +++ b/arch/x86/kvm/vmx/main.c @@ -535,12 +535,12 @@ static void vt_flush_tlb_current(struct kvm_vcpu *vcpu) vmx_flush_tlb_current(vcpu); } -static void vt_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr) +static void vt_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full) { if (is_td_vcpu(vcpu)) return; - vmx_flush_tlb_gva(vcpu, addr); + vmx_flush_tlb_gva(vcpu, addr, full); } static void vt_flush_tlb_guest(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index cc75feec05da..b8d745f6fd22 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3361,7 +3361,7 @@ void vmx_flush_tlb_current(struct kvm_vcpu *vcpu) vpid_sync_context(vmx_get_current_vpid(vcpu)); } -void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr) +void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full) { /* * vpid_sync_vcpu_addr() is a nop if vpid==0, see the comment in diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h index 409858074246..17595d52985c 100644 --- a/arch/x86/kvm/vmx/x86_ops.h +++ b/arch/x86/kvm/vmx/x86_ops.h @@ -82,7 +82,7 @@ void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags); bool vmx_get_if_flag(struct kvm_vcpu *vcpu); void vmx_flush_tlb_all(struct kvm_vcpu *vcpu); void vmx_flush_tlb_current(struct kvm_vcpu *vcpu); -void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr); +void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full); void vmx_flush_tlb_guest(struct kvm_vcpu *vcpu); void vmx_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask); u32 vmx_get_interrupt_shadow(struct kvm_vcpu *vcpu); -- cgit v1.2.3 From 4f5f23846d6713c05481c8cfc710bb23817bf5e9 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 5 Aug 2026 23:28:35 +0200 Subject: rust: io: gate ioremap/iounmap on CONFIG_HAS_IOMEM s390 does not provide ioremap()/iounmap() when CONFIG_HAS_IOMEM is not set (which requires CONFIG_PCI on that architecture). This causes a build failure with Rust enabled on e.g. s390 allnoconfig: In file included from rust/helpers/helpers.c:68: rust/helpers/io.c:8:9: error: call to undeclared function 'ioremap'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 8 | return ioremap(offset, size); | ^ rust/helpers/io.c:19:2: error: call to undeclared function 'iounmap'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 19 | iounmap(addr); Guard the C helpers behind #ifdef CONFIG_HAS_IOMEM and cfg-gate the Rust io::mem module, such that IoMem, ExclusiveIoMem and IoRequest are not available without CONFIG_HAS_IOMEM. Note that the C API is inconsistent about this. For instance, devm_ioremap() has no stub and produces a link failure without CONFIG_HAS_IOMEM, whereas devm_platform_ioremap_resource() provides an inline stub returning -EINVAL. The approach taken here (compile-time gating) matches the former, which is slightly more appropriate since any driver performing MMIO currently requires CONFIG_HAS_IOMEM. Ideally, s390 should provide ioremap()/iounmap() stubs unconditionally (as UML already does), removing the need for any config gating as discussed in [1]; a follow-up patch for s390 is expected. Cc: Arnd Bergmann Reported-by: Miguel Ojeda Closes: https://lore.kernel.org/all/20260803180931.97202-1-ojeda@kernel.org [1] Fixes: 3f70ebe63858 ("s390: Enable Rust support") Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20260805212920.1996937-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/helpers/io.c | 2 ++ rust/kernel/io.rs | 1 + rust/kernel/platform.rs | 9 +++++---- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rust/helpers/io.c b/rust/helpers/io.c index 397810864a24..1edbc274951c 100644 --- a/rust/helpers/io.c +++ b/rust/helpers/io.c @@ -3,6 +3,7 @@ #include #include +#ifdef CONFIG_HAS_IOMEM __rust_helper void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size) { return ioremap(offset, size); @@ -18,6 +19,7 @@ __rust_helper void rust_helper_iounmap(void __iomem *addr) { iounmap(addr); } +#endif /* CONFIG_HAS_IOMEM */ __rust_helper u8 rust_helper_readb(const void __iomem *addr) { diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index fcc7678fd9e3..d30bb5c6d4fc 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -9,6 +9,7 @@ use crate::{ prelude::*, // }; +#[cfg(CONFIG_HAS_IOMEM)] pub mod mem; pub mod poll; pub mod register; diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 9b362e0495d3..d41555a4b31d 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -17,10 +17,7 @@ use crate::{ from_result, to_result, // }, - io::{ - mem::IoRequest, - Resource, // - }, + io::Resource, irq::{ self, IrqRequest, // @@ -31,6 +28,9 @@ use crate::{ ThisModule, // }; +#[cfg(CONFIG_HAS_IOMEM)] +use crate::io::mem::IoRequest; + use core::{ marker::PhantomData, mem::offset_of, @@ -307,6 +307,7 @@ impl Device { } } +#[cfg(CONFIG_HAS_IOMEM)] impl Device { /// Returns an `IoRequest` for the resource at `index`, if any. pub fn io_request_by_index(&self, index: u32) -> Option> { -- cgit v1.2.3 From f88db65aece9f9d26287b6377b7c7730ecf9f11a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 5 Aug 2026 23:28:36 +0200 Subject: rust: io: gate ioremap doctests on CONFIG_HAS_IOMEM The doc examples in io.rs and devres.rs directly call bindings::ioremap() and bindings::iounmap(), which do not exist when CONFIG_HAS_IOMEM is not set. This causes build failures with CONFIG_RUST_KERNEL_DOCTESTS=y on such configurations (e.g. s390 allnoconfig). Gate the affected doctests with `#![cfg(CONFIG_HAS_IOMEM)]` so they are skipped when IOMEM is unavailable. Fixes: 3f70ebe63858 ("s390: Enable Rust support") Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20260805212920.1996937-2-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/devres.rs | 1 + rust/kernel/io.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 11ce500e9b76..8ff8aedf251a 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -59,6 +59,7 @@ struct Inner { /// # Examples /// /// ```no_run +/// # #![cfg(CONFIG_HAS_IOMEM)] /// use kernel::{ /// bindings, /// device::{ diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index d30bb5c6d4fc..d4063ee41200 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -81,6 +81,7 @@ impl MmioRaw { /// # Examples /// /// ```no_run +/// # #![cfg(CONFIG_HAS_IOMEM)] /// use kernel::{ /// bindings, /// ffi::c_void, -- cgit v1.2.3 From 1d78d33275ef2a16c6d080910b291d0a97a0e613 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Aug 2026 16:44:17 +0200 Subject: KVM: SVM: Serialize accesses to the owner and mirror list with separate lock Interaction between KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM and KVM_CAP_VM_COPY_ENC_CONTEXT_FROM can cause two separate issues: - in sev_migrate_from(), when the destination KVM is a mirror, the mirror entry is moved from the source's list to the owner's mirror_vms list, without holding the owner's lock unlike other writers of the owner's mirror list (sev_vm_copy_enc_context_from(), sev_vm_destroy()). A concurrent COPY or destroy can race with sev_migrate_from() and corrupt the list. - In sev_vm_destroy(), the *owner* is still active and could receive concurrently a KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM that causes sev->enc_context_owner to change. In this case the incorrect VM receives kvm_put_kvm(). The second issue needs particular care because the owner could disappear altogether (even though the race window is impossibly small) between reading it and locking it. There is thus no way to perform the checks under the owner lock without putting struct kvm under SLAB_TYPESAFE_BY_RCU (which would allow kvm_get_kvm_safe() under RCU critical section). It is much simpler to just use a global lock, since the critical sections are so small and the new lock is always a leaf lock. Fixes: b2125513dfc0 ("KVM: SEV: Allow SEV intra-host migration of VM with mirrors") Cc: stable@vger.kernel.org Reported-by: Shen Yongchao Link: https://lore.kernel.org/kvm/tencent_625C0F42824E542C72B34733392AF2C49709@qq.com/ Link: https://lore.kernel.org/kvm/tencent_DDC4E4352EC91CAC05A9A8F4E55E8C96730A@qq.com/ Signed-off-by: Paolo Bonzini --- arch/x86/kvm/svm/sev.c | 34 +++++++++++++++++++++++++--------- arch/x86/kvm/svm/svm.h | 1 + 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 944aaea6501f..0f0ea7896af5 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -97,6 +97,8 @@ static u64 sev_supported_vmsa_features __ro_after_init; static u8 sev_enc_bit; static DECLARE_RWSEM(sev_deactivate_lock); static DEFINE_MUTEX(sev_bitmap_lock); +/* Protects kvm_sev_info's enc_context_owner, mirror_vms and mirror_entry. */ +static DEFINE_MUTEX(sev_mirror_lock); unsigned int max_sev_asid; static unsigned int min_sev_asid; static unsigned int max_sev_es_asid; @@ -2018,7 +2020,6 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) dst->asid = src->asid; dst->handle = src->handle; dst->pages_locked = src->pages_locked; - dst->enc_context_owner = src->enc_context_owner; dst->es_active = src->es_active; dst->vmsa_features = src->vmsa_features; @@ -2026,11 +2027,12 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) src->active = false; src->handle = 0; src->pages_locked = 0; - src->enc_context_owner = NULL; src->es_active = false; list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list); + mutex_lock(&sev_mirror_lock); + /* * If this VM has mirrors, "transfer" each mirror's refcount of the * source to the destination (this KVM). The caller holds a reference @@ -2047,12 +2049,15 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) * If this VM is a mirror, remove the old mirror from the owners list * and add the new mirror to the list. */ - if (is_mirroring_enc_context(dst_kvm)) { - struct kvm_sev_info *owner_sev_info = to_kvm_sev_info(dst->enc_context_owner); + if (is_mirroring_enc_context(src_kvm)) { + struct kvm_sev_info *owner_sev_info = to_kvm_sev_info(src->enc_context_owner); + dst->enc_context_owner = src->enc_context_owner; + src->enc_context_owner = NULL; list_del(&src->mirror_entry); list_add_tail(&dst->mirror_entry, &owner_sev_info->mirror_vms); } + mutex_unlock(&sev_mirror_lock); kvm_for_each_vcpu(i, dst_vcpu, dst_kvm) { dst_svm = to_svm(dst_vcpu); @@ -2871,11 +2876,14 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) * disappear until we're done with it */ source_sev = to_kvm_sev_info(source_kvm); - kvm_get_kvm(source_kvm); - list_add_tail(&mirror_sev->mirror_entry, &source_sev->mirror_vms); /* Set enc_context_owner and copy its encryption context over */ + mutex_lock(&sev_mirror_lock); + kvm_get_kvm(source_kvm); + list_add_tail(&mirror_sev->mirror_entry, &source_sev->mirror_vms); mirror_sev->enc_context_owner = source_kvm; + mutex_unlock(&sev_mirror_lock); + mirror_sev->active = true; mirror_sev->asid = source_sev->asid; mirror_sev->fd = source_sev->fd; @@ -2963,11 +2971,19 @@ void sev_vm_destroy(struct kvm *kvm) * Note, mirror VMs don't support registering encrypted regions. */ if (is_mirroring_enc_context(kvm)) { - struct kvm *owner_kvm = sev->enc_context_owner; + struct kvm *owner_kvm; - mutex_lock(&owner_kvm->lock); + mutex_lock(&sev_mirror_lock); + owner_kvm = sev->enc_context_owner; list_del(&sev->mirror_entry); - mutex_unlock(&owner_kvm->lock); + sev->enc_context_owner = NULL; + + /* + * The reference to owner_kvm cannot move after sev_mirror_lock is + * released. Release it before kvm_put_kvm() so that owner_kvm is + * never destroyed inside sev_mirror_lock. + */ + mutex_unlock(&sev_mirror_lock); kvm_put_kvm(owner_kvm); return; } diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 716be21fba33..d63e5878988a 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -109,6 +109,7 @@ struct kvm_sev_info { u64 ap_jump_table; /* SEV-ES AP Jump Table address */ u64 vmsa_features; u16 ghcb_version; /* Highest guest GHCB protocol version allowed */ + /* The three fields below are protected by sev_mirror_lock */ struct kvm *enc_context_owner; /* Owner of copied encryption context */ struct list_head mirror_vms; /* List of VMs mirroring */ struct list_head mirror_entry; /* Use as a list entry of mirrors */ -- cgit v1.2.3 From 7fc1c937b6b37c77df4ba374c37435ab06a2e945 Mon Sep 17 00:00:00 2001 From: Łukasz Patron Date: Tue, 4 Aug 2026 22:18:30 +0200 Subject: net: qrtr: ns: Raise lookup limit to 128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current limit of 64 is not enough for Sony Xperia 10 VII (SM6475). After merging v6.6.142 into a downstream AOSP device, it's stuck on boot animation and following log spam can be observed in dmesg: E qrtr : ctrl_cmd_new_lookup(): QRTR client node exceeds max lookup limit! E qrtr : qrtr_ns_worker(): failed while handling packet from 1:16600 No idea why it needs more than 64 client lookups, but it appears to work fine with 128 as it did when there were no limits. I don't really have a good way to investigate what it needs all these lookups for as most of the userspace is closed source. Fixes: 5640227d9a21 ("net: qrtr: ns: Limit the maximum number of lookups") Signed-off-by: Łukasz Patron Link: https://patch.msgid.link/20260804201919.1148015-1-priv.luk@gmail.com Signed-off-by: Jakub Kicinski --- net/qrtr/ns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index e5b2adb161d9..c5e7e01db249 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -78,7 +78,7 @@ struct qrtr_node { */ #define QRTR_NS_MAX_NODES 512 #define QRTR_NS_MAX_SERVERS 256 -#define QRTR_NS_MAX_LOOKUPS 64 +#define QRTR_NS_MAX_LOOKUPS 128 static u16 node_count; -- cgit v1.2.3 From 68bf02b6b4ad3f748c6db71fd77b6c0402d252f4 Mon Sep 17 00:00:00 2001 From: Fan XinRan Date: Mon, 3 Aug 2026 14:38:50 +0000 Subject: net: thunderbolt: Tear down DMA paths before stopping the rings tbnet_tear_down() stops both rings and frees their frame buffers before calling tb_xdomain_disable_paths(). tb_ring_stop() zeroes the ring's descriptor base and tbnet_free_buffers() unmaps and frees the pages the frames sit in, so by the time __tb_path_deactivate_hop() polls the hop's 'pending' bit, anything still in flight has nowhere to drain to. The teardown sequence has been in this order since the driver was added. The setup path has not: commit ff7cd07f3064 ("net: thunderbolt: Enable DMA paths only after rings are enabled") moved the path enable to the end of tbnet_connected_work() and documented why: /* Both logins successful so enable the rings, high-speed DMA * paths and start the network device queue. * * Note we enable the DMA paths last to make sure we have primed * the Rx ring before any incoming packets are allowed to * arrive. */ Teardown was never updated to match, so the rings and the paths now come down in the same order they go up instead of in reverse. On an ASMedia ASM4242 host router the 'pending' bit then never clears: every teardown burns the full 500 ms timeout and __tb_path_deactivate_hop() returns -ETIMEDOUT. Raising the timeout to 5 s does not help, so the hop is not slow to drain, it never drains at all. The failure is invisible above the thunderbolt core. __tb_path_deactivate_hops() is void and only calls tb_port_warn(); tb_path_deactivate(), tb_tunnel_deactivate() and __tb_disconnect_xdomain_paths() are void as well, and tb_disconnect_xdomain_paths() ends in an unconditional "return 0". So tb_xdomain_disable_paths() reports success and the netdev_warn() below it never fires. Repeated teardowns eventually take the XDomain control channel down, after which the peer node is gone and only a power cycle brings the controller back. Deactivating the paths first fixes it. Measured with kretprobes on a stock v6.17 tree with no other patches applied, on a link that was up and had just carried traffic: before: __tb_path_deactivate_hop() returns 0 for the first hop, then -ETIMEDOUT for the second 500335 us later after: 0 for both, 525 us apart Alternating the two orderings ABBA over three load levels, four teardowns per arm: every teardown failed before the change (21 of 21 that ran), none failed after (0 of 24). The before arms ran short because the link died partway through. The same split shows up when the interface is enslaved to a bond instead of just brought down, which is how I ran into this in the first place. Throughput and latency after the change are unchanged. Hosts whose routers drain the hop despite the stale descriptor base see no functional difference, since the paths end up deactivated either way. Fixes: e69b6c02b4c3 ("net: Add support for networking over Thunderbolt cable") Signed-off-by: Fan XinRan Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260803-b4-tbnet-teardown-v2-1-27de6a13ca2d@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/thunderbolt/main.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index c5670d61820c..98893732bc6e 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -386,11 +386,16 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout) break; } - tb_ring_stop(net->rx_ring.ring); - tb_ring_stop(net->tx_ring.ring); - tbnet_free_buffers(&net->rx_ring); - tbnet_free_buffers(&net->tx_ring); - + /* Tear the paths down before stopping the rings. This mirrors + * tbnet_connected_work(), which enables the paths last so the + * Rx ring is primed before packets can arrive. Stopping a + * ring zeroes its descriptor base and tbnet_free_buffers() + * unmaps and frees the frame buffers, leaving anything still + * in flight with nowhere to drain to; + * __tb_path_deactivate_hop() then waits for the hop's + * 'pending' bit, which on some host routers never clears in + * that state. + */ ret = tb_xdomain_disable_paths(net->xd, net->local_transmit_path, net->tx_ring.ring->hop, @@ -399,6 +404,11 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout) if (ret) netdev_warn(net->dev, "failed to disable DMA paths\n"); + tb_ring_stop(net->rx_ring.ring); + tb_ring_stop(net->tx_ring.ring); + tbnet_free_buffers(&net->rx_ring); + tbnet_free_buffers(&net->tx_ring); + tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path); net->remote_transmit_path = 0; } -- cgit v1.2.3 From fde39b8a521780391fb4e5bda2c0aa4928947f12 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Sun, 2 Aug 2026 14:06:02 +0200 Subject: net: usb: ipheth: fix carrier_work UAF on disconnect ipheth_sndbulk_callback() re-arms the carrier-check work on any non-zero URB status: else schedule_delayed_work(&dev->carrier_work, 0); Nothing ties that to the interface being up, so the work can be armed again after ipheth_close() has already drained it, and stay armed until the netdev whose private area embeds it is freed. On unplug with a TX URB in flight, ipheth_disconnect() drains the work through unregister_netdev() -> ipheth_close() -> cancel_delayed_work_sync() and only then calls ipheth_kill_urbs(). usb_kill_urb() completes the in-flight TX URB with -ENOENT, so ipheth_sndbulk_callback() runs after the drain and re-arms carrier_work. The same completion also re-arms the work if the interface is only brought down while a TX URB is in flight, and ipheth_carrier_check_work() then keeps re-queueing itself once a second. unregister_netdev() does not call ipheth_close() for an already-down interface, so nothing drains it on the later unplug either. In both cases free_netdev() frees the netdev while carrier_work is still pending, and ipheth_carrier_check_work() dereferences freed memory. Tie the work to the interface state instead of chasing the completion: disable it in ipheth_close() and enable it in ipheth_open(), so a schedule_delayed_work() from the URB completion is a no-op whenever the interface is not up. disable_delayed_work_sync() also waits for a running instance, so it fully replaces the cancel_delayed_work_sync() it takes the place of. The work starts out disabled in ipheth_probe() so the enable/disable counts balance from the first open. Reproduced under KASAN on linux-next (next-20260731) with dummy_hcd and raw-gadget standing in for the device, driving the second path above (the interface is already down, so unregister_netdev() does not call ipheth_close()): 15 of 15 unpatched boots report a slab-use-after-free in __run_timers(), freed by ipheth_disconnect() and re-armed from ipheth_sndbulk_callback() via queue_delayed_work_on(). The same trigger on a kernel differing only by this patch reports 0 of 15, and the carrier check still functions across open/close cycles. The reproducer needs an attached USB device that stops draining bulk OUT, plus a link down and unplug, driven as root. It is not a privilege boundary crossing and no exploit primitive was developed. Found by 0sec (https://0sec.ai). Fixes: bb1b40c7cb86 ("usbnet: ipheth: prevent TX queue timeouts when device not ready") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Link: https://patch.msgid.link/20260802120602.42595-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski --- drivers/net/usb/ipheth.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index bb1364f85bd1..2b490114d232 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -490,6 +490,7 @@ static int ipheth_open(struct net_device *net) if (retval) return retval; + enable_delayed_work(&dev->carrier_work); schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); return retval; } @@ -499,7 +500,11 @@ static int ipheth_close(struct net_device *net) struct ipheth_device *dev = netdev_priv(net); netif_stop_queue(net); - cancel_delayed_work_sync(&dev->carrier_work); + /* A TX URB can still complete with an error after this point and + * try to re-arm the carrier work. Disable it instead of cancelling + * it, so that such a schedule_delayed_work() is a no-op. + */ + disable_delayed_work_sync(&dev->carrier_work); return 0; } @@ -629,6 +634,10 @@ static int ipheth_probe(struct usb_interface *intf, } INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work); + /* Armed only between ipheth_open() and ipheth_close(). Start out + * disabled so the enable/disable counts balance from the first open. + */ + disable_delayed_work(&dev->carrier_work); retval = ipheth_alloc_urbs(dev); if (retval) { -- cgit v1.2.3 From 35772b4981f38ba8059372cde8753e8e477e98ec Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 3 Aug 2026 18:16:33 +0200 Subject: mptcp: options: reset DSS fields in case of unexpected size A remote peer could send a malformed DSS with a wrong size, followed by another DSS or MPC + Data. In this case, the first suboption will be ignored, but leaving some fields written, which could lead to inconsistency or access uninitialized data. Explicitly reset the fields that could have been modified in case of unexpected size. Link: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260728-net-mptcp-misc-fixes-7-2-rc6-v1-0-f7e2d229159d%40kernel.org?part=1 Fixes: 648ef4b88673 ("mptcp: Implement MPTCP receive path") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-1-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index c664023d37ba..038eca33c6b1 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -188,8 +188,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, * RFC 8684 Section 3.3.0 checks later in subflow_data_ready */ if (opsize != expected_opsize && - opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM) + opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM) { + mp_opt->dsn64 = 0; + mp_opt->use_map = 0; + mp_opt->ack64 = 0; + mp_opt->use_ack = 0; + mp_opt->data_fin = 0; break; + } mp_opt->suboptions |= OPTION_MPTCP_DSS; if (mp_opt->use_ack) { -- cgit v1.2.3 From b6ee361524641f57b2e2363f7737f20e17f67827 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 3 Aug 2026 18:16:34 +0200 Subject: mptcp: avoid combining some incoming suboptions Some MPTCP suboptions are mutually exclusive according to the RFC8684, but also because in different places, the code doesn't expect some combinations to be present. That's specially true for suboptions that would be present twice, but with different attributes. The new restrictions are the same as the ones applied on the output side, with mptcp_write_options. The same rules can be reused with a small fix: an MP_FASTCLOSE can be used with a DSS when the sender picks this option [1], which is not the case on Linux. Here are the rules: Which options can be used together? X: mutually exclusive O: often used together C: can be used together in some cases P: could be used together but we prefer not to (optimisations) | Opt: | MPC | MPJ | DSS | ADD | RM | PRIO | FAIL | FC | |------|------|------|------|------|------|------|------|------| | MPC |------|------|------|------|------|------|------|------| | MPJ | X |------|------|------|------|------|------|------| | DSS | X | X |------|------|------|------|------|------| | ADD | X | X | P |------|------|------|------|------| | RM | C | C | C | P |------|------|------|------| | PRIO | X | C | C | C | C |------|------|------| | FAIL | X | X | C | X | X | X |------|------| | FC | X | X | P | X | X | X | X |------| | RST | X | X | X | X | X | X | O | O | |------|------|------|------|------|------|------|------|------| The only difference is with the 'P': another stack could send and ADD_ADDR with other suboptions (DSS, RM_ADDR), and this should be allowed. A few points of attention: - In theory, an MP_CAPABLE could be used with a RM_ADDR, but there is no reason to add it with a SYN. Note that even with a 4th ACK, it doesn't seem to be useful, except when IDs are known in advance via another channel. Better not to break that. - Now, combining both an MP_CAPABLE and an MP_JOIN will no longer result to a reject of the two options, but only the second suboption is ignored. That seems OK to do that for this unexpected error. At least now all inconsistent combinations are handled the same way. This could change later in next. This also means the explicit checks for having both MPC + MPJ in subflow.c will now be unreachable. That's fine, they will be removed in a follow-up patch. - In case of conflicting combinations, the extra suboption(s) is/are ignored: having such combinations either means the remote peer is buggy, or is evil. The simplest action is then taken in this case: stop processing the current suboption. - In mp_opt->suboptions, there is also a bit reserved to the checksum, which can be used in an MP_CAPABLE and a DSS. Each time a DSS option can be used in parallel with another option, the checksum can be set, so the verification is combined into a new OPTIONS_MPTCP_DSS macro. - An MP_CAPABLE ACK can carry a Data-Level Length, and an optional Checksum: they are the same as the ones found in a DSS, because a DSS cannot be used in parallel to an MP_CAPABLE. Similarly, even if there is room, a DSS cannot be used with an MP_JOIN. Fixes: eda7acddf808 ("mptcp: Handle MPTCP TCP options") Cc: stable@vger.kernel.org Link: https://www.rfc-editor.org/rfc/rfc8684.html#section-3.5-5.1 [1] Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-2-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++- net/mptcp/protocol.h | 1 + 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 038eca33c6b1..1057d500577b 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -50,6 +50,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, } } + /* Only the MPC + ACK can be used with a RM_ADDR */ + if (subopt == OPTION_MPTCP_MPC_ACK) { + if ((mp_opt->suboptions & ~OPTION_MPTCP_RM_ADDR) != 0) + break; + } else if (mp_opt->suboptions != 0) { + break; + } + /* Cfr RFC 8684 Section 3.3.0: * If a checksum is present but its use had * not been negotiated in the MP_CAPABLE handshake, the receiver MUST @@ -122,6 +130,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_JOIN: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + if (opsize == TCPOLEN_MPTCP_MPJ_SYN) { mp_opt->suboptions |= OPTION_MPTCP_MPJ_SYN; mp_opt->backup = *ptr++ & MPTCPOPT_BACKUP; @@ -153,6 +166,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_DSS: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO | + OPTION_MPTCP_FASTCLOSE | + OPTION_MPTCP_FAIL)) != 0) + break; + pr_debug("DSS\n"); ptr++; @@ -240,6 +261,12 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_ADD_ADDR: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + mp_opt->echo = (*ptr++) & MPTCP_ADDR_ECHO; if (!mp_opt->echo) { if (opsize == TCPOLEN_MPTCP_ADD_ADDR || @@ -299,6 +326,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_RM_ADDR: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_MPC_ACK | + OPTIONS_MPTCP_MPJ | + OPTIONS_MPTCP_DSS | + OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + if (opsize < TCPOLEN_MPTCP_RM_ADDR_BASE + 1 || opsize > TCPOLEN_MPTCP_RM_ADDR_BASE + MPTCP_RM_IDS_MAX) break; @@ -313,6 +348,13 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_PRIO: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_MPJ | + OPTIONS_MPTCP_DSS | + OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_RM_ADDR)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_PRIO) break; @@ -322,6 +364,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_FASTCLOSE: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RST)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_FASTCLOSE) break; @@ -333,6 +380,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_RST: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_FAIL | + OPTION_MPTCP_FASTCLOSE)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_RST) break; @@ -348,6 +400,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_FAIL: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RST)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_FAIL) break; @@ -1406,7 +1463,7 @@ void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp, * RM | C | C | C | P |------|------|------|------| * PRIO | X | C | C | C | C |------|------|------| * FAIL | X | X | C | X | X | X |------|------| - * FC | X | X | X | X | X | X | X |------| + * FC | X | X | P | X | X | X | X |------| * RST | X | X | X | X | X | X | O | O | * ------|------|------|------|------|------|------|------|------| * diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 4a2d40cd7b13..c13680d18994 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -37,6 +37,7 @@ OPTION_MPTCP_MPC_ACK) #define OPTIONS_MPTCP_MPJ (OPTION_MPTCP_MPJ_SYN | OPTION_MPTCP_MPJ_SYNACK | \ OPTION_MPTCP_MPJ_ACK) +#define OPTIONS_MPTCP_DSS (OPTION_MPTCP_DSS | OPTION_MPTCP_CSUMREQD) /* MPTCP option subtypes */ #define MPTCPOPT_MP_CAPABLE 0 -- cgit v1.2.3 From 900e6d80548e69be7ffd358122790fbaa458a9b4 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 3 Aug 2026 18:16:35 +0200 Subject: mptcp: remove MPC && MPJ check After the parent commit ("mptcp: avoid combining some incoming suboptions"), the parsing step no longer allow to have both the MP_CAPABLE and MP_JOIN suboptions set together. These chunks are now unreachable, these checks can then be removed. Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-3-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/subflow.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 8e386899ceb9..e1f20ff8fdb4 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -174,8 +174,6 @@ static int subflow_check_req(struct request_sock *req, if (unlikely(listener->pm_listener)) return subflow_reset_req_endp(req, skb); - if (opt_mp_join) - return 0; } else if (opt_mp_join) { SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINSYNRX); @@ -277,9 +275,6 @@ int mptcp_subflow_init_cookie_req(struct request_sock *req, opt_mp_capable = !!(mp_opt.suboptions & OPTION_MPTCP_MPC_ACK); opt_mp_join = !!(mp_opt.suboptions & OPTION_MPTCP_MPJ_ACK); - if (opt_mp_capable && opt_mp_join) - return -EINVAL; - if (opt_mp_capable && listener->request_mptcp) { if (mp_opt.sndr_key == 0) return -EINVAL; -- cgit v1.2.3 From a7aad5b69d3bdaec20a3ed9284e184502450c0cd Mon Sep 17 00:00:00 2001 From: Qing Luo Date: Mon, 3 Aug 2026 18:16:36 +0200 Subject: mptcp: pm: fix data race in add_addr timer callback The timer callback reads entry->retrans_times outside pm.lock to decide whether to call mptcp_pm_subflow_established(). Since mptcp_pm_announced_del_timer() can concurrently set retrans_times = ADD_ADDR_RETRANS_MAX under pm.lock, a race condition exists. I discovered this issue while studying the code. AI tools helped me to verify the issue can potentially happen under race conditions. Use a local 'retransmit' flag set inside pm.lock to capture whether retransmission is still possible when the lock is taken. This allows to call mptcp_pm_subflow_established() accordingly, and not depending on the situation that can be different when checked outside the pm.lock. Fixes: 348d5c1dec60 ("mptcp: move to next addr when timeout") Cc: stable@vger.kernel.org Signed-off-by: Qing Luo Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-4-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/pm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 6afd39aea110..c71dcf887683 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -380,6 +380,7 @@ static void mptcp_pm_add_addr_timer(struct timer_list *timer) struct mptcp_sock *msk = entry->sock; struct sock *sk = (struct sock *)msk; unsigned int timeout = 0; + bool retransmit; pr_debug("msk=%p\n", msk); @@ -412,14 +413,15 @@ static void mptcp_pm_add_addr_timer(struct timer_list *timer) entry->retrans_times++; } - if (entry->retrans_times < ADD_ADDR_RETRANS_MAX) + retransmit = entry->retrans_times < ADD_ADDR_RETRANS_MAX; + if (retransmit) timeout <<= entry->retrans_times; else timeout = 0; spin_unlock_bh(&msk->pm.lock); - if (entry->retrans_times == ADD_ADDR_RETRANS_MAX) + if (!retransmit) mptcp_pm_subflow_established(msk); out: -- cgit v1.2.3 From ca318e7bbb7723f57bcd9e69a2873b5884435552 Mon Sep 17 00:00:00 2001 From: Gang Yan Date: Mon, 3 Aug 2026 18:16:37 +0200 Subject: selftests: mptcp: join: mark tests with data corruption as failed check_transfer() compares the input and output files byte-by-byte using `cmp -l "$in" "$out" | while read ...`. Because the while-loop body runs in a subshell (the script sets neither lastpipe nor pipefail), the fail_test call inside it -- which sets the global ret/last_test_failed -- and the `return 1` both act on the subshell, not on check_transfer(). check_transfer() thus always falls through to `return 0`, and any data corruption affecting only the payload (leaving the subflow/PM counters untouched) is silently reported as PASS. Fixes: 8117dac3e7c3 ("selftests: mptcp: add invert check in check_transfer") Cc: stable@vger.kernel.org Signed-off-by: Gang Yan Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-5-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index c0aeffd5cb71..7dc91fac4917 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -584,7 +584,7 @@ check_transfer() mv "$tmpfile" "$out" tmpfile="" fi - cmp -l "$in" "$out" | while read -r i a b; do + while read -r i a b; do local sum=$((0${a} + 0${b})) if [ $check_invert -eq 0 ] || [ $sum -ne $((0xff)) ]; then fail_test "$what does not match (in, out):" @@ -595,7 +595,7 @@ check_transfer() else print_info "$what has inverted byte at ${i}" fi - done + done < <(cmp -l "$in" "$out") return 0 } -- cgit v1.2.3 From efc33b5102ff859bacd390a5f30112d8e0c084c0 Mon Sep 17 00:00:00 2001 From: Shardul Bankar Date: Mon, 3 Aug 2026 18:16:38 +0200 Subject: mptcp: pm: fix memory leak from alloc-during-teardown race mptcp_pm_destroy() empties msk->pm.anno_list and msk->pm.userspace_pm_local_addr_list under msk->pm.lock during socket teardown, dropping the lock between the two. A concurrent userspace PM genl ANNOUNCE on the same msk holds a sock reference via mptcp_token_get_sock() and, in mptcp_pm_nl_announce_doit(), calls mptcp_userspace_pm_append_new_local_addr() and mptcp_pm_announced_alloc(). Both take msk->pm.lock briefly to add to their respective lists. Because the genl handler holds a sock reference, mptcp_pm_destroy() may run on the same msk via mptcp_disconnect(), which invokes mptcp_destroy_common() without dropping the sock refcount, before the handler completes. If the lock acquisitions interleave such that mptcp_pm_destroy() empties a list first, the later alloc adds its entry to a list head that nothing else iterates for this msk, and the entry leaks. kmemleak reports both mptcp_pm_add_addr objects (from mptcp_pm_announced_alloc()) and mptcp_pm_addr_entry objects (from mptcp_userspace_pm_append_new_local_addr()) under sustained concurrent ANNOUNCE + close load against the userspace PM. Add an MPTCP_PM_DESTROYING bit in msk->pm.status, set by mptcp_pm_destroy() under pm.lock before the lists are emptied and checked under pm.lock by the alloc paths. Either the alloc takes pm.lock first, in which case its entry is on the list when mptcp_pm_destroy() frees it; or mptcp_pm_destroy() takes pm.lock first, in which case the later alloc observes the bit and refuses. Found by an MPTCP protocol-flow harness extending BRF (arXiv:2305.08782). Fixes: 9ab4807c84a4 ("mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE") Cc: stable@vger.kernel.org Signed-off-by: Shardul Bankar Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-6-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/pm.c | 13 +++++++++++-- net/mptcp/pm_userspace.c | 4 ++++ net/mptcp/protocol.h | 7 ++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index c71dcf887683..64a1236aabee 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -443,6 +443,9 @@ bool mptcp_pm_announced_alloc(struct mptcp_sock *msk, lockdep_assert_held(&msk->pm.lock); + if (msk->pm.status & BIT(MPTCP_PM_DESTROYING)) + return false; + add_entry = mptcp_pm_announced_lookup(msk, addr); if (add_entry) { if (WARN_ON_ONCE(mptcp_pm_is_kernel(msk))) @@ -1145,10 +1148,16 @@ void mptcp_pm_worker(struct mptcp_sock *msk) void mptcp_pm_destroy(struct mptcp_sock *msk) { + spin_lock_bh(&msk->pm.lock); + msk->pm.status |= BIT(MPTCP_PM_DESTROYING); + spin_unlock_bh(&msk->pm.lock); + mptcp_pm_free_announced_list(msk); - if (mptcp_pm_is_userspace(msk)) - mptcp_userspace_pm_free_local_addr_list(msk); + /* Free the userspace local address list unconditionally: the socket + * can be reused (mptcp_disconnect()) and re-selected to a different PM + */ + mptcp_userspace_pm_free_local_addr_list(msk); } void mptcp_pm_data_reset(struct mptcp_sock *msk) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index 945aa5afc2dd..2203cc2d2748 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -54,6 +54,10 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, bitmap_zero(id_bitmap, MPTCP_PM_MAX_ADDR_ID + 1); spin_lock_bh(&msk->pm.lock); + if (msk->pm.status & BIT(MPTCP_PM_DESTROYING)) { + ret = -EINVAL; + goto append_err; + } mptcp_for_each_userspace_pm_addr(msk, e) { addr_match = mptcp_addresses_equal(&e->addr, &entry->addr, true); if (addr_match && entry->addr.id == 0 && needs_id) diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index c13680d18994..1b80f2d6ec5a 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -190,9 +190,10 @@ enum mptcp_pm_status { MPTCP_PM_ESTABLISHED, MPTCP_PM_SUBFLOW_ESTABLISHED, MPTCP_PM_ALREADY_ESTABLISHED, /* persistent status, set after ESTABLISHED event */ - MPTCP_PM_MPC_ENDPOINT_ACCOUNTED /* persistent status, set after MPC local address is - * accounted int id_avail_bitmap - */ + MPTCP_PM_MPC_ENDPOINT_ACCOUNTED, /* persistent status, set after MPC local address is + * accounted int id_avail_bitmap + */ + MPTCP_PM_DESTROYING, /* To fence out PM list allocs */ }; enum mptcp_pm_type { -- cgit v1.2.3 From e00b63056fb4f261455b3e5df5268a1f8ce47a87 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Mon, 3 Aug 2026 18:16:39 +0200 Subject: mptcp: fastopen: only mark MPTFO subflows with SYN data Passive TCP Fast Open accepts a valid-cookie SYN even when it carries no data. In that case the child socket's receive queue is intentionally left empty. mptcp_fastopen_subflow_synack_set_params() set is_mptfo before checking for queued SYN data. That made data-less TFO SYNs hit a WARN and, if the warning was non-fatal, left stale MPTFO state behind. The stale flag could later trigger a state-confusion bug in check_fully_established(). Only mark the subflow as MPTFO after confirming that an SKB was queued. Return quietly when the receive queue is empty. Note that mptcp_subflow_context's is_mptfo field is now not just about subflows where the TFO was present, but about MPTFO subflow that consumed SYN data. Only having a valid cookie but not carrying data is not really "doing TFO". Fixes: 36b122baf6a8 ("mptcp: add subflow_v(4,6)_send_synack()") 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 Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-7-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/fastopen.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c index 082c46c0f50e..f717750906ff 100644 --- a/net/mptcp/fastopen.c +++ b/net/mptcp/fastopen.c @@ -24,12 +24,13 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf sk = subflow->conn; tp = tcp_sk(ssk); - subflow->is_mptfo = 1; - + /* A valid TFO cookie does not guarantee SYN data. */ skb = skb_peek(&ssk->sk_receive_queue); - if (WARN_ON_ONCE(!skb)) + if (!skb) return; + subflow->is_mptfo = 1; + /* dequeue the skb from sk receive queue */ __skb_unlink(skb, &ssk->sk_receive_queue); skb_ext_reset(skb); -- cgit v1.2.3 From 41b49a8b914ec7dcb03eae93fb27f3c464078644 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 3 Aug 2026 18:16:40 +0200 Subject: mptcp: reclaim forward-allocated memory on RX path errors After commit 9db5b3cec4ec ("mptcp: borrow forward memory from subflow"), errors in the receive path prior to queueing skbs into the receive queue do not trigger forward-allocated memory reclaiming. Prevent forward memory from growing unboundedly in pathological drop scenarios by explicitly reclaiming memory when skbs are dropped. Fixes: 9db5b3cec4ec ("mptcp: borrow forward memory from subflow") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-8-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index ca644ec53eed..7c8180d8d5ef 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -149,6 +149,12 @@ struct sock *__mptcp_nmpc_sk(struct mptcp_sock *msk) static void mptcp_drop(struct sock *sk, struct sk_buff *skb) { + /* The skb forward memory was already transferred to sk by + * mptcp_borrow_fwdmem(), even before setting the destructor. + */ + if (!skb->destructor) + sk_mem_reclaim(sk); + sk_drops_skbadd(sk, skb); __kfree_skb(skb); } -- cgit v1.2.3 From e48e8edbef2eb824201495daa5234560f632b23c Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Mon, 3 Aug 2026 20:15:32 +0800 Subject: xdp: reject clones that overrun skb_shared_info tailroom xdpf_clone() clones broadcast copies into a single page and sets frame_sz to PAGE_SIZE. __xdp_build_skb_from_frame() later treats that page like a normal XDP frame and expects the usual skb_shared_info tailroom at the end of the buffer. The current check only rejects frames whose linear xdp_frame header, headroom, and packet data exceed PAGE_SIZE. A source frame backed by a larger allocation can still satisfy that check while extending into the clone's required shared-info area. When such a clone is converted back into an skb, build_skb_around() places skb_shared_info over live packet bytes and later writes can corrupt XDP return metadata. Reject clones unless their linear area fits inside SKB_WITH_OVERHEAD(PAGE_SIZE), matching the tailroom requirement already enforced by the XDP-to-skb conversion path. Fixes: e624d4ed4aa8 ("xdp: Extend xdp_redirect_map with broadcast support") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Link: https://patch.msgid.link/6b2afef5d1738763c6965e8e466eb16e43e4f956.1785757386.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/core/xdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/xdp.c b/net/core/xdp.c index 9890a30584ba..0194e69da339 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -871,7 +871,7 @@ struct xdp_frame *xdpf_clone(struct xdp_frame *xdpf) headroom = xdpf->headroom + sizeof(*xdpf); totalsize = headroom + xdpf->len; - if (unlikely(totalsize > PAGE_SIZE)) + if (unlikely(totalsize > SKB_WITH_OVERHEAD(PAGE_SIZE))) return NULL; page = dev_alloc_page(); if (!page) -- cgit v1.2.3 From 7700a31039cdc6715cb6cce7e7a664ee4e945f67 Mon Sep 17 00:00:00 2001 From: Hongyan Xu Date: Thu, 6 Aug 2026 14:06:28 +0800 Subject: ata: pata_sl82c105: fix bridge revision use-after-free pci_get_slot() returns a referenced PCI device. Commit 44c10138fd4b ("PCI: Change all drivers to use pci_device->revision") replaced a configuration-space read with direct access to the cached revision field, but left that access after pci_dev_put(). The bridge may therefore be freed before its revision is read. Read the revision before dropping the reference. Fixes: 44c10138fd4b ("PCI: Change all drivers to use pci_device->revision") Signed-off-by: Hongyan Xu Reviewed-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/pata_sl82c105.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c index 2d24c6b3e9d9..25bce3fe2a10 100644 --- a/drivers/ata/pata_sl82c105.c +++ b/drivers/ata/pata_sl82c105.c @@ -264,6 +264,7 @@ static struct ata_port_operations sl82c105_port_ops = { static int sl82c105_bridge_revision(struct pci_dev *pdev) { struct pci_dev *bridge; + u8 revision; /* * The bridge should be part of the same device, but function 0. @@ -285,8 +286,9 @@ static int sl82c105_bridge_revision(struct pci_dev *pdev) /* * We need to find function 0's revision, not function 1 */ + revision = bridge->revision; pci_dev_put(bridge); - return bridge->revision; + return revision; } static void sl82c105_fixup(struct pci_dev *pdev) -- cgit v1.2.3 From 7bca91d63341274e857f4aeaad54d229405e93dc Mon Sep 17 00:00:00 2001 From: chanyoung Date: Tue, 4 Aug 2026 14:28:35 +0900 Subject: tls: don't leave a full plaintext sk_msg ring unpushed When the copy path in tls_sw_sendmsg_locked() adds the fragment that fills the plaintext sk_msg ring, it does not set full_record, so the record is left full and unpushed. A later splice() then adds to an already full ring: sk_msg_page_add() has no fullness check of its own, so sg.end wraps onto sg.start and the ring appears empty. Fragments added after that overwrite live entries, and sg.size no longer matches what is reachable between sg.start and sg.end, so pushing the record runs the scatterwalk off the end of the scatterlist. An unprivileged user can trigger this on a loopback TCP socket with the "tls" ULP attached: BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: 0010:memcpy_from_scatterwalk+0x32/0xc0 Call Trace: skcipher_walk_next+0x1d1/0x2c0 gcm_encrypt_aesni_avx+0x1e9/0x220 bpf_exec_tx_verdict+0x3bb/0x860 tls_sw_sendmsg+0xa1a/0xca0 __sys_sendto+0x1da/0x1f0 Set full_record in the copy path when the ring becomes full, and push a record that is already full on entry to the sendmsg loop. Suggested-by: Sabrina Dubroca Fixes: fe1e81d4f73b ("tls/sw: Support MSG_SPLICE_PAGES") Cc: stable@vger.kernel.org Signed-off-by: chanyoung Link: https://patch.msgid.link/20260804052837.49015-2-ppoo1220@gmail.com Signed-off-by: Jakub Kicinski --- net/tls/tls_sw.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index d4afc90fd796..d2e399be8ef6 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -832,6 +832,14 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf; + /* open record may be full if we couldn't push it in the last sendmsg call */ + if (sk_msg_full(msg_pl)) { + full_record = true; + sk_msg_trim(sk, msg_en, + msg_pl->sg.size + prot->overhead_size); + goto copied; + } + alloc_encrypted: ret = tls_alloc_encrypted_msg(sk, required_size); if (ret) { @@ -921,6 +929,12 @@ fallback_to_reg_send: msg_pl, try_to_copy); if (ret < 0) goto trim_sgl; + + if (sk_msg_full(msg_pl)) { + full_record = true; + sk_msg_trim(sk, msg_en, + msg_pl->sg.size + prot->overhead_size); + } } /* Open records defined only if successfully copied, otherwise -- cgit v1.2.3 From 3834e079d67feda5fd57d1ea8d4185d316997335 Mon Sep 17 00:00:00 2001 From: chanyoung Date: Tue, 4 Aug 2026 14:28:36 +0900 Subject: selftests: tls: add a test for splicing onto a full plaintext record Splicing onto a plaintext sk_msg ring that is already full used to wrap the ring and make the kernel oops in the scatterwalk once the record was pushed. Only the copy path leaves the ring full without pushing it, so splice until the ring is one fragment short, add the last fragment with a one-byte MSG_MORE send, and splice once more before pushing the record. CONFIG_MAX_SKB_FRAGS is 17..45, so that last fragment follows between 16 and 44 splices; sweep that range to trigger the bug on any build. Signed-off-by: chanyoung Link: https://patch.msgid.link/20260804052837.49015-3-ppoo1220@gmail.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/tls.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index cbdd3ea28b99..3d6f553eaf97 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -835,6 +835,43 @@ TEST_F(tls, send_and_splice) EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); } +TEST_F(tls, splice_onto_full_record) +{ + char mem_send[4608]; + char mem_recv[4608]; + int frag_len = 100; + int nfrags, i, off; + int p[2]; + + memrnd(mem_send, sizeof(mem_send)); + ASSERT_GE(pipe(p), 0); + + for (nfrags = 16; nfrags <= 44; nfrags++) { + for (i = 0, off = 0; i < nfrags; i++, off += frag_len) { + EXPECT_EQ(write(p[1], mem_send + off, frag_len), frag_len); + EXPECT_EQ(splice(p[0], NULL, self->fd, NULL, frag_len, + SPLICE_F_MORE), frag_len); + } + + EXPECT_EQ(send(self->fd, mem_send + off, 1, MSG_MORE), 1); + off++; + + EXPECT_EQ(write(p[1], mem_send + off, frag_len), frag_len); + EXPECT_EQ(splice(p[0], NULL, self->fd, NULL, frag_len, + SPLICE_F_MORE), frag_len); + off += frag_len; + + EXPECT_EQ(send(self->fd, mem_send + off, 1, 0), 1); + off++; + + EXPECT_EQ(recv(self->cfd, mem_recv, off, MSG_WAITALL), off); + EXPECT_EQ(memcmp(mem_send, mem_recv, off), 0); + } + + close(p[0]); + close(p[1]); +} + TEST_F(tls, splice_to_pipe) { int send_len = TLS_PAYLOAD_MAX_LEN; -- cgit v1.2.3 From 1c8629651cb54f7b51db8fc0b1a9944e4a4b0f5e Mon Sep 17 00:00:00 2001 From: Jérémy Jean Date: Tue, 4 Aug 2026 12:55:28 +0000 Subject: tls: rx: restore msg_iter before TLS 1.3 optimistic retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tls_decrypt_sg() advances msg->msg_iter when it maps user pages for the optimistic TLS 1.3 zero-copy path. If the decrypted record turns out not to be unpadded application data, tls_decrypt_sw() retries into a kernel skb, but leaves the iterator advanced. The subsequent copy from the skb then writes decrypted bytes again at a later point in the caller iovecs while recvmsg() reports only the post-retry length. A TLS peer can trigger this after the receiver enables TLS_RX_EXPECT_NO_PAD. Revert the iterator by the number of bytes consumed by the optimistic mapping before retrying without zero-copy. Add a selftest which sends a TLS 1.3 control record with TLS_RX_EXPECT_NO_PAD enabled and verifies that recvmsg() does not overwrite later iovecs beyond the returned length. Fixes: ce61327ce989 ("tls: rx: support optimistic decrypt to user buffer with TLS 1.3") Cc: stable@vger.kernel.org Signed-off-by: Jérémy Jean Link: https://patch.msgid.link/20260804125528.2139928-1-Jeremy.Jean@oss.cyber.gouv.fr Signed-off-by: Jakub Kicinski --- net/tls/tls_sw.c | 2 ++ tools/testing/selftests/net/tls.c | 57 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index d2e399be8ef6..133ed7c89402 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1456,6 +1456,8 @@ tls_decrypt_sw(struct sock *sk, struct tls_context *tls_ctx, /* If opportunistic TLS 1.3 ZC failed retry without ZC */ if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION && darg->tail != TLS_RECORD_TYPE_DATA)) { + iov_iter_revert(&msg->msg_iter, strp_msg(darg->skb)->full_len - + prot->overhead_size); darg->zc = false; if (!darg->tail) TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXNOPADVIOL); diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 3d6f553eaf97..a0e72e402748 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -1839,6 +1839,63 @@ TEST_F(tls, recv_efault) } #define TLS_RECORD_TYPE_HANDSHAKE 0x16 + +TEST_F(tls_basic, recvmsg_nopad_retry_iov) +{ + char payload[32]; + char first_iov[sizeof(payload)]; + char later_iov[sizeof(payload) * 2]; + char expected_later_iov[sizeof(later_iov)]; + char cbuf[CMSG_SPACE(sizeof(char))]; + struct tls_crypto_info_keys tls13; + struct iovec iov[] = { + { .iov_base = first_iov, .iov_len = sizeof(first_iov) }, + { .iov_base = later_iov, .iov_len = sizeof(later_iov) }, + }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + }; + int one = 1; + int ret; + int i; + + if (self->notls) + SKIP(return, "no TLS support"); + + tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128, + &tls13, 0); + + ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls13, tls13.len); + ASSERT_EQ(ret, 0); + + ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls13, tls13.len); + ASSERT_EQ(ret, 0); + + ret = setsockopt(self->cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD, + &one, sizeof(one)); + ASSERT_EQ(ret, 0); + + for (i = 0; i < sizeof(payload); i++) + payload[i] = 0x40 + i; + memset(first_iov, 0xa5, sizeof(first_iov)); + memset(later_iov, 0x5a, sizeof(later_iov)); + memset(expected_later_iov, 0x5a, sizeof(expected_later_iov)); + + /* A control record forces optimistic TLS 1.3 RX to retry. */ + ret = tls_send_cmsg(self->fd, TLS_RECORD_TYPE_HANDSHAKE, + payload, sizeof(payload), 0); + ASSERT_EQ(ret, sizeof(payload)); + + ret = recvmsg(self->cfd, &msg, 0); + ASSERT_EQ(ret, sizeof(payload)); + EXPECT_EQ(memcmp(first_iov, payload, sizeof(payload)), 0); + EXPECT_EQ(memcmp(later_iov, expected_later_iov, + sizeof(later_iov)), 0); +} + /* key_update, length 1, update_not_requested */ static const char key_update_msg[] = "\x18\x00\x00\x01\x00"; static void tls_send_keyupdate(struct __test_metadata *_metadata, int fd) -- cgit v1.2.3 From 6b69f2ef10cdb018c0b127a7cab88e590bbddba4 Mon Sep 17 00:00:00 2001 From: Ahmad Byagowi Date: Tue, 4 Aug 2026 14:07:51 -0700 Subject: ptp: ocp: Fix board ID over-read The EEPROM board ID is a fixed 13-byte field and is not guaranteed to contain a NUL terminator. Passing it directly to devlink_info_version_fixed_put() treats it as a C string and may read beyond the field. Format at most OCP_BOARD_ID_LEN bytes into the existing local buffer before reporting the ID. Use a precision limit because the snprintf() output size alone does not bound the source string scan. Fixes: 0cfcdd1ebcfe ("ptp: ocp: add nvmem interface for accessing eeprom") Cc: stable@vger.kernel.org Signed-off-by: Ahmad Byagowi Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260804210751.48248-1-ahmadexp@gmail.com Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_ocp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 35e911f1ad78..3d26ec1f7b9e 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -2177,9 +2177,11 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, if (err) return err; + snprintf(buf, sizeof(buf), "%.*s", OCP_BOARD_ID_LEN, + (const char *)bp->board_id); err = devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, - bp->board_id); + buf); if (err) return err; -- cgit v1.2.3 From bfec39ff1484b4e9f7d93bc4580fdb634bbc7d19 Mon Sep 17 00:00:00 2001 From: Bhargava Marreddy Date: Wed, 5 Aug 2026 15:10:22 +0530 Subject: bnge: Fix resource leak in bnge_init_nic() error path If bnge_init_chip() fails, bnge_init_nic() jumps to err_free_ring_grps and returns immediately, skipping cleanup for RX ring pair buffers. Remove the early return so execution falls through to err_free_rx_ring_pair_bufs to properly free resources on error. Fixes: 23df6aebf803 ("bng_en: Allocate stat contexts") Signed-off-by: Bhargava Marreddy Reviewed-by: Dharmender Garg Reviewed-by: Rajashekar Hudumula Link: https://patch.msgid.link/20260805094022.15487-1-bhargava.marreddy@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index 6f7ef506d4e1..ac4c93e5b634 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -2768,8 +2768,6 @@ static int bnge_init_nic(struct bnge_net *bn) err_free_ring_grps: bnge_free_ring_grps(bn); - return rc; - err_free_rx_ring_pair_bufs: bnge_free_rx_ring_pair_bufs(bn); return rc; -- cgit v1.2.3 From 3b9a324e646d3657a8d9806dfbfe4f3e4066e882 Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Wed, 5 Aug 2026 20:57:27 +0800 Subject: net: remove CAP_SYS_RAWIO zero-padding in dev_validate_header dev_validate_header() reads dev->hard_header_len directly when zero-padding short link layer headers for CAP_SYS_RAWIO holders: if (capable(CAP_SYS_RAWIO)) { memset(ll_header + len, 0, dev->hard_header_len - len); return true; } Packet send paths call dev_validate_header() on skbs whose headroom was allocated from an earlier hard_header_len read. If the device is reconfigured so that dev->hard_header_len increases before validation, the memset writes past the reserved buffer, an out-of-bounds write. This out-of-bounds write is masked in some SOCK_RAW paths today because the same concurrent increase can first make skb_push() exceed the reserved headroom and trigger skb_under_panic(). Remove the zero-padding branch before making those hard_header_len reads consistent, so the snapshot fixes do not turn a loud panic into a silent overwrite. This path is only reached for variable length L2 protocols, where len < hard_header_len but len >= min_header_len. No remaining in-tree variable length L2 protocol implements header_ops->validate, and the CAP_SYS_RAWIO bypass that zero-pads and accepts short headers has no real value beyond allowing testing of intentionally malformed input. Drop the CAP_SYS_RAWIO branch. The remaining reads of dev->hard_header_len in dev_validate_header() are comparisons only and have no memory safety impact. Suggested-by: Willem de Bruijn Fixes: 2793a23aacbd ("net: validate variable length ll headers") Cc: stable@vger.kernel.org Signed-off-by: Qihang Tang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260805125729.19220-2-q.h.hack.winter@gmail.com Signed-off-by: Jakub Kicinski --- include/linux/netdevice.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9981d637f8b5..9a770eb823ce 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3531,11 +3531,6 @@ static inline bool dev_validate_header(const struct net_device *dev, if (len < dev->min_header_len) return false; - if (capable(CAP_SYS_RAWIO)) { - memset(ll_header + len, 0, dev->hard_header_len - len); - return true; - } - if (dev->header_ops && dev->header_ops->validate) return dev->header_ops->validate(ll_header, len); -- cgit v1.2.3 From 03390aa32e669cc4ecd7d34108e2e1afc13d689d Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Wed, 5 Aug 2026 20:57:28 +0800 Subject: packet: use consistent hard_header_len in non-ring send paths packet_snd() reads dev->hard_header_len multiple times while allocating and constructing an skb. Device reconfiguration can change this value concurrently, for example through bonding device type changes. For SOCK_RAW, packet_snd() can save a larger value in reserve and later allocate headroom using a smaller value. Moving skb->data back by reserve then places it before skb->head, and the following copy from userspace can attempt an out-of-bounds write. packet_sendmsg_spkt() has the same issue because it calculates its reservation and header offset from separate reads before dropping the RCU read lock to allocate the skb. Add LL_RESERVED_SPACE_EX() for callers that already saved a header length. Read hard_header_len once in packet_snd() and use it for allocation and construction. In packet_sendmsg_spkt(), preserve the allocation-time value through the device lookup retry. The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here. Fixes: b84bbaf7a6c8 ("packet: in packet_snd start writing at link layer allocation") Cc: stable@vger.kernel.org Signed-off-by: Qihang Tang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260805125729.19220-3-q.h.hack.winter@gmail.com Signed-off-by: Jakub Kicinski --- include/linux/netdevice.h | 6 ++++-- net/packet/af_packet.c | 26 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9a770eb823ce..8840b126979f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -300,9 +300,11 @@ struct hh_cache { * We could use other alignment values, but we must maintain the * relationship HH alignment <= LL alignment. */ -#define LL_RESERVED_SPACE(dev) \ - ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom)) \ +#define LL_RESERVED_SPACE_EX(dev, hlen) \ + ((((hlen) + READ_ONCE((dev)->needed_headroom)) \ & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD) +#define LL_RESERVED_SPACE(dev) \ + LL_RESERVED_SPACE_EX(dev, (dev)->hard_header_len) #define LL_RESERVED_SPACE_EXTRA(dev,extra) \ ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom) + (extra)) \ & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 0e1355be89f6..b7af45c809e4 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1966,8 +1966,9 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, struct net_device *dev; struct sockcm_cookie sockc; __be16 proto = 0; - int err; + int hard_header_len; int extra_len = 0; + int err; /* * Get and verify the address. @@ -2010,14 +2011,18 @@ retry: extra_len = 4; /* We're doing our own CRC */ } + /* Keep the allocation-time header length across retry. */ + if (!skb) + hard_header_len = READ_ONCE(dev->hard_header_len); + err = -EMSGSIZE; - if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN + extra_len) + if (len > dev->mtu + hard_header_len + VLAN_HLEN + extra_len) goto out_unlock; if (!skb) { - size_t reserved = LL_RESERVED_SPACE(dev); + size_t reserved = LL_RESERVED_SPACE_EX(dev, hard_header_len); int tlen = dev->needed_tailroom; - unsigned int hhlen = dev->header_ops ? dev->hard_header_len : 0; + unsigned int hhlen = dev->header_ops ? hard_header_len : 0; rcu_read_unlock(); skb = sock_wmalloc(sk, len + reserved + tlen, 0, GFP_KERNEL); @@ -2047,7 +2052,7 @@ retry: err = -EINVAL; goto out_unlock; } - if (len > (dev->mtu + dev->hard_header_len + extra_len) && + if (len > (dev->mtu + hard_header_len + extra_len) && !packet_extra_vlan_len_allowed(dev, skb)) { err = -EMSGSIZE; goto out_unlock; @@ -2969,7 +2974,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) int offset = 0; struct packet_sock *po = pkt_sk(sk); int vnet_hdr_sz = READ_ONCE(po->vnet_hdr_sz); - int hlen, tlen, linear; + int hard_header_len, hlen, tlen, linear; int extra_len = 0; /* @@ -3009,8 +3014,9 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) goto out_unlock; } + hard_header_len = READ_ONCE(dev->hard_header_len); if (sock->type == SOCK_RAW) - reserve = dev->hard_header_len; + reserve = hard_header_len; if (vnet_hdr_sz) { err = packet_snd_vnet_parse(msg, &len, &vnet_hdr, vnet_hdr_sz); if (err) @@ -3031,10 +3037,10 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) goto out_unlock; err = -ENOBUFS; - hlen = LL_RESERVED_SPACE(dev); + hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len); tlen = dev->needed_tailroom; linear = __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len); - linear = max(linear, min_t(int, len, dev->hard_header_len)); + linear = max(linear, min_t(int, len, hard_header_len)); skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, linear, msg->msg_flags & MSG_DONTWAIT, &err); if (skb == NULL) @@ -3050,7 +3056,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) } else if (reserve) { skb_reserve(skb, -reserve); if (len < reserve + sizeof(struct ipv6hdr) && - dev->min_header_len != dev->hard_header_len) + dev->min_header_len != hard_header_len) skb_reset_network_header(skb); } -- cgit v1.2.3 From 21b5953e7494c16a42e6cd8cf110e18d13ae4a6b Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Wed, 5 Aug 2026 20:57:29 +0800 Subject: packet: use consistent hard_header_len in TX_RING send path tpacket_snd() reads dev->hard_header_len independently for skb allocation and header construction in tpacket_fill_skb(). Concurrent netdevice reconfiguration can therefore make the reserved headroom smaller than the amount later pushed, or make copylen - hard_header_len negative. Snapshot hard_header_len once before processing ring frames and use it for the frame limit, headroom allocation, copy length, and skb construction. Pass the snapshot to tpacket_fill_skb(). The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here. Fixes: 69e3c75f4d54 ("net: TX_RING and packet mmap") Cc: stable@vger.kernel.org Signed-off-by: Qihang Tang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260805125729.19220-4-q.h.hack.winter@gmail.com Signed-off-by: Jakub Kicinski --- net/packet/af_packet.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index b7af45c809e4..435756877aba 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2587,6 +2587,7 @@ static int packet_snd_vnet_parse(struct msghdr *msg, size_t *len, static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, void *frame, struct net_device *dev, void *data, int tp_len, __be16 proto, unsigned char *addr, int hlen, int copylen, + int hard_header_len, const struct sockcm_cookie *sockc) { union tpacket_uhdr ph; @@ -2618,8 +2619,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, } else if (copylen) { int hdrlen = min_t(int, copylen, tp_len); - skb_push(skb, dev->hard_header_len); - skb_put(skb, copylen - dev->hard_header_len); + skb_push(skb, hard_header_len); + skb_put(skb, copylen - hard_header_len); err = skb_store_bits(skb, 0, data, hdrlen); if (unlikely(err)) return err; @@ -2750,7 +2751,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) void *data; int len_sum = 0; int status = TP_STATUS_AVAILABLE; - int hlen, tlen, copylen = 0; + int hard_header_len, hlen, tlen, copylen = 0; long timeo; mutex_lock(&po->pg_vec_lock); @@ -2797,8 +2798,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) goto out_put; } + hard_header_len = READ_ONCE(dev->hard_header_len); if (po->sk.sk_socket->type == SOCK_RAW) - reserve = dev->hard_header_len; + reserve = hard_header_len; size_max = po->tx_ring.frame_size - (po->tp_hdrlen - sizeof(struct sockaddr_ll)); @@ -2835,7 +2837,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) goto tpacket_error; status = TP_STATUS_SEND_REQUEST; - hlen = LL_RESERVED_SPACE(dev); + hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len); tlen = dev->needed_tailroom; if (vnet_hdr_sz) { data += vnet_hdr_sz; @@ -2853,10 +2855,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) vnet_hdr.hdr_len); has_vnet_hdr = true; } - copylen = max_t(int, copylen, dev->hard_header_len); + copylen = max_t(int, copylen, hard_header_len); skb = sock_alloc_send_skb(&po->sk, hlen + tlen + sizeof(struct sockaddr_ll) + - (copylen - dev->hard_header_len), + (copylen - hard_header_len), !need_wait, &err); if (unlikely(skb == NULL)) { @@ -2866,7 +2868,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) goto out_status; } tp_len = tpacket_fill_skb(po, skb, ph, dev, data, tp_len, proto, - addr, hlen, copylen, &sockc); + addr, hlen, copylen, hard_header_len, + &sockc); if (likely(tp_len >= 0) && tp_len > dev->mtu + reserve && !vnet_hdr_sz && -- cgit v1.2.3 From b1896543ce59c4258625a35cf41e23a9a1f80ea2 Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Wed, 5 Aug 2026 15:10:43 +0200 Subject: s390/ism: Fix UAF of sba and ieq during ism_dev_exit() A ism interrupt handler can be active in parallel with ism_dev_exit(), accessing freed data structures. No new interrupts will be generated after unregister_ieq(). Drain ongoing interrupt handlers by free_irq(), before freeing ism data structures. Fixes: 684b89bc39ce ("s390/ism: add device driver for internal shared memory") Signed-off-by: Alexandra Winter Link: https://patch.msgid.link/20260805131043.954639-1-wintera@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/s390/net/ism_drv.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index d99c588d3e00..242da20f27e0 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -148,13 +148,16 @@ static int unregister_sba(struct ism_dev *ism) if (ret && ret != ISM_ERROR) return -EIO; + return 0; +} + +static void ism_free_sba(struct ism_dev *ism) +{ dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, ism->sba, ism->sba_dma_addr); ism->sba = NULL; ism->sba_dma_addr = 0; - - return 0; } static int unregister_ieq(struct ism_dev *ism) @@ -168,13 +171,16 @@ static int unregister_ieq(struct ism_dev *ism) if (ret && ret != ISM_ERROR) return -EIO; + return 0; +} + +static void ism_free_ieq(struct ism_dev *ism) +{ dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, ism->ieq, ism->ieq_dma_addr); ism->ieq = NULL; ism->ieq_dma_addr = 0; - - return 0; } static int ism_read_local_gid(struct dibs_dev *dibs) @@ -573,6 +579,7 @@ static int ism_dev_init(struct ism_dev *ism) unreg_sba: unregister_sba(ism); + ism_free_sba(ism); free_irq: free_irq(pci_irq_vector(pdev, 0), ism); free_vectors: @@ -585,9 +592,13 @@ static void ism_dev_exit(struct ism_dev *ism) { struct pci_dev *pdev = ism->pdev; + /* ism will only generate new IRQs while ieq & sba are registered */ unregister_ieq(ism); unregister_sba(ism); + /* drain ongoing irpt handlers */ free_irq(pci_irq_vector(pdev, 0), ism); + ism_free_ieq(ism); + ism_free_sba(ism); pci_free_irq_vectors(pdev); } -- cgit v1.2.3 From d0c80dbb970439bd2eeb0e5effff8c16a5f4e1e3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 5 Aug 2026 13:15:08 +0000 Subject: net/atm: fix slab-out-of-bounds read in vcc_setsockopt() vcc_setsockopt() contained an ineffective optlen check: if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname)) return -EINVAL; If __SO_LEVEL_MATCH(optname, level) evaluated to false (e.g. if the caller passed a mismatched level), the length check optlen != __SO_SIZE(optname) was short-circuited and bypassed. Execution then fell through to switch(optname), calling copy_from_sockptr() assuming optval contained sufficient space. Furthermore, even if level matched, a cgroup BPF setsockopt filter could shrink optlen after entry. Because copy_from_sockptr() on kernel pointers uses memcpy(), this leads to a KASAN slab-out-of-bounds read when optlen is smaller than the expected structure size. Fix this by using copy_safe_from_sockptr(), which unconditionally validates that optlen is at least the expected size before copying. Also change the local 'value' variable type from 'unsigned long' to 'int' so that SO_SETCLP matches its sizeof(int) ABI encoding on 64-bit systems. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot+53ecc09fb81df10ef4de@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=53ecc09fb81df10ef4de Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260805131508.3227331-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/atm/common.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/atm/common.c b/net/atm/common.c index c7f92405daf0..81195727fa18 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -710,7 +710,7 @@ int vcc_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, unsigned int optlen) { struct atm_vcc *vcc; - unsigned long value; + int value; int error; if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname)) @@ -722,8 +722,10 @@ int vcc_setsockopt(struct socket *sock, int level, int optname, { struct atm_qos qos; - if (copy_from_sockptr(&qos, optval, sizeof(qos))) - return -EFAULT; + error = copy_safe_from_sockptr(&qos, sizeof(qos), optval, + optlen); + if (error) + return error; error = check_qos(&qos); if (error) return error; @@ -737,8 +739,10 @@ int vcc_setsockopt(struct socket *sock, int level, int optname, return 0; } case SO_SETCLP: - if (copy_from_sockptr(&value, optval, sizeof(value))) - return -EFAULT; + error = copy_safe_from_sockptr(&value, sizeof(value), optval, + optlen); + if (error) + return error; if (value) vcc->atm_options |= ATM_ATMOPT_CLP; else -- cgit v1.2.3 From c9158ceaf27780ef64534ad72f44ffde3f8ccc49 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 5 Aug 2026 11:18:40 -0400 Subject: sctp: clear control chunk transport if it is being removed sctp_make_heartbeat_ack() caches the destination transport in chunk->transport without taking a reference. When src_out_of_asoc_ok is enabled, the HEARTBEAT ACK may remain queued on control_chunk_list instead of being transmitted immediately. If the peer transport is removed while the chunk is still queued, sctp_assoc_rm_peer() drops the transport and schedules it for RCU freeing, but only clears cached transport pointers in out_chunk_list. The queued control chunk therefore retains a dangling transport pointer. Once an ASCONF_ACK clears the suppression and the queued control chunk is transmitted, SCTP dereferences the stale transport pointer, leading to a use-after-free. Fix this by also clearing chunk->transport for queued control chunks in control_chunk_list when removing the transport. Fixes: 8a07eb0a50ae ("sctp: Add ASCONF operation on the single-homed host") Reported-by: Daniele Linguaglossa Signed-off-by: Xin Long Link: https://patch.msgid.link/7e1168cb722132152a29d47e5eafaeac4a3bf6f3.1785943120.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/associola.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index b6ac0966420a..5b0ae616e1ff 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -573,6 +573,10 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, if (ch->transport == peer) ch->transport = NULL; + list_for_each_entry(ch, &asoc->outqueue.control_chunk_list, list) + if (ch->transport == peer) + ch->transport = NULL; + asoc->peer.transport_count--; sctp_ulpevent_notify_peer_addr_change(peer, SCTP_ADDR_REMOVED, 0); -- cgit v1.2.3 From 03a736fde464cefa0a6e10d29914c27635965ce2 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Wed, 5 Aug 2026 17:54:25 +0200 Subject: MAINTAINERS: dpll: zl3073x: replace Prathosh Satish with Min Li Replace Prathosh Satish by Min Li as the Microchip co-maintainer of the ZL3073X DPLL driver. Signed-off-by: Ivan Vecera Link: https://patch.msgid.link/20260805155425.38808-1-ivecera@redhat.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a77cf599bac2..9de29cdcd8c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17847,7 +17847,7 @@ F: drivers/net/wireless/microchip/ MICROCHIP ZL3073X DRIVER M: Ivan Vecera -M: Prathosh Satish +M: Min Li L: netdev@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml -- cgit v1.2.3 From 8e63c9e6179ace3033dcd19991f95386e9d3130f Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Thu, 6 Aug 2026 11:07:58 +0300 Subject: net: Defer netdev KOBJ_ADD uevent until the device is published netdev_register_kobject() calls device_add(), which emits KOBJ_ADD and wakes udev, but register_netdevice() only makes the device findable by name later, in list_netdevice(). A udev worker that reacts to the uevent can therefore run against a device that no lookup can find yet. This used to be harmless because the ethtool ioctl took the rtnl_lock when looking the device up, and register_netdevice() runs under rtnl, so the worker simply blocked until registration finished. The commit in the fixes tag moved the lookup out from under rtnl for ops-locked drivers. Now there is a short window in register_netdevice() between netdev_register_kobject() until list_netdevice() when the device is not findable by name. This was reproduced with the mlx5 driver on a kernel with KASAN enabled during devlink reload: systemd-udevd's net_driver builtin gets -ENODEV from ETHTOOL_GDRVINFO, which was preventing interface renaming. Suppress the uevent in netdev_register_kobject() and emit it from register_netdevice() next to rtmsg_ifinfo(). This is the last point in register_netdevice() where no error can happen, so only fully registered devices are announced: the registration error paths never reach it, and the device_del() that unwinds them stays silent as well, leaving userspace with neither an add nor a remove. Fixes: f994752b1127 ("net: ethtool: optionally skip rtnl_lock on IOCTL path") Signed-off-by: Dragos Tatulea Reviewed-by: Shahar Shitrit Link: https://patch.msgid.link/20260806080758.2039586-2-dtatulea@nvidia.com Signed-off-by: Jakub Kicinski --- net/core/dev.c | 1 + net/core/net-sysfs.c | 14 ++++++++++++++ net/core/net-sysfs.h | 1 + 3 files changed, 16 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index 5933c5dab09e..c49d2ce51285 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11494,6 +11494,7 @@ int register_netdevice(struct net_device *dev) * Prevent userspace races by waiting until the network * device is fully setup before sending notifications. */ + netdev_uevent_add(dev); if (!(dev->rtnl_link_ops && dev->rtnl_link_initializing)) rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL, 0, NULL); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 0e71c9ed41e8..25546deacec8 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -2334,6 +2334,9 @@ int netdev_register_kobject(struct net_device *ndev) *groups++ = &wireless_group; #endif /* CONFIG_SYSFS */ + /* Hold back the KOBJ_ADD uevent until the device is listed. */ + dev_set_uevent_suppress(dev, 1); + error = device_add(dev); if (error) return error; @@ -2349,6 +2352,17 @@ int netdev_register_kobject(struct net_device *ndev) return error; } +/* Announce a fully registered device to userspace. This pairs with the uevent + * suppression from netdev_register_kobject(). + */ +void netdev_uevent_add(struct net_device *ndev) +{ + struct device *dev = &ndev->dev; + + dev_set_uevent_suppress(dev, 0); + kobject_uevent(&dev->kobj, KOBJ_ADD); +} + /* Change owner for sysfs entries when moving network devices across network * namespaces owned by different user namespaces. */ diff --git a/net/core/net-sysfs.h b/net/core/net-sysfs.h index 38e2e3ffd0bd..2f41a4dee866 100644 --- a/net/core/net-sysfs.h +++ b/net/core/net-sysfs.h @@ -4,6 +4,7 @@ int __init netdev_kobject_init(void); int netdev_register_kobject(struct net_device *); +void netdev_uevent_add(struct net_device *dev); void netdev_unregister_kobject(struct net_device *); int net_rx_queue_update_kobjects(struct net_device *, int old_num, int new_num); int netdev_queue_update_kobjects(struct net_device *net, -- cgit v1.2.3 From d31c14e56a4f01f0689c29c0bcbcd45f65b4a473 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 5 Aug 2026 19:28:21 -0700 Subject: net: avoid theoretical races with ref drain Technically, it's illegal to take a ref on a netdev just because we have a pointer on which we already hold a ref, with no other protection. This is because our simple per-cpu refcount implementation cannot atomically read the count. Let's make sure we cancel outstanding work and never queue more work for a device we know is dead. This way taking a ref on a dev we know is on the netdev_work_list is always going to be safe. Jiangshan Yi reports that the issues is caught by ref tracker infra leading to a warning: WARNING: lib/ref_tracker.c:322 at ref_tracker_free WARNING: lib/ref_tracker.c:246 at ref_tracker_dir_exit Reported-by: Jiangshan Yi Link: https://lore.kernel.org/20260731035135.3917308-2-yijiangshan@kylinos.cn Fixes: 12c765be84d2 ("net: turn the rx_mode work into a generic netdev_work facility") Link: https://patch.msgid.link/20260806022821.2079945-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/core/dev.c | 1 + net/core/dev.h | 1 + net/core/netdev_work.c | 16 ++++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index c49d2ce51285..af260ff5462a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -12436,6 +12436,7 @@ void unregister_netdevice_many_notify(struct list_head *head, dev_tcx_uninstall(dev); dev_xdp_uninstall(dev); dev_memory_provider_uninstall(dev); + netdev_work_cancel_all(dev); netdev_unlock_ops(dev); bpf_dev_bound_netdev_unregister(dev); diff --git a/net/core/dev.h b/net/core/dev.h index 5d0b0305d3ba..b757faead4d1 100644 --- a/net/core/dev.h +++ b/net/core/dev.h @@ -179,6 +179,7 @@ enum netdev_work_core { void __netdev_work_core_sched(struct net_device *dev, unsigned long event); unsigned long __netdev_work_core_cancel(struct net_device *dev, unsigned long mask); +void netdev_work_cancel_all(struct net_device *dev); void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, unsigned int gchanges, u32 portid, diff --git a/net/core/netdev_work.c b/net/core/netdev_work.c index 3109fae132ad..e721a06d58df 100644 --- a/net/core/netdev_work.c +++ b/net/core/netdev_work.c @@ -31,6 +31,10 @@ static void netdev_work_enqueue(struct net_device *dev, unsigned long events, return; spin_lock_bh(&netdev_work_lock); + if (!dev_isalive(dev)) { + spin_unlock_bh(&netdev_work_lock); + return; + } if (list_empty(&dev->work_node)) { list_add_tail(&dev->work_node, &netdev_work_list); netdev_hold(dev, &dev->work_tracker, GFP_ATOMIC); @@ -61,6 +65,18 @@ netdev_work_dequeue(struct net_device *dev, unsigned long *pending, return events; } +void netdev_work_cancel_all(struct net_device *dev) +{ + spin_lock_bh(&netdev_work_lock); + dev->work_pending = 0; + dev->work_core_pending = 0; + if (!list_empty(&dev->work_node)) { + list_del_init(&dev->work_node); + netdev_put(dev, &dev->work_tracker); + } + spin_unlock_bh(&netdev_work_lock); +} + void netdev_work_sched(struct net_device *dev, unsigned long events) { netdev_work_enqueue(dev, events, 0); -- cgit v1.2.3 From af0e5cdd031f4f4a8f6d4160bfbda4f36872b0ed Mon Sep 17 00:00:00 2001 From: Maximilian Immanuel Brandtner Date: Wed, 5 Aug 2026 08:22:48 +0200 Subject: tls: don't abort the connection on signal-interrupted sends When a signal interrupts a blocking send, tls_tx_records() treats the resulting -ERESTARTSYS as a transmission failure and marks the socket errored via tls_err_abort() with the raw error code. Later syscalls return the kernel-internal errno 512 (ERESTARTSYS) to userspace, as the signal it stems from is no longer pending during syscall exit and thus never translated. An interrupted send is not a connection error: the partially sent record stays queued and is resent later. Interrupt error codes are therefore excluded from the abort in the same way as -EAGAIN. Fixes: b341ca51d267 ("tls: Fix tls_sw_sendmsg error handling") Signed-off-by: Maximilian Immanuel Brandtner Link: https://patch.msgid.link/20260805063109.1772314-1-maxbr@linux.ibm.com Signed-off-by: Jakub Kicinski --- net/tls/tls_sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 133ed7c89402..62d46736e24b 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -458,7 +458,7 @@ int tls_tx_records(struct sock *sk, int flags) } tx_err: - if (rc < 0 && rc != -EAGAIN) + if (rc < 0 && rc != -EAGAIN && rc != -EINTR && rc != -ERESTARTSYS) tls_err_abort(sk, rc); return rc; -- cgit v1.2.3 From b0ce5fd9fabe7c79463cf4602217d4dfeff5b1fd Mon Sep 17 00:00:00 2001 From: Philipp David Date: Tue, 4 Aug 2026 15:22:03 -0700 Subject: igc: fix netdev not re-attached after resume if interface is down __igc_resume() calls netif_device_attach() only inside the netif_running() branch, so an interface that was down during suspend is never re-attached on resume. It then stays in the not-present state that __igc_shutdown() set via netif_device_detach(): ethtool reports ENODEV and every attempt to bring the interface up fails the netif_device_present() check in __dev_open() with -ENODEV, silently, since __igc_resume() returns 0. Only reloading the driver recovers the device. This is easy to hit in practice because NetworkManager brings managed interfaces down before sleep unless Wake-on-LAN is configured, making the adapter unusable after every suspend/resume cycle with WoL disabled. Re-attach the netdev on every successful resume, as igb and e1000e do. Fixes: 6f31d6b643a3 ("igc: Refactor runtime power management flow") Cc: stable@vger.kernel.org Signed-off-by: Philipp David Reviewed-by: Aleksandr Loktionov Reviewed-by: Dima Ruinskiy Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260804222205.1580328-11-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igc/igc_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 16fcc8cf6191..39043d8ca1bf 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -7585,11 +7585,13 @@ static int __igc_resume(struct device *dev, bool rpm) err = __igc_open(netdev, true); if (!rpm) rtnl_unlock(); - if (!err) - netif_device_attach(netdev); + if (err) + return err; } - return err; + netif_device_attach(netdev); + + return 0; } static int igc_resume(struct device *dev) -- cgit v1.2.3 From c87801f545dac9fcbb84c96f280706572be00a85 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Wed, 29 Jul 2026 16:57:08 +0800 Subject: drm/amdgpu: fix JPEG v5.0.0 queue reset failure in DPG mode In DPG mode jpeg_v5_0_0_ring_reset() takes the DPG stop path, which only clears the JPEG_PG_MODE bit and never resets the JRBC. A hung ring is not recovered: the post-reset ring test times out and the driver falls back to a full MODE1 reset. Temporarily force the static power-gating path during the reset so the stop/start sequence power-cycles the JPEG block (JMI soft reset + power off/on), matching the jpeg_v4_0 reset which has no DPG path. Acked-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit 79b3612827d1adcd2008cd585961fa35a6ff20f2) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c index 72a4b2d0676f..c696a0dea32c 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_0.c @@ -648,15 +648,28 @@ static int jpeg_v5_0_0_ring_reset(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) { + struct amdgpu_device *adev = ring->adev; + u32 pg_flags = adev->pg_flags; int r; amdgpu_ring_reset_helper_begin(ring, timedout_fence); - r = jpeg_v5_0_0_stop(ring->adev); - if (r) - return r; - r = jpeg_v5_0_0_start(ring->adev); + + /* + * The DPG stop path only clears the JPEG_PG_MODE bit and never resets a + * hung JRBC, so the post-reset ring test times out and the driver falls + * back to a full MODE1 reset. Temporarily force the static power-gating + * path so the stop/start sequence actually power-cycles the JPEG block + * (JMI soft reset + ONO1 power off/on), matching the working jpeg_v4_0 + * reset. + */ + adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG; + r = jpeg_v5_0_0_stop(adev); + if (!r) + r = jpeg_v5_0_0_start(adev); + adev->pg_flags = pg_flags; if (r) return r; + return amdgpu_ring_reset_helper_end(ring, timedout_fence); } -- cgit v1.2.3 From 4301e60e406c613aea06fdc0c36bf0675b0b8a2e Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Wed, 29 Jul 2026 16:57:08 +0800 Subject: drm/amdgpu: fix JPEG v4.0.5 queue reset failure in DPG mode Like jpeg_v5_0_0, in DPG mode the ring reset path only clears the JPEG_PG_MODE bit and never resets a hung JRBC, so the post-reset ring test times out and the driver falls back to a full MODE1 reset. Temporarily force the static power-gating path during the reset so the stop/start sequence power-cycles the JPEG block (JMI soft reset + power off/on), matching the jpeg_v4_0 reset. Acked-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit 75a308eef4503a9d2bf297bef5a9317d2209e696) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c index a43582b9c876..3f61d5367d94 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_5.c @@ -772,15 +772,28 @@ static int jpeg_v4_0_5_ring_reset(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) { + struct amdgpu_device *adev = ring->adev; + u32 pg_flags = adev->pg_flags; int r; amdgpu_ring_reset_helper_begin(ring, timedout_fence); - r = jpeg_v4_0_5_stop(ring->adev); - if (r) - return r; - r = jpeg_v4_0_5_start(ring->adev); + + /* + * The DPG stop path only clears the JPEG_PG_MODE bit and never resets a + * hung JRBC, so the post-reset ring test times out and the driver falls + * back to a full MODE1 reset. Temporarily force the static power-gating + * path so the stop/start sequence actually power-cycles the JPEG block + * (JMI soft reset + static power off/on), matching the working jpeg_v4_0 + * reset. + */ + adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG; + r = jpeg_v4_0_5_stop(adev); + if (!r) + r = jpeg_v4_0_5_start(adev); + adev->pg_flags = pg_flags; if (r) return r; + return amdgpu_ring_reset_helper_end(ring, timedout_fence); } -- cgit v1.2.3 From d2dc81ed5191e7e06a8aa3e153f665d36bae8eb6 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Wed, 29 Jul 2026 16:57:08 +0800 Subject: drm/amdgpu: fix JPEG v5.3.0 queue reset failure in DPG mode Like jpeg_v5_0_0, in DPG mode the ring reset path only clears the JPEG_PG_MODE bit and never resets a hung JRBC, so the post-reset ring test times out and the driver falls back to a full MODE1 reset. Temporarily force the static power-gating path during the reset so the stop/start sequence power-cycles the JPEG block (JMI soft reset + power off/on), matching the jpeg_v4_0 reset. Acked-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit e93659cab11c48255dcac58af60203c99815586b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c index 2c3e6d9f52a6..33c2626abc80 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c @@ -655,15 +655,28 @@ static int jpeg_v5_3_0_ring_reset(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) { + struct amdgpu_device *adev = ring->adev; + u32 pg_flags = adev->pg_flags; int r; amdgpu_ring_reset_helper_begin(ring, timedout_fence); - r = jpeg_v5_3_0_stop(ring->adev); - if (r) - return r; - r = jpeg_v5_3_0_start(ring->adev); + + /* + * The DPG stop path only clears the JPEG_PG_MODE bit and never resets a + * hung JRBC, so the post-reset ring test times out and the driver falls + * back to a full MODE1 reset. Temporarily force the static power-gating + * path so the stop/start sequence actually power-cycles the JPEG block + * (JMI soft reset + static power off/on), matching the working jpeg_v4_0 + * reset. + */ + adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG; + r = jpeg_v5_3_0_stop(adev); + if (!r) + r = jpeg_v5_3_0_start(adev); + adev->pg_flags = pg_flags; if (r) return r; + return amdgpu_ring_reset_helper_end(ring, timedout_fence); } -- cgit v1.2.3 From 2d69604b4d0b9c0c0ac71624b5fafb36cf249729 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 23 Jul 2026 17:20:47 +0800 Subject: drm/amdgpu: read TRUNCATE_COORD_MODE on gfx12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TA_CNTL2.TRUNCATE_COORD_MODE selects whether texture coordinate truncation is D3D9/GL/Vulkan conformant. gfx11 reads it and reports it to userspace via AMDGPU_IDS_FLAGS_CONFORMANT_TRUNC_COORD, but gfx12 never read it, so the flag was always reported as 0 and userspace fell back to the non-conformant path. Read it in gfx_v12_0_constants_init() like gfx11 does. Fixes: 52cb80c12e8a ("drm/amdgpu: Add gfx v12_0 ip block support (v6)") Signed-off-by: Qiang Yu Reviewed-by: Marek Olšák Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 4261cbc7b03f1f56e95aeaf1492b8690fa5a253e) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index da668a8d6abd..9d7a75670bee 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -1823,6 +1823,11 @@ static void gfx_v12_0_constants_init(struct amdgpu_device *adev) gfx_v12_0_get_tcc_info(adev); adev->gfx.config.pa_sc_tile_steering_override = 0; + /* Set whether texture coordinate truncation is conformant. */ + tmp = RREG32_SOC15(GC, 0, regTA_CNTL2); + adev->gfx.config.ta_cntl2_truncate_coord_mode = + REG_GET_FIELD(tmp, TA_CNTL2, TRUNCATE_COORD_MODE); + /* XXX SH_MEM regs */ /* where to put LDS, scratch, GPUVM in FSA64 space */ mutex_lock(&adev->srbm_mutex); -- cgit v1.2.3 From cda6ab11c1a25a572857b9ea2ded4b7cb13c2895 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 30 Jul 2026 12:00:06 -0400 Subject: drm/amdgpu/gmc12.1: implement tlb inv semaphore Needed to properly lock the interface before using it. Cc: Sonny Jiang Reviewed-by: Sonny Jiang Signed-off-by: Alex Deucher (cherry picked from commit 8e37aa0bd56ba75801a6a21bed45f96372cd9fdc) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c index 855cd29cbffa..bdf6788b85b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c @@ -267,9 +267,24 @@ static bool gmc_v12_1_get_vmid_pasid_mapping_info(struct amdgpu_device *adev, * by the amdgpu vm/hsa code. */ +/** + * gmc_v12_1_use_invalidate_semaphore - judge whether to use semaphore + * + * @adev: amdgpu_device pointer + * @vmhub: vmhub type + * + */ +static bool gmc_v12_1_use_invalidate_semaphore(struct amdgpu_device *adev, + uint32_t vmhub) +{ + return ((!AMDGPU_IS_GFXHUB(vmhub)) && + (!amdgpu_sriov_vf(adev))); +} + static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid, unsigned int vmhub, uint32_t flush_type) { + bool use_semaphore = gmc_v12_1_use_invalidate_semaphore(adev, vmhub); struct amdgpu_vmhub *hub = &adev->vmhub[vmhub]; u32 inv_req = hub->vmhub_funcs->get_invalidate_req(vmid, flush_type); u32 tmp; @@ -283,6 +298,19 @@ static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid, spin_lock(&adev->gmc.invalidate_lock); + if (use_semaphore) { + for (i = 0; i < adev->usec_timeout; i++) { + /* a read return value of 1 means semaphore acuqire */ + tmp = RREG32_RLC_NO_KIQ(hub->vm_inv_eng0_sem + hub->eng_distance * eng, hub_ip); + if (tmp & 0x1) + break; + udelay(1); + } + + if (i >= adev->usec_timeout) + DRM_ERROR("Timeout waiting for sem acquire in VM flush!\n"); + } + WREG32_RLC_NO_KIQ(hub->vm_inv_eng0_req + hub->eng_distance * eng, inv_req, hub_ip); /* Wait for ACK with a delay.*/ @@ -296,6 +324,9 @@ static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid, udelay(1); } + if (use_semaphore) + WREG32_RLC_NO_KIQ(hub->vm_inv_eng0_sem + hub->eng_distance * eng, 0, hub_ip); + /* Issue additional private vm invalidation to MMHUB */ if (!AMDGPU_IS_GFXHUB(vmhub) && (hub->vm_l2_bank_select_reserved_cid2) && @@ -431,10 +462,17 @@ static void gmc_v12_1_flush_gpu_tlb_pasid(struct amdgpu_device *adev, static uint64_t gmc_v12_1_emit_flush_gpu_tlb(struct amdgpu_ring *ring, unsigned vmid, uint64_t pd_addr) { + bool use_semaphore = gmc_v12_1_use_invalidate_semaphore(ring->adev, ring->vm_hub); struct amdgpu_vmhub *hub = &ring->adev->vmhub[ring->vm_hub]; uint32_t req = hub->vmhub_funcs->get_invalidate_req(vmid, 0); unsigned eng = ring->vm_inv_eng; + if (use_semaphore) + /* a read return value of 1 means semaphore acuqire */ + amdgpu_ring_emit_reg_wait(ring, + hub->vm_inv_eng0_sem + + hub->eng_distance * eng, 0x1, 0x1); + amdgpu_ring_emit_wreg(ring, hub->ctx0_ptb_addr_lo32 + (hub->ctx_addr_distance * vmid), lower_32_bits(pd_addr)); @@ -449,6 +487,14 @@ static uint64_t gmc_v12_1_emit_flush_gpu_tlb(struct amdgpu_ring *ring, hub->eng_distance * eng, req, 1 << vmid); + if (use_semaphore) + /* + * add semaphore release after invalidation, + * write with 0 means semaphore release + */ + amdgpu_ring_emit_wreg(ring, hub->vm_inv_eng0_sem + + hub->eng_distance * eng, 0); + return pd_addr; } -- cgit v1.2.3 From 384c1d907eeb3be65591991fdda4e2ee2ec1f350 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Fri, 31 Jul 2026 20:18:20 -0400 Subject: drm/amdgpu: Fix lockdep false positive in amdgpu_lockdep_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move fs_reclaim_acquire() to before all lock acquisitions to eliminate false positive circular locking dependency warning. This is a 7.2-cycle regression fix suitable for stable backport. v3: Address Mikhail Gavrilov technical review: - Clarify that fs_reclaim_acquire/release pair only REGISTERS the fs_reclaim lock class, does NOT create a static edge when called with no locks held - Explain that the actual fs_reclaim -> notifier_lock edge is established at runtime during memory reclaim -> MMU notifier path - Add Cc: Arunpravin PaneerSelvam v2: Address Mikhail Gavrilov review feedback: - Fix author name: Michael -> Mikhail Gavrilov in all trailers - Add Fixes: tag to link regression to original commit - Add Tested-by: Mikhail Gavrilov (tested on RX 7900 XTX) Fixes: 1d0f5838b126 ("drm/amdgpu: Add lockdep annotations for lock ordering validation") Reported-by: Mikhail Gavrilov Analyzed-by: Mikhail Gavrilov Test-case-by: Mikhail Gavrilov Tested-by: Mikhail Gavrilov Suggested-by: Christian König Tested-by: Vitaly Prosyak Cc: Christian König Cc: Alex Deucher Cc: Arunpravin PaneerSelvam Signed-off-by: Vitaly Prosyak Acked-by: Arunpravin Paneer Selvam Signed-off-by: Alex Deucher (cherry picked from commit 70a1e9849e6ed12bb9f1c0faa24b0f1f9de601eb) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c index 61450af539a6..d5787d848d04 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c @@ -135,6 +135,21 @@ int amdgpu_lockdep_init(void) lockdep_set_class(&locks->srbm_mutex, &amdgpu_srbm_lock_key); lockdep_set_class(&locks->grbm_idx_mutex, &amdgpu_grbm_lock_key); lockdep_set_class(&locks->mmio_idx_lock, &amdgpu_mmio_lock_key); + + /* + * Register fs_reclaim lock class FIRST, before taking any locks. + * + * This acquire/release pair does NOT create a static lockdep edge + * (no locks are held between acquire and release). It only registers + * the fs_reclaim lock class with lockdep. + * + * The actual fs_reclaim -> notifier_lock dependency is established at + * RUNTIME when memory reclaim invokes MMU notifiers: + * fs_reclaim (held by reclaim) -> notifier_lock (acquired in callback) + */ + fs_reclaim_acquire(GFP_KERNEL); + fs_reclaim_release(GFP_KERNEL); + /* * Take locks in the correct order to train lockdep. * This establishes the dependency chain. @@ -154,11 +169,6 @@ int amdgpu_lockdep_init(void) /* Level 6: Reset control lock */ mutex_lock(&locks->reset_lock); - /* - * Mark potential memory reclaim boundary. - * GPU operations might trigger memory allocation/reclaim. - */ - fs_reclaim_acquire(GFP_KERNEL); /* Level 7: SRBM register access */ mutex_lock(&locks->srbm_mutex); @@ -176,7 +186,6 @@ int amdgpu_lockdep_init(void) spin_unlock_irqrestore(&locks->mmio_idx_lock, flags); mutex_unlock(&locks->grbm_idx_mutex); mutex_unlock(&locks->srbm_mutex); - fs_reclaim_release(GFP_KERNEL); mutex_unlock(&locks->reset_lock); up_read(&reset_domain->sem); -- cgit v1.2.3 From b77a725e50c87c091f51affc8e46710badea0d49 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Mon, 3 Aug 2026 17:19:46 +0800 Subject: drm/amdgpu/userq: serialize queue map against GPU reset Creating a user queue can race with a GPU reset. While recovery holds reset_domain->sem for write, MES is unresponsive, so the ADD_QUEUE from amdgpu_userq_map_helper() times out (-110) and an otherwise valid queue create fails: amdgpu: MES(0) failed to respond to msg=ADD_QUEUE [drm:mes_userq_map [amdgpu]] *ERROR* Failed to map queue in HW, err (-110) amdgpu: [drm] *ERROR* ... Failed to map Queue amdgpu: [drm] *ERROR* ... Failed to create usermode queue Take reset_domain->sem for read around the map so it runs only once MES is back up. This mirrors amdgpu_userq_cleanup() and honors the userq_mutex -> reset_domain->sem order; the reset path never takes userq_mutex, so there is no deadlock. Reviewed-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher (cherry picked from commit a8e151fe629c63b0eb08aa57de0d434614db3e1b) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 3efe1ae27386..b18d78720656 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -700,7 +700,12 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) if (!adev->userq_halt_for_enforce_isolation || ((queue->queue_type != AMDGPU_HW_IP_GFX) && (queue->queue_type != AMDGPU_HW_IP_COMPUTE))) { + /* Serialize the map against an in-progress GPU reset (MES is + * unresponsive during recovery), matching amdgpu_userq_cleanup(). + */ + down_read(&adev->reset_domain->sem); r = amdgpu_userq_map_helper(queue); + up_read(&adev->reset_domain->sem); if (r) { drm_file_err(uq_mgr->file, "Failed to map Queue\n"); mutex_unlock(&uq_mgr->userq_mutex); -- cgit v1.2.3 From fd37f9dd5b5ab70a46fa7bc76623c0528d602b27 Mon Sep 17 00:00:00 2001 From: Candice Li Date: Thu, 30 Jul 2026 11:28:10 +0800 Subject: drm/amdgpu: reject oversized IBs with per-ring packet limits On GFX rings, amdgpu_cs_p2_ib() passed user-supplied ib_bytes through to ib->length_dw without a limit, while ring_emit_ib() encodes length into packet fields. Oversized values can corrupt adjacent control bits and destabilize command submission. Add a per-ring IB packet size limit helper and reject command submissions exceeding the corresponding dword limit before IB allocation. Use the documented 20-bit limit for GFX/compute/SDMA/VPE, and apply the MM fallback limit for other ring types. Signed-off-by: Candice Li Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 7f48fa2cf62e3fa6c9c3870aa74988f773247e52) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 5445f75741b5..f8bf0f6b5097 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -42,6 +42,26 @@ #include "amdgpu_ras.h" #include "amdgpu_hmm.h" +/* + * Maximum IB length (dwords) for rings whose emit_ib packet format + * documents a 20-bit size field. + */ +#define AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW 0xFFFFF +#define AMDGPU_MM_IB_PACKET_SIZE_MAX_DW 0x7FFFF0 + +static u32 amdgpu_cs_ib_packet_size_max_dw(enum amdgpu_ring_type type) +{ + switch (type) { + case AMDGPU_RING_TYPE_GFX: + case AMDGPU_RING_TYPE_COMPUTE: + case AMDGPU_RING_TYPE_SDMA: + case AMDGPU_RING_TYPE_VPE: + return AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW; + default: + return AMDGPU_MM_IB_PACKET_SIZE_MAX_DW; + } +} + static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, struct amdgpu_device *adev, struct drm_file *filp, @@ -345,7 +365,6 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p, job = p->jobs[r]; ring = amdgpu_job_ring(job); - ib = &job->ibs[job->num_ibs++]; /* submissions to kernel queues are disabled */ if (ring->no_user_submission) @@ -374,6 +393,12 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p, return -EINVAL; } + if (chunk_ib->ib_bytes / 4 > + amdgpu_cs_ib_packet_size_max_dw(ring->funcs->type)) + return -EINVAL; + + ib = &job->ibs[job->num_ibs++]; + if (chunk_ib->flags & AMDGPU_IB_FLAG_PREAMBLE) job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT; -- cgit v1.2.3 From b88a5a43c070df46939de419663d4679b90caf2f Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Wed, 29 Jul 2026 13:00:21 +0530 Subject: drm/amdgpu: Use virtual alloc during coredump The number of rings with outstanding fences can be large, requiring a bigger allocation. Such allocations don't need to be physically contiguous, so use kvzalloc/kvcalloc which fall back to vmalloc when contiguous memory isn't available. This also matches the existing kvfree used to free these allocations. Also guard the allocation with ring_count to avoid passing 0 size to allocation routines. Fixes: eea85914d15b ("drm/amdgpu: save ring content before resetting the device") Signed-off-by: Lijo Lazar Reviewed-by: Hawking Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 74d48bd6b7e12eba65de0507475b059966685ad1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 6480a344006d..8dafc84f4232 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -553,7 +553,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, if (job && job->pasid) size += sizeof(struct amdgpu_coredump_ib_info) * job->num_ibs; - coredump = kzalloc(size, GFP_NOWAIT); + coredump = kvzalloc(size, GFP_NOWAIT); if (!coredump) return; @@ -596,8 +596,12 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, total_ring_size += ring->ring_size; ring_count++; } - coredump->rings_dw = kzalloc(total_ring_size, GFP_NOWAIT); - coredump->rings = kcalloc(ring_count, sizeof(struct amdgpu_coredump_ring), GFP_NOWAIT); + if (ring_count) { + coredump->rings_dw = kvzalloc(total_ring_size, GFP_NOWAIT); + coredump->rings = kvcalloc(ring_count, + sizeof(struct amdgpu_coredump_ring), + GFP_NOWAIT); + } if (coredump->rings && coredump->rings_dw) { for (i = 0, off = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) { ring = adev->rings[i]; -- cgit v1.2.3 From e40ff9840fa8a633d149f0242df10cae5e518062 Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Wed, 29 Jul 2026 16:23:58 +0530 Subject: drm/amdgpu: Allocate coredump ring buffers per ring Allocate each ring buffer separately. A single allocation summing all ring sizes can exceed the page allocator's MAX_ORDER limit and fail; per-ring buffers stay small enough to satisfy. The existing allocation style doesn't capture any ring data if the huge allocation fails. Splitting into multiple allocations helps to capture as much data as possible for the core dump. A failed ring is left with a NULL buffer and skipped when formatting. Fixes: eea85914d15b ("drm/amdgpu: save ring content before resetting the device") Signed-off-by: Lijo Lazar Assisted-by: Claude Code Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 3e8e92b7892a6377bef86106bfff1b98cf586aee) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c | 50 ++++++++++++------------ drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h | 3 +- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 8dafc84f4232..45dfe1c8a11b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -342,7 +342,7 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf struct amdgpu_ip_block *ip_block; struct amdgpu_ring *ring; int ver, i, j; - u32 ring_idx, off; + u32 ring_idx; bool sizing_pass; sizing_pass = buffer == NULL; @@ -442,7 +442,6 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf for (i = 0; i < coredump->num_rings; i++) { ring_idx = coredump->rings[i].ring_index; ring = coredump->adev->rings[ring_idx]; - off = coredump->rings[i].offset; drm_printf(&p, "ring name: %s\n", ring->name); drm_printf(&p, "Rptr: 0x%llx Wptr: 0x%llx RB mask: %x\n", @@ -451,12 +450,18 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf ring->buf_mask); drm_printf(&p, "Ring size in dwords: %d\n", ring->ring_size / 4); + + if (!coredump->rings[i].ring_dw) { + drm_printf(&p, "Ring contents unavailable\n"); + continue; + } + drm_printf(&p, "Ring contents\n"); drm_printf(&p, "Offset \t Value\n"); for (j = 0; j < ring->ring_size; j += 4) drm_printf(&p, "0x%x \t 0x%x\n", j, - coredump->rings_dw[off + j / 4]); + coredump->rings[i].ring_dw[j / 4]); } } @@ -497,10 +502,12 @@ amdgpu_devcoredump_read(char *buffer, loff_t offset, size_t count, static void amdgpu_devcoredump_free(void *data) { struct amdgpu_coredump_info *coredump = data; + u32 i; kvfree(coredump->formatted); + for (i = 0; i < coredump->num_rings; i++) + kvfree(coredump->rings[i].ring_dw); kvfree(coredump->rings); - kvfree(coredump->rings_dw); kvfree(data); } @@ -542,9 +549,9 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, struct amdgpu_coredump_info *coredump; size_t size = sizeof(*coredump); struct drm_sched_job *s_job; - u64 total_ring_size, ring_count; + u64 ring_count; struct amdgpu_ring *ring; - int i, off, idx; + int i, idx; /* No need to generate a new coredump if there's one in progress already. */ if (work_busy(&adev->coredump_work)) @@ -584,7 +591,6 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, /* Dump ring content if memory allocation succeeds. */ ring_count = 0; - total_ring_size = 0; for (i = 0; i < adev->num_rings; i++) { ring = adev->rings[i]; @@ -593,38 +599,34 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, coredump->ring != ring) continue; - total_ring_size += ring->ring_size; ring_count++; } - if (ring_count) { - coredump->rings_dw = kvzalloc(total_ring_size, GFP_NOWAIT); + if (ring_count) coredump->rings = kvcalloc(ring_count, sizeof(struct amdgpu_coredump_ring), GFP_NOWAIT); - } - if (coredump->rings && coredump->rings_dw) { - for (i = 0, off = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) { + if (coredump->rings) { + for (i = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) { + struct amdgpu_coredump_ring *cdump_ring; + ring = adev->rings[i]; if (atomic_read(&ring->fence_drv.last_seq) == ring->fence_drv.sync_seq && coredump->ring != ring) continue; - coredump->rings[idx].ring_index = ring->idx; - coredump->rings[idx].rptr = amdgpu_ring_get_rptr(ring); - coredump->rings[idx].wptr = amdgpu_ring_get_wptr(ring); - coredump->rings[idx].offset = off; + cdump_ring = &coredump->rings[idx]; + + cdump_ring->ring_dw = kvzalloc(ring->ring_size, GFP_NOWAIT); + if (cdump_ring->ring_dw) + memcpy(cdump_ring->ring_dw, ring->ring, ring->ring_size); - memcpy(&coredump->rings_dw[off], ring->ring, ring->ring_size); - off += ring->ring_size / 4; + cdump_ring->ring_index = ring->idx; + cdump_ring->rptr = amdgpu_ring_get_rptr(ring); + cdump_ring->wptr = amdgpu_ring_get_wptr(ring); idx++; } coredump->num_rings = idx; - } else { - kvfree(coredump->rings_dw); - kvfree(coredump->rings); - coredump->rings_dw = NULL; - coredump->rings = NULL; } coredump->adev = adev; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h index 63f27337c09a..cb874d8c3394 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h @@ -34,8 +34,8 @@ struct amdgpu_coredump_ring { u64 rptr; u64 wptr; + u32 *ring_dw; u32 ring_index; - u32 offset; }; struct amdgpu_coredump_ib_info { @@ -53,7 +53,6 @@ struct amdgpu_coredump_info { struct amdgpu_ring *ring; struct amdgpu_coredump_ring *rings; - u32 *rings_dw; u32 num_rings; /* Readable form of coredevdump, generate once to speed up -- cgit v1.2.3 From 5227c2c77c3869cbbc680d5a61cb5d4574fd8e38 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 28 Jul 2026 11:20:38 -0400 Subject: drm/amdgpu/gmc12.1: fix MMHUB0 check in pasid tlb flush Check for mmhub0 rather than mmhub1. Looks like a copy paste typo. Fixes: d0c989a0aad3 ("drm/amd/amdgpu : Use the MES INV_TLBS API for tlb invalidation on gfx12_1") Cc: Shaoyun Liu Reviewed-by: Shaoyun Liu Signed-off-by: Alex Deucher (cherry picked from commit 0e8faef0aaa4d08f3f4f67ee7bb74e1babc8efc4) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c index bdf6788b85b5..5fe43f7eab29 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c @@ -427,7 +427,7 @@ static void gmc_v12_1_flush_gpu_tlb_pasid(struct amdgpu_device *adev, if (all_hub) { /* invalidate mm_hub */ - if (test_bit(AMDGPU_MMHUB1(0), adev->vmhubs_mask)) { + if (test_bit(AMDGPU_MMHUB0(0), adev->vmhubs_mask)) { input.hub_id = AMDGPU_MMHUB0(0); adev->mes.funcs->invalidate_tlbs_pasid(&adev->mes, &input); } -- cgit v1.2.3 From ff209cd04845d819acc2fcc19b25904b4b7c3ea9 Mon Sep 17 00:00:00 2001 From: "Travis K. Bangs" Date: Mon, 3 Aug 2026 15:13:52 -0400 Subject: drm/amd: Disable DP audio spread spectrum for Cyan Skillfish The VBIOS for Cyan Skillfish devices (DCN201) indicates there is DisplayPort ref clock spread spectrum downspread, so the audio clock is corrected for it. However, the clock source in this hardware does not seem to actually be running with a clock downspread, so DisplayPort audio desyncs with video after several minutes. Ignore dprefclk SS downspread on CYAN_SKILLFISH2 asic. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5429 Signed-off-by: Travis K. Bangs Signed-off-by: Alex Deucher (cherry picked from commit f3a2d86587432fdd9a6d401507b60a01153453c5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 1820547b1dde..941c1a312824 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2189,6 +2189,11 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) init_data.flags.unify_link_enc_assignment = true; init_data.flags.usb4_bw_alloc_support = true; } + + /* DCN201 audio desyncs using DP SS */ + if (adev->apu_flags & AMD_APU_IS_CYAN_SKILLFISH2) + init_data.flags.ignore_dpref_ss = true; + retrieve_dmi_info(&adev->dm); if (adev->dm.edp0_on_dp1_quirk) init_data.flags.support_edp0_on_dp1 = true; -- cgit v1.2.3 From 5f08eee9c3fb1a5a70966612e0ff249900f8e77b Mon Sep 17 00:00:00 2001 From: Wang Jiang Date: Wed, 29 Jul 2026 18:26:26 +0800 Subject: drm/radeon: restore hardware polling in fence_is_signaled to fix performance regression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 527ba26e50ec ("drm/radeon: delete radeon_fence_process in is_signaled, no deadlock") removed the hardware polling from radeon_fence_is_signaled() to fix a self-deadlock caused by wake_up_all(&rdev->fence_queue) being called with the fence queue lock held. However, removing the polling entirely causes significant performance regression (e.g. glxgears FPS drop) because the fence signaled check becomes purely passive — it only reads the cached last_seq without probing the GPU, so completed GPU work is not detected in time, causing unnecessary CPU stalls in sync-heavy workloads. Fix this by calling radeon_fence_activity() directly instead of radeon_fence_process(). radeon_fence_activity() reads the hardware fence counter and updates last_seq via atomic ops without calling wake_up_all(), thus avoiding the deadlock while restoring timely fence detection. Fixes: 527ba26e50ec ("drm/radeon: delete radeon_fence_process in is_signaled, no deadlock") Acked-by: Christian König Signed-off-by: Wang Jiang Signed-off-by: Alex Deucher (cherry picked from commit f59ad4cca219c7fdf934f712c5860ec5f5900fd7) Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/radeon_fence.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 02a40e4750c7..299865569252 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -360,6 +360,13 @@ static bool radeon_fence_is_signaled(struct dma_fence *f) if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) return true; + if (down_read_trylock(&rdev->exclusive_lock)) { + radeon_fence_activity(rdev, ring); + up_read(&rdev->exclusive_lock); + + if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) + return true; + } return false; } -- cgit v1.2.3 From b96c529cd2551b78316a4afa3237b2ed96ba03c8 Mon Sep 17 00:00:00 2001 From: Asad Kamal Date: Thu, 30 Jul 2026 15:00:00 +0800 Subject: Revert "drm/amdgpu: fix aperture mapping leak" devres teardown is LIFO. The aperture devres node was registered after the DRM device node, so devres_release_all() unmaps the aperture before the DRM device release callback fires amdgpu_device_fini_sw(). IP sw_fini callbacks (e.g. vcn_v4_0_sw_fini) write to fw_shared through a pointer derived from aper_base_kaddr, causing a kernel page fault on probe failure / rollback: BUG: unable to handle page fault ... PMD 0 RIP: vcn_v4_0_sw_fini+0x7b/0x170 [amdgpu] Call Trace: amdgpu_device_fini_sw amdgpu_driver_release_kms devm_drm_dev_init_release devres_release_all This reverts commit d871e99879cb5fd1fa798b006b4888887e63a17a. Fixes: d871e99879cb ("drm/amdgpu: fix aperture mapping leak") Reported-by: Yuansheng Mao Signed-off-by: Asad Kamal Reviewed-by: Lijo Lazar Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher (cherry picked from commit 336e0cd576817ac64a4b394ca2b3680029f3e37f) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 35 ++++++++++++++++-------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index e5f26e5892ba..bff3e06a756c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -4194,6 +4194,8 @@ static void amdgpu_device_unmap_mmio(struct amdgpu_device *adev) iounmap(adev->rmmio); adev->rmmio = NULL; + if (adev->mman.aper_base_kaddr) + iounmap(adev->mman.aper_base_kaddr); adev->mman.aper_base_kaddr = NULL; /* Memory manager related */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 5fc91f45db5b..12a3b8bc58a4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2120,23 +2120,17 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) /* Change the size here instead of the init above so only lpfn is affected */ amdgpu_ttm_disable_buffer_funcs(adev); #ifdef CONFIG_64BIT - if (adev->gmc.xgmi.connected_to_cpu) { - void *kaddr = devm_memremap(adev->dev, adev->gmc.aper_base, - adev->gmc.visible_vram_size, - MEMREMAP_WB); - if (IS_ERR(kaddr)) - return PTR_ERR(kaddr); - adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr; - } else if (adev->gmc.is_app_apu) { +#ifdef CONFIG_X86 + if (adev->gmc.xgmi.connected_to_cpu) + adev->mman.aper_base_kaddr = ioremap_cache(adev->gmc.aper_base, + adev->gmc.visible_vram_size); + else if (adev->gmc.is_app_apu) DRM_DEBUG_DRIVER( "No need to ioremap when real vram size is 0\n"); - } else { - adev->mman.aper_base_kaddr = devm_ioremap_wc(adev->dev, - adev->gmc.aper_base, - adev->gmc.visible_vram_size); - if (!adev->mman.aper_base_kaddr) - return -ENOMEM; - } + else +#endif + adev->mman.aper_base_kaddr = ioremap_wc(adev->gmc.aper_base, + adev->gmc.visible_vram_size); #endif amdgpu_ttm_init_vram_resv_regions(adev); @@ -2265,6 +2259,8 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) */ void amdgpu_ttm_fini(struct amdgpu_device *adev) { + int idx; + if (!adev->mman.initialized) return; @@ -2287,7 +2283,14 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE); amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE); - adev->mman.aper_base_kaddr = NULL; + if (drm_dev_enter(adev_to_drm(adev), &idx)) { + + if (adev->mman.aper_base_kaddr) + iounmap(adev->mman.aper_base_kaddr); + adev->mman.aper_base_kaddr = NULL; + + drm_dev_exit(idx); + } if (!adev->gmc.is_app_apu) amdgpu_vram_mgr_fini(adev); -- cgit v1.2.3 From 3141e3d61469bba2624a91c5e2407f110b33b29e Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Wed, 5 Aug 2026 09:47:17 +0800 Subject: drm/amd/display: Check for tg ops in dce110_set_avmute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some older DCE timing generators do not implement is_tg_enabled in their ops table. Calling it unconditionally when waiting for AV mute frames causes a NULL pointer dereference on Southern Islands dGPUs when turning the display off over HDMI. Check that tg and the required ops exist before waiting for frames. Fixes: 414da24137ac ("drm/amd/display: Add AV mute wait frames to dce110_set_avmute") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5557 Tested-by: Viktor Jägersküpper Signed-off-by: Ray Wu Signed-off-by: Alex Deucher (cherry picked from commit 2686a0c0aaa07bec2e24131835cf27b5fd4935a5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index 53fffe58602f..f5b3a2eff94a 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -1348,7 +1348,10 @@ void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) * process the mute state, especially after link re-establishment * with HDMI 2.0 scrambling enabled. */ - if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { + if (enable && pipe_ctx->stream_res.tg && + pipe_ctx->stream_res.tg->funcs->is_tg_enabled && + pipe_ctx->stream_res.tg->funcs->wait_for_state && + pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { int i; pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); -- cgit v1.2.3 From f9e5f51549000e2665e3b5e02ff876b9e09cfe95 Mon Sep 17 00:00:00 2001 From: Asad Kamal Date: Thu, 30 Jul 2026 15:00:00 +0800 Subject: drm/amdgpu: fix aperture iounmap skipped on device removal amdgpu_pci_remove() calls drm_dev_unplug() before invoking the fini routines. After drm_dev_unplug() the drm_dev_enter() guard in amdgpu_ttm_fini() always returns false, so iounmap() for aper_base_kaddr is silently skipped. On connected_to_cpu hardware ioremap_cache() maps the aperture as WB; when iounmap() is skipped the stale WB PAT entry persists. On reload IP discovery's memremap(MEMREMAP_WC) on the same aperture range hits a WB/WC conflict, producing an ioremap error and failing re-probe. Remove the drm_dev_enter() guard and call iounmap() unconditionally. The aperture mapping is plain MMIO and does not require device-presence protection. Surprise-removal cleanup of aper_base_kaddr is already handled unconditionally by amdgpu_device_unmap_mmio(). Fixes: 62d5f9f7110a ("drm/amdgpu: Unmap MMIO mappings when device is not unplugged") Signed-off-by: Asad Kamal Reviewed-by: Lijo Lazar Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher (cherry picked from commit fb3f68af9f6fce9343a2bd13b4d68a1c02d283df) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 12a3b8bc58a4..eaa86e32912e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2259,8 +2259,6 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) */ void amdgpu_ttm_fini(struct amdgpu_device *adev) { - int idx; - if (!adev->mman.initialized) return; @@ -2283,13 +2281,9 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE); amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE); - if (drm_dev_enter(adev_to_drm(adev), &idx)) { - - if (adev->mman.aper_base_kaddr) - iounmap(adev->mman.aper_base_kaddr); + if (adev->mman.aper_base_kaddr) { + iounmap(adev->mman.aper_base_kaddr); adev->mman.aper_base_kaddr = NULL; - - drm_dev_exit(idx); } if (!adev->gmc.is_app_apu) -- cgit v1.2.3 From 8099bd08646544730ba8c9fc7c0ebf6773206e19 Mon Sep 17 00:00:00 2001 From: David Weber Date: Thu, 30 Jul 2026 05:32:00 +0200 Subject: drm/amd/display: allow self-refresh exit while entry is blocked amdgpu_dm_crtc_set_static_screen_optimze() maps sso_enable to the Replay and PSR1 vsync events. allow_sr_entry is an entry gate, but the helper currently applies it to both directions. A non-fast update clears allow_sr_entry. During a modeset, a separate hardware-programming event keeps self-refresh blocked while the stream is reprogrammed. If vblank is enabled before the entry delay expires, the ISM calls the helper with sso_enable false. The early return drops the disable request, so the vsync events are not set. After enough fast commits, allow_sr_entry becomes true and the hardware-programming event can be cleared. Since the vblank reference remains held, there is no further zero-to-one vblank transition to restore the missing vsync events. Replay or PSR1 can then become active while vblank is still enabled. Gate only requests that enable static-screen optimization. Always process disable requests so a vblank requestor keeps Replay and PSR1 blocked. On a Phoenix system, repeated SDDM-to-VT handoffs produced stuck flips followed by flip_done and commit-wait timeouts. The timeout was not observed with this change applied. Fixes: 3c108046e1d6 ("drm/amd/display: Add power module on Linux") Assisted-by: Codex:gpt-5.6-sol Assisted-by: Claude:opus-5 Signed-off-by: David Weber Reviewed-by: Leo Li Signed-off-by: Alex Deucher (cherry picked from commit bd0c00982166d34ed47b11ba29cd8bf2950cc2e2) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 95d3da3c4199..06598273d481 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -119,7 +119,11 @@ void amdgpu_dm_crtc_set_static_screen_optimze( struct dc_link *link = stream->link; bool set_vsync_event = !sso_enable; - if (!allow_sr_entry) + /* + * allow_sr_entry gates only entry. A disable request must still set + * the vsync events to force Replay and PSR1 out and keep them blocked. + */ + if (sso_enable && !allow_sr_entry) return; amdgpu_dm_replay_set_event(dm, stream, -- cgit v1.2.3 From 594d905195024b228c962627ae5ae7c17bd582a4 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 4 Aug 2026 00:21:54 +0000 Subject: af_unix: Unlink scc_entry in unix_del_edge(). Kyle Zeng reported that GC could free a dead SCC partially. The scenario is as follows: 1) Create two SCCs: X -. A <-> B ^--' 2) Run the following concurrently: 2-1) send() sk-B to sk-B from sk-X 2-2) close() both A and B At 2-1), there is a small window where unix_add_edges() publishes a new edge (B <-> B) to GC but its skb is not queued by skb_queue_tail(). If 2-2) completes before skb_queue_tail() and GC is triggered, it judges A <-> B as dead, but B is not freed because GC cannot collect the not-yet-queued skb holding the B <-> B edge. X -. A <-> B -. This edge is visible ^--' ^..' but skb is not This itself is not a problem since the next GC run will judge B as dead as well and free it finally. X -. A <.> B -. ^--' ^--' However, X's SCC forces the next GC to call unix_walk_scc_fast(), and it iterates over A through B's scc_entry. Let's unlink scc_entry before freeing the vertex in unix_del_edge(). Fixes: 4090fa373f0e ("af_unix: Replace garbage collection algorithm.") Reported-by: Kyle Zeng Signed-off-by: Kuniyuki Iwashima Reviewed-by: Kyle Zeng Fixes: 4090fa373f0e ("af_unix: Replace garbage collection algorithm."). Link: https://patch.msgid.link/20260804002155.2233594-1-kuniyu@google.com Signed-off-by: Jakub Kicinski --- net/unix/garbage.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 0783555e2526..9fcaaf55cba5 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -186,6 +186,7 @@ static void unix_del_edge(struct scm_fp_list *fpl, struct unix_edge *edge) if (!vertex->out_degree) { edge->predecessor->vertex = NULL; list_move_tail(&vertex->entry, &fpl->vertices); + list_del(&vertex->scc_entry); } } -- cgit v1.2.3 From 8444d66aa6b6e7fe0a26fa1a00a11cb4d0523783 Mon Sep 17 00:00:00 2001 From: Hongyan Xu Date: Thu, 6 Aug 2026 14:06:13 +0800 Subject: watchdog: at91sam9_wdt: prevent timer rearm during teardown at91_ping() rearms the watchdog timer from its callback. timer_delete() neither waits for a running callback nor prevents it from rearming the timer, so probe failure or driver removal can leave the timer accessing the devm-allocated at91wdt after it has been freed. Use timer_shutdown_sync() on both teardown paths. It waits for a running callback and rejects any attempt by the callback to rearm the timer. Fixes: 5161b31dc39a ("watchdog: at91sam9_wdt: better watchdog support") Signed-off-by: Hongyan Xu Link: https://lore.kernel.org/r/20260806060613.1830-1-getshell@seu.edu.cn Signed-off-by: Guenter Roeck --- drivers/watchdog/at91sam9_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index aba66b8e9d03..80ba04df54ad 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -242,7 +242,7 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt) return 0; out_stop_timer: - timer_delete(&wdt->timer); + timer_shutdown_sync(&wdt->timer); return err; } @@ -378,7 +378,7 @@ static void at91wdt_remove(struct platform_device *pdev) watchdog_unregister_device(&wdt->wdd); pr_warn("I quit now, hardware will probably reboot!\n"); - timer_delete(&wdt->timer); + timer_shutdown_sync(&wdt->timer); } #if defined(CONFIG_OF) -- cgit v1.2.3 From 7a3c0289c3c8eb4607dff448ae9ff9f902c813af Mon Sep 17 00:00:00 2001 From: Kumar Kartikeya Dwivedi Date: Sun, 2 Aug 2026 04:17:59 +0200 Subject: rqspinlock: Reset tail when preserving queue on deadlock Currently, the destruction of the waiter queue is suppressed for rqspinlock in cases where a deadlock is detected. Deadlock checks happen relatively frequently (on entry for AA, within 1ms for ABBA), and waiter threads may not be involved in locking scenarios involving deadlocks. Thus, it is useful to not flush the queue and let other waiters take a stab at acquiring the lock after we detect a deadlock and exit. However, we need to follow the same logic as what we did previously for the waitq_timeout label: reset the tail, and if we cannot, signal the next waiter appropriately. In case of deadlocks, this signal would just mark the MCS node as unlocked, and in case of timeouts, it would signal RES_TIMEOUT_VAL. The difference thus is in the value propagated, which decides whether the queue remains active or gets flushed. Not doing the tail reset, and waiting for the next waiter can lead to cases where we are the final waiter, and thus no next waiter arrives, leading to intermittent stalls in this path. Once the next waiter does join, we will be unblocked. In the theoretical case when the next waiter never joins, we risk stalling indefinitely. This can only happen for ABBA deadlocks, since entry into the wait queue is guarded with AA checks. A precise sequence of executions leading up to this scenario can be: CPU 0 holds lock A. CPU 1 holds lock B. CPU 2 attempts lock B, becomes the pending waiter for B. CPU 0 attempts lock B. B has locked+pending bits set, thus CPU 0 queues. CPU 1 attempts lock A. CPU 0 detects an ABBA deadlock. Once deadlock detection happens for CPU 0, it will sit waiting for the next waiter in the queue to populate node->next, which will experience delays until such a waiter arrives. Fix this by adjusting the logic for the check for deadlocks preceding the waitq_timeout label. It would make sense to consolidate code for both cases and use 'ret' to distinguish the value being propagated, but that is left as an exercise for a future refactoring task to avoid diff noise in this patch. Fixes: 7bd6e5ce5be6 ("rqspinlock: Disable queue destruction for deadlocks") Signed-off-by: Kumar Kartikeya Dwivedi Link: https://patch.msgid.link/20260802021759.1139457-1-memxor@gmail.com Signed-off-by: Eduard Zingerman --- kernel/bpf/rqspinlock.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/rqspinlock.c b/kernel/bpf/rqspinlock.c index e4e338cdb437..2129defc4a9a 100644 --- a/kernel/bpf/rqspinlock.c +++ b/kernel/bpf/rqspinlock.c @@ -572,9 +572,10 @@ queue: /* Disable queue destruction when we detect deadlocks. */ if (ret == -EDEADLK) { - if (!next) + if (!try_cmpxchg_tail(lock, tail, 0)) { next = smp_cond_load_relaxed(&node->next, (VAL)); - arch_mcs_spin_unlock_contended(&next->locked); + arch_mcs_spin_unlock_contended(&next->locked); + } goto err_release_node; } -- cgit v1.2.3 From 41d44ac7a61e2f74453af40d4fe1b82af9ea0ada Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 29 Jul 2026 12:21:41 +0200 Subject: ovpn: fix NULL dereference when killing missing key ovpn_crypto_kill_key assumes both crypto slots are populated and dereferences each slot before checking it. That is not guaranteed: a peer can have only one installed key, and the kill path may be asked to remove a key that is not present. Read each slot once while holding the crypto state lock, check for NULL before looking at key_id, and only replace the slot that actually matches. Fixes: 89d3c0e4612a ("ovpn: kill key and notify userspace in case of IV exhaustion") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/crypto.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c index 90580e32052f..2e95f29514fc 100644 --- a/drivers/net/ovpn/crypto.c +++ b/drivers/net/ovpn/crypto.c @@ -58,15 +58,19 @@ void ovpn_crypto_state_release(struct ovpn_crypto_state *cs) bool ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id) { struct ovpn_crypto_key_slot *ks = NULL; + struct ovpn_crypto_key_slot *tmp; + int slot = 0; spin_lock_bh(&cs->lock); - if (rcu_access_pointer(cs->slots[0])->key_id == key_id) { - ks = rcu_replace_pointer(cs->slots[0], NULL, - lockdep_is_held(&cs->lock)); - } else if (rcu_access_pointer(cs->slots[1])->key_id == key_id) { - ks = rcu_replace_pointer(cs->slots[1], NULL, - lockdep_is_held(&cs->lock)); + tmp = rcu_access_pointer(cs->slots[slot]); + if (!tmp || tmp->key_id != key_id) { + slot = 1; + tmp = rcu_access_pointer(cs->slots[slot]); } + + if (tmp && tmp->key_id == key_id) + ks = rcu_replace_pointer(cs->slots[slot], NULL, + lockdep_is_held(&cs->lock)); spin_unlock_bh(&cs->lock); if (ks) -- cgit v1.2.3 From 9e163917a86c6adfbe150e13f4c73653a54616de Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 29 Jul 2026 12:21:44 +0200 Subject: ovpn: finish crypto callback cleanup before peer release Crypto completion callbacks hold both key-slot and peer references. The peer reference pins the netdev, and dropping the last peer reference can let netdev unregistration and module removal make progress. Do not release that peer reference before the callback has finished its own cleanup. If ovpn_crypto_key_slot_put runs after ovpn_peer_put, it can schedule an RCU callback backed by module text after ovpn_cleanup rcu_barrier has already run. The TX error path also freed the remaining skb after ovpn_peer_put, leaving callback cleanup outside the peer/netdev lifetime window. Release the key slot and free any remaining skb first, then drop the peer reference as the last callback action. Fixes: 8534731dbf2d ("ovpn: implement packet processing") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 9a66d693039a..9526f8096da6 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -204,10 +204,10 @@ drop: ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); kfree_skb(skb); drop_nocount: - if (likely(peer)) - ovpn_peer_put(peer); if (likely(ks)) ovpn_crypto_key_slot_put(ks); + if (likely(peer)) + ovpn_peer_put(peer); } /* RX path entry point: decrypt packet and forward it to the device */ @@ -302,11 +302,11 @@ err_unlock: err: if (unlikely(skb)) ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); - if (likely(peer)) - ovpn_peer_put(peer); + kfree_skb(skb); if (likely(ks)) ovpn_crypto_key_slot_put(ks); - kfree_skb(skb); + if (likely(peer)) + ovpn_peer_put(peer); } static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb) -- cgit v1.2.3 From 2820d227ad4ee70805d693d698437cc3e88d6c3d Mon Sep 17 00:00:00 2001 From: Pu Lehui Date: Sun, 2 Aug 2026 09:49:29 +0000 Subject: riscv: ftrace: Fix ftrace_modify_call failure on kprobed functions We are frequently hitting the following splat during the riscv bpf selftests: 00000000026dc75a: expected (7c3ff297) but got (00100073) ------------[ ftrace bug ]------------ ftrace failed to modify [] bpf_kfunc_common_test+0x4/0x20 [bpf_testmod] actual: e7:82:c2:ce Updating ftrace call site to call a different ftrace function ftrace record flags: 80100002 (2) expected tramp: ffffffff80043904 ------------[ cut here ]------------ WARNING: kernel/trace/ftrace.c:2278 at ftrace_bug+0x46e/0x4b0, CPU#1: test_progs/98 ... [] ftrace_bug+0x46e/0x4b0 [] ftrace_replace_code+0x16e/0x170 [] ftrace_modify_all_code+0x12e/0x1b8 [] arch_ftrace_update_code+0x14/0x28 [] ftrace_startup+0x14c/0x2a0 [] ftrace_startup_subops+0x584/0x1050 [] register_ftrace_graph+0x4e6/0x1018 [] register_fprobe_ips+0xc66/0x12f8 [] bpf_kprobe_multi_link_attach+0x5d8/0xe68 [] __sys_bpf+0x3d5a/0x47f0 [] __riscv_sys_bpf+0xae/0x168 [] syscall_handler+0x60/0x100 [] do_trap_ecall_u+0x174/0x208 [] handle_exception+0x16c/0x178 After debugging, it can be triggered by similar commands below: ``` echo do_nanosleep > set_ftrace_filter echo function > current_tracer echo 'p do_nanosleep' > kprobe_events echo 1 > events/kprobes/enable echo 'f do_nanosleep' > dynamic_events echo 1 > events/fprobes/enable ``` The reason is that attaching a kprobe to an ftrace-traced function entry replaces its initial auipc insn with ebreak. When ftrace_modify_call later runs, it expects auipc insn, so verification fails and triggers ftrace_bug. The expected auipc logic remains conceptually unchanged, and kprobe single-stepping ensures normal execution. Therefore, if the first insn is ebreak, bypassing the check to continue patching the jalr insn is safe and avoids ftrace failures. Fixes: b2137c3b6d7a ("riscv: ftrace: prepare ftrace for atomic code patching") Signed-off-by: Pu Lehui Link: https://patch.msgid.link/20260802094929.3978390-1-pulehui@huaweicloud.com [pjw@kernel.org: fixed reproducer in commit message] Signed-off-by: Paul Walmsley --- arch/riscv/kernel/ftrace.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c index b430edfb83f4..be8b68514417 100644 --- a/arch/riscv/kernel/ftrace.c +++ b/arch/riscv/kernel/ftrace.c @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef CONFIG_DYNAMIC_FTRACE void ftrace_arch_code_modify_prepare(void) @@ -63,7 +64,9 @@ static int __ftrace_modify_call(unsigned long source, unsigned long target, bool if (copy_from_kernel_nofault(replaced, (void *)source, 2 * MCOUNT_INSN_SIZE)) return -EFAULT; - if (replaced[0] != call[0]) { + /* Bypass the check if the auipc insn is a kprobe breakpoint */ + if (replaced[0] != call[0] && + !(riscv_insn_is_ebreak(replaced[0]) || riscv_insn_is_c_ebreak(replaced[0]))) { pr_err("%p: expected (%08x) but got (%08x)\n", (void *)source, call[0], replaced[0]); return -EINVAL; -- cgit v1.2.3 From 36c4d73ce05d1d8896c2669eb0730d35a02a2ec1 Mon Sep 17 00:00:00 2001 From: Wilken Gottwalt Date: Wed, 5 Aug 2026 07:19:20 +0000 Subject: hwmon: (corsair-psu) fix possible out-of-bounds access on missing string termination In theory it could be possible that the REPLY_SIZE sized buffers for holding the vendor and product strings could be end up missing the null termination (for example by malicious hardware built on purpose) required by the seq_printf() call. That limits the debugfs printf calls to a maximum string length of REPLY_SIZE. Fixes: d115b51e0e567 ("hwmon: add Corsair PSU HID controller driver") Signed-off-by: Wilken Gottwalt Link: https://lore.kernel.org/r/anLj9gPWRoRDbQBV@monster.localdomain Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-psu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index ce958cdaef58..3c01ae3fc4af 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -701,7 +701,7 @@ static int vendor_show(struct seq_file *seqf, void *unused) { struct corsairpsu_data *priv = seqf->private; - seq_printf(seqf, "%s\n", priv->vendor); + seq_printf(seqf, "%.*s\n", REPLY_SIZE, priv->vendor); return 0; } @@ -711,7 +711,7 @@ static int product_show(struct seq_file *seqf, void *unused) { struct corsairpsu_data *priv = seqf->private; - seq_printf(seqf, "%s\n", priv->product); + seq_printf(seqf, "%.*s\n", REPLY_SIZE, priv->product); return 0; } -- cgit v1.2.3 From fddb5ceaf901b050ed2a1a7deeecbf97e003435a Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Wed, 5 Aug 2026 14:16:45 +0800 Subject: hwmon: (ads7828) Fix external VREF regulator handling The driver currently has two issues with the external VREF regulator handling in ads7828_probe(): 1. All errors from devm_regulator_get_optional() are ignored, causing the driver to incorrectly fall back to internal VREF even for transient errors like -EPROBE_DEFER or genuine failures like -ENOMEM. 2. The external regulator is never enabled. The driver calls regulator_get_voltage() without first calling regulator_enable(), so the VREF pin may remain unpowered if the regulator is not configured as always-on. Fix both issues by switching to devm_regulator_get_enable_read_voltage(), which handles regulator get, enable, and voltage read in one call. Only -ENODEV (no regulator specified in device tree) should trigger the fallback to internal VREF. All other errors are propagated to the caller. Fixes: a8ddfea09566 ("hwmon: (ads7828) Accept optional parameters from device tree") Signed-off-by: Qingshuang Fu Link: https://lore.kernel.org/r/20260805061645.1331652-1-fffsqian@163.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ads7828.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 149cfcec78dc..f8f2f621dfea 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -106,12 +106,11 @@ static int ads7828_probe(struct i2c_client *client) struct ads7828_data *data; struct device *hwmon_dev; unsigned int vref_mv = ADS7828_INT_VREF_MV; - unsigned int vref_uv; + int vref_uv; bool diff_input = false; bool ext_vref = false; unsigned int regval; enum ads7828_chips chip; - struct regulator *reg; data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL); if (!data) @@ -125,9 +124,11 @@ static int ads7828_probe(struct i2c_client *client) } else if (dev->of_node) { diff_input = of_property_read_bool(dev->of_node, "ti,differential-input"); - reg = devm_regulator_get_optional(dev, "vref"); - if (!IS_ERR(reg)) { - vref_uv = regulator_get_voltage(reg); + vref_uv = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (vref_uv < 0) { + if (vref_uv != -ENODEV) + return vref_uv; + } else { vref_mv = DIV_ROUND_CLOSEST(vref_uv, 1000); if (vref_mv < ADS7828_EXT_VREF_MV_MIN || vref_mv > ADS7828_EXT_VREF_MV_MAX) -- cgit v1.2.3 From edd11a94335747423569500a194c6eaa915f2963 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 4 Aug 2026 15:42:42 -0700 Subject: hwmon: (ltc4282) Avoid overflow in maximum power calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During device initialization in ltc4282_set_max_limits(), the calculation of the maximum power limit can suffer from a 32-bit integer overflow. static int ltc4282_set_max_limits(struct ltc4282_state *st) { ... st->power_max = DIV_ROUND_CLOSEST(st->vsense_max * DECA * MILLI, st->rsense) * st->vfs_out; ... } The result of DIV_ROUND_CLOSEST() evaluates to a 32-bit unsigned integer on 32-bit architectures. This result is then multiplied by st->vfs_out, which is a 16-bit unsigned integer. According to C promotion rules, since both operands are 32-bit or smaller, the multiplication is performed in 32-bit precision. If the device is configured with a low sense resistor value via the device tree (for example, 100 nano-ohms, resulting in st->rsense = 1) and the voltage is high, the division result can reach 343,750,000 and st->vfs_out can be 33,280. The product of these values is approximately 11.44 trillion, which exceeds the maximum capacity of a 32-bit integer and overflows before being stored in st->power_max. This overflow causes a truncated value to be assigned to st->power_max and written to the hardware limit register. An incorrect maximum power limit can trigger spurious power-bad faults or alarms, which may lead to the shutdown of the monitored power rail. Avoid the problem by calculating and storing the maximum power using 64-bit variables. Reported-by: Sashiko Fixes: cbc29538dbf7d ("hwmon: Add driver for LTC4282") Cc: Nuno Sa Reviewed-by: Nuno Sá Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4282.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index cc698803f8bf..bb7f6727c44d 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -137,7 +137,7 @@ struct ltc4282_state { */ struct ltc4282_cache in0_1_cache[LTC4282_CHAN_VGPIO]; u32 vsense_max; - long power_max; + s64 power_max; u32 rsense; u16 vdd; u16 vfs_out; @@ -613,13 +613,12 @@ static int ltc4282_read(struct device *dev, enum hwmon_sensor_types type, } static int ltc4282_write_power_byte(const struct ltc4282_state *st, u32 reg, - long val) + s64 val) { u32 power; u64 temp; - if (val > st->power_max) - val = st->power_max; + val = clamp(val, 0, st->power_max); temp = val * int_pow(U8_MAX, 2) * st->rsense; power = DIV64_U64_ROUND_CLOSEST(temp, @@ -629,7 +628,7 @@ static int ltc4282_write_power_byte(const struct ltc4282_state *st, u32 reg, } static int ltc4282_write_power_word(const struct ltc4282_state *st, u32 reg, - long val) + u64 val) { u64 temp = int_pow(U16_MAX, 2) * st->rsense, temp_2; __be16 __raw; @@ -1222,7 +1221,8 @@ static int ltc4282_set_max_limits(struct ltc4282_state *st) return ret; /* Power is given by ISENSE * Vout. */ - st->power_max = DIV_ROUND_CLOSEST(st->vsense_max * DECA * MILLI, st->rsense) * st->vfs_out; + st->power_max = DIV_ROUND_CLOSEST_ULL((u64)st->vsense_max * DECA * MILLI, + st->rsense) * st->vfs_out; ret = ltc4282_write_power_byte(st, LTC4282_POWER_MAX, st->power_max); if (ret) return ret; -- cgit v1.2.3 From e253dd5f9f6d875a317895bf43ec9534ed7523cb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 4 Aug 2026 16:26:05 -0700 Subject: hwmon: (ltc4282) Clamp negative current limits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a negative value is passed to ltc4282_write_curr(), the signed long val is cast directly to u64: drivers/hwmon/ltc4282.c:ltc4282_write_curr() { /* need to pass it in millivolt */ u32 in = DIV_ROUND_CLOSEST_ULL((u64)val * st->rsense, DECA * MICRO); ... } This cast converts negative inputs into large positive values. The subsequent division result overflows the u32 in variable, truncating to a pseudo-random positive value. When this is passed to ltc4282_write_voltage_byte(), it is clamped to the maximum limit instead of zero. Clamp val to 0 and to the maximum supported upper limit before the cast and assign the result to a 64-bit temporary variable before the division to avoid the underflow and an also possible overflow. Reported-by: Sashiko Fixes: cbc29538dbf7d ("hwmon: Add driver for LTC4282") Cc: Nuno Sa Reviewed-by: Nuno Sá Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4282.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index bb7f6727c44d..bb1bcb369016 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -929,8 +930,11 @@ static int ltc4282_curr_reset_hist(struct ltc4282_state *st) static int ltc4282_write_curr(struct ltc4282_state *st, u32 attr, long val) { + s32 ulimit = min_t(u64, INT_MAX, + div_u64((u64)INT_MAX * DECA * MICRO, st->rsense)); + u64 val64 = clamp(val, 0, ulimit); /* need to pass it in millivolt */ - u32 in = DIV_ROUND_CLOSEST_ULL((u64)val * st->rsense, DECA * MICRO); + u32 in = DIV_ROUND_CLOSEST_ULL(val64 * st->rsense, DECA * MICRO); switch (attr) { case hwmon_curr_max: -- cgit v1.2.3 From 335698fd7f60b6707b21fda725f97f35fa956b07 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 4 Aug 2026 17:30:42 -0700 Subject: hwmon: (ltc4282) Fix parsing adi,current-limit-sense-microvolt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ltc4282_parse_dt() evaluates the wrong variable when parsing the current limit. When the adi,current-limit-sense-microvolt property is parsed into st->vsense_max, the subsequent switch statement evaluates the unrelated val variable instead of st->vsense_max: drivers/hwmon/ltc4282.c:ltc4282_parse_dt() { ... ret = device_property_read_u32(dev, "adi,current-limit-sense-microvolt", &st->vsense_max); if (!ret) { int reg_val; switch (val) { case 12500: reg_val = 0; break; ... } Because val holds a small integer representing vin_mode (from 0 to 3), it never matches any of the valid current limit cases. This causes it to always fall through to the default error case, return -EINVAL, and aborts probe initialization for any device tree using this property. Validate st->vsense_max instead to fix the problem. Reported-by: Sashiko Fixes: cbc29538dbf7d ("hwmon: Add driver for LTC4282") Cc: Nuno Sa Reviewed-by: Nuno Sá Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4282.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index bb1bcb369016..b1675dc5b3c7 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -1394,7 +1394,7 @@ static int ltc4282_setup(struct ltc4282_state *st, struct device *dev) if (!ret) { int reg_val; - switch (val) { + switch (st->vsense_max) { case 12500: reg_val = 0; break; -- cgit v1.2.3 From 2da6050809d437a805e7a3aa22398a15073b0234 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Thu, 6 Aug 2026 14:21:39 +0000 Subject: hwmon: (corsair-psu) serialize debugfs access against hwmon corsairpsu_request() sends a rail select command and then the actual read as two separate transfers, both going through the single shared cmd_buffer and wait_completion in corsairpsu_usb_cmd(). The hwmon core serializes its own callers, but the debugfs files call corsairpsu_get_value() directly and never take that lock, so a debugfs read can land between another reader's rail select and its value read. The result is a value from the wrong rail reported as the right one, because corsairpsu_usb_cmd() only checks the command echo and both transfers echo the command it expects. It can also make a caller consume the reply meant for the other one, since raw_event() writes into the shared buffer and completes whoever happens to be waiting. Locking was dropped in commit 4207069edbf0 ("hwmon: (corsair-psu) Rely on subsystem locking") on the grounds that the subsystem serializes for us, which holds for sysfs but not for these files. Take the same lock in the debugfs paths that issue commands, using the guard added in commit d1e720c7328e ("hwmon: Support guard() and scoped_guard for subsystem locks"). The lock cannot go into corsairpsu_request() itself: the hwmon core already holds it across ->read, so every sysfs read would deadlock. vendor_show() and product_show() only print strings cached during probe and issue no command, and corsairpsu_get_criticals() and corsairpsu_check_cmd_support() run before either interface is registered, so none of them need it. Fixes: 4207069edbf0 ("hwmon: (corsair-psu) Rely on subsystem locking") Signed-off-by: Ali Ahmet Memis Tested-by: Wilken Gottwalt Link: https://lore.kernel.org/r/20260806142139.168611-1-ali@iusegentoo.com Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-psu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index 3c01ae3fc4af..ebcaddf337d8 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -664,6 +664,8 @@ static void print_uptime(struct seq_file *seqf, u8 cmd) long val; int ret; + guard(hwmon_lock)(priv->hwmon_dev); + ret = corsairpsu_get_value(priv, cmd, 0, &val); if (ret < 0) { seq_puts(seqf, "N/A\n"); @@ -723,6 +725,8 @@ static int ocpmode_show(struct seq_file *seqf, void *unused) long val; int ret; + guard(hwmon_lock)(priv->hwmon_dev); + /* * The rail mode is switchable on the fly. The RAW interface can be used for this. But it * will not be included here, because I consider it somewhat dangerous for the health of the -- cgit v1.2.3 From c6c4234928d2eb4f61fecb61067e612d9bdbd2ff Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 3 Aug 2026 20:48:11 -0700 Subject: hwmon: (corsair-psu) Fix linear11 calculation In corsairpsu_linear11_to_int(), the mantissa is extracted using bitwise operations and cast to s16 before being shifted left: static int corsairpsu_linear11_to_int(const u16 val, const int scale) { ... const int mant = (((s16)(val & 0x7ff)) << 5) >> 5; ... } Due to C integer promotion rules, the masked value (which is always positive) is promoted to a 32-bit integer before the left shift. As a result, the sign bit is never extended to bit 31 of the promoted integer. When the device hardware reports a negative temperature in Linear11 format (such as an ambient temperature probe reporting sub-zero), the negative mantissa is parsed incorrectly as a massive positive value. For example, -1 becomes 2047, which scales to 2047 degrees Celsius. Fix the problem by type casting the result of the left shift operation to s16. Another problem is left-shifting of negative values. In C, the result of left-shifting negative values is undefined. Use a multiplication instead to avoid the problem. Also use a local s64 variable to store temporary results, change the return value type from int to long, and clamp the final value to LONG_MIN and LONG_MAX to avoid under- and overflow issues while retaining as much information as possible. Reported-by: Sashiko Cc: Wilken Gottwalt Signed-off-by: Guenter Roeck Tested-by: Wilken Gottwalt Link: https://lore.kernel.org/r/20260804034811.2385506-1-linux@roeck-us.net Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-psu.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index ebcaddf337d8..0cf072f86acb 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -137,13 +137,18 @@ struct corsairpsu_data { }; /* some values are SMBus LINEAR11 data which need a conversion */ -static int corsairpsu_linear11_to_int(const u16 val, const int scale) +static long corsairpsu_linear11_to_long(const u16 val, const int scale) { const int exp = ((s16)val) >> 11; - const int mant = (((s16)(val & 0x7ff)) << 5) >> 5; - const int result = mant * scale; + const int mant = ((s16)((val & 0x7ff) << 5)) >> 5; + s64 result = mant * scale; - return (exp >= 0) ? (result << exp) : (result >> -exp); + if (exp >= 0) + result *= (int)(1UL << exp); + else + result >>= -exp; + + return clamp(result, LONG_MIN, LONG_MAX); } /* the micro-controller uses percentage values to control pwm */ @@ -263,13 +268,13 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l case PSU_CMD_RAIL_AMPS: case PSU_CMD_TEMP0: case PSU_CMD_TEMP1: - *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000); + *val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1000); break; case PSU_CMD_FAN: - *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1); + *val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1); break; case PSU_CMD_FAN_PWM_ENABLE: - *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1); + *val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1); /* * 0 = automatic mode, means the micro-controller controls the fan using a plan * which can be modified, but changing this plan is not supported by this @@ -283,12 +288,12 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l *val = 2; break; case PSU_CMD_FAN_PWM: - *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1); + *val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1); *val = corsairpsu_dutycycle_to_pwm(*val); break; case PSU_CMD_RAIL_WATTS: case PSU_CMD_TOTAL_WATTS: - *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000); + *val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1000000); break; case PSU_CMD_TOTAL_UPTIME: case PSU_CMD_UPTIME: -- cgit v1.2.3 From 5ec42d57655c690234c14aece6dd3f209778c1d8 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Mon, 13 Jul 2026 08:25:49 -0700 Subject: KVM: x86/mmu: WARN and clear role.invalid when creating a child shadow page Explicitly clear role.invalid when deriving a child shadow page's role from its parent to harden against bugs elsewhere in KVM, as violating KVM's invariant that invalid pages are NOT on the list of active MMU pages leads to use-after-free due to __kvm_mmu_prepare_zap_page() using list_add() instead of list_move() when processing an invalid shadow page, i.e. makes a bad situation far worse. Yell loudly if the parent is invalid, as it means KVM has missed a validity check, i.e. KVM is attempting to map memory using an invalid/obsolete root, but continue on as the child is otherwise still a valid shadow page. ================================================================== BUG: KASAN: slab-use-after-free in __kvm_mmu_get_shadow_page+0x1817/0x1860 [kvm] Write of size 8 at addr ff11000153dd1368 by task repro/853 CPU: 1 UID: 1000 PID: 853 Comm: repro Not tainted 7.2.0-rc2-3aec122bdcaf-next-vm #5 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 Call Trace: dump_stack_lvl+0x4b/0x70 print_report+0x153/0x49c kasan_report+0xbc/0xf0 __kvm_mmu_get_shadow_page+0x1817/0x1860 [kvm] mmu_alloc_root+0x141/0x320 [kvm] kvm_mmu_load+0x612/0x20f0 [kvm] kvm_arch_vcpu_ioctl_run+0x3dd5/0x6150 [kvm] kvm_vcpu_ioctl+0x5e4/0x10d0 [kvm] __x64_sys_ioctl+0x131/0x1b0 do_syscall_64+0x67/0x5f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Allocated by task 853: kasan_save_stack+0x20/0x40 kasan_save_track+0x14/0x30 __kasan_slab_alloc+0x5f/0x70 kmem_cache_alloc_noprof+0xfe/0x2e0 __kvm_mmu_topup_memory_cache+0x135/0x530 [kvm] paging64_page_fault+0x318/0x1e30 [kvm] kvm_mmu_do_page_fault+0x21d/0x630 [kvm] kvm_mmu_page_fault+0x18c/0x17b0 [kvm] kvm_arch_vcpu_ioctl_run+0x1f35/0x6150 [kvm] kvm_vcpu_ioctl+0x5e4/0x10d0 [kvm] __x64_sys_ioctl+0x131/0x1b0 do_syscall_64+0x67/0x5f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Freed by task 853: kasan_save_stack+0x20/0x40 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x43/0x70 kmem_cache_free+0xe2/0x400 kvm_mmu_commit_zap_page.part.0+0x1e2/0x310 [kvm] kvm_mmu_free_roots+0x283/0x560 [kvm] kvm_arch_vcpu_ioctl_run+0x33c8/0x6150 [kvm] kvm_vcpu_ioctl+0x5e4/0x10d0 [kvm] __x64_sys_ioctl+0x131/0x1b0 do_syscall_64+0x67/0x5f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Reported-by: Hyunwoo Kim Fixes: a770f6f28b1a ("KVM: MMU: Inherit a shadow page's guest level count from vcpu setup") Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson Signed-off-by: Paolo Bonzini --- arch/x86/kvm/mmu/mmu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index c9e4739b26d7..a61750f8e1e3 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2442,6 +2442,9 @@ static union kvm_mmu_page_role kvm_mmu_child_role(u64 *sptep, bool direct, role.direct = direct; role.passthrough = 0; + WARN_ON_ONCE(role.invalid); + role.invalid = 0; + /* * If the guest has 4-byte PTEs then that means it's using 32-bit, * 2-level, non-PAE paging. KVM shadows such guests with PAE paging -- cgit v1.2.3 From a8934c2c6dfd72901cf3cc0de28e85eb902a61a4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 7 Aug 2026 10:34:16 +0200 Subject: ALSA: usb-audio: Fix sticky mixer regressions on M-Audio Fast Track Ultra The recent fix for sticky mixer volumes caused regressions of M-audio Fast Track Ultra device, where the mixer state is kept to the default value. Add the quirk entries to tolerate the broken mixer behavior. As the device is known to work in the implicit feedback mode, explicitly enable the implicit feedback mode, too. Since there are two FTU models that are almost identical, both entries are added in this patch (0763:2080 and 0763:2081). Fixes: 86aa1ea1f15c ("ALSA: usb-audio: Do not expose sticky mixers") Link: https://bugzilla.suse.com/show_bug.cgi?id=1273166 Link: https://patch.msgid.link/20260807083418.1712585-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index c90c89200baf..90ca39dbed18 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2327,6 +2327,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */ QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0763, 0x2080, /* M-Audio Fast Track Ultra */ + QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x0763, 0x2081, /* M-Audio Fast Track Ultra */ + QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */ QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */ -- cgit v1.2.3 From 872f486259ae0bc6b73ca4735a15d013241f73e9 Mon Sep 17 00:00:00 2001 From: Hongjie Fang Date: Wed, 29 Jul 2026 19:16:14 +0800 Subject: scsi: core: pair EH runtime PM get and put shost->eh_noresume is currently consulted twice in one error handling iteration: once before scsi_autopm_get_host() and once again before scsi_autopm_put_host(). That is racy when a PM-triggered error path flips shost->eh_noresume while the SCSI EH thread is still running. The problem flow looks like this: PM path ufshcd_set_dev_pwr_mode() shost->eh_noresume = 1 ufshcd_execute_start_stop <-- trigger EH ... shost->eh_noresume = 0 EH path scsi_error_handler() if (!shost->eh_noresume) scsi_autopm_get_host() <-- skipped ... if (!shost->eh_noresume) scsi_autopm_put_host() <-- executed later In that case one EH iteration can skip autoresume on entry and still drop a runtime PM reference on exit. That leaves an unmatched runtime PM put and can trigger a runtime PM usage count underflow. Fix this by making eh_noresume a regular bool so it can be accessed with READ_ONCE() and WRITE_ONCE(). Snapshot it once per EH iteration and use that snapshot for both runtime PM get and put decisions. Fixes: ae0751ffc77e ("[SCSI] add flag to skip the runtime PM calls on the host") Signed-off-by: Hongjie Fang Reviewed-by: Bart Van Assche Reviewed-by: Peter Wang Link: https://patch.msgid.link/20260729111614.2407559-1-hongjiefang@asrmicro.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/scsi_error.c | 6 ++++-- drivers/ufs/core/ufshcd.c | 4 ++-- include/scsi/scsi_host.h | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 453a2232452d..74b70801269b 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -2362,6 +2362,7 @@ static void scsi_unjam_host(struct Scsi_Host *shost) int scsi_error_handler(void *data) { struct Scsi_Host *shost = data; + bool eh_noresume; /* * We use TASK_INTERRUPTIBLE so that the thread is not @@ -2403,7 +2404,8 @@ int scsi_error_handler(void *data) * what we need to do to get it up and online again (if we can). * If we fail, we end up taking the thing offline. */ - if (!shost->eh_noresume && scsi_autopm_get_host(shost) != 0) { + eh_noresume = READ_ONCE(shost->eh_noresume); + if (!eh_noresume && scsi_autopm_get_host(shost) != 0) { SCSI_LOG_ERROR_RECOVERY(1, shost_printk(KERN_ERR, shost, "scsi_eh_%d: unable to autoresume\n", @@ -2427,7 +2429,7 @@ int scsi_error_handler(void *data) * which are still online. */ scsi_restart_operations(shost); - if (!shost->eh_noresume) + if (!eh_noresume) scsi_autopm_put_host(shost); } __set_current_state(TASK_RUNNING); diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 13e6357a8bbb..a41b56dbf3b7 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -10016,7 +10016,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, * we are functional while we are here, skip host resume in error * handling context. */ - hba->host->eh_noresume = 1; + WRITE_ONCE(hba->host->eh_noresume, 1); /* * Current function would be generally called from the power management @@ -10038,7 +10038,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, } scsi_device_put(sdp); - hba->host->eh_noresume = 0; + WRITE_ONCE(hba->host->eh_noresume, 0); return ret; } diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index f6b286fa59f2..98b0ccf0813e 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -664,6 +664,9 @@ struct Scsi_Host { /* Asynchronous scan in progress */ bool async_scan __guarded_by(&scan_mutex); + /* Don't resume host in EH */ + bool eh_noresume; + unsigned active_mode:2; /* @@ -682,9 +685,6 @@ struct Scsi_Host { /* Task mgmt function in progress */ unsigned tmf_in_progress:1; - /* Don't resume host in EH */ - unsigned eh_noresume:1; - /* The controller does not support WRITE SAME */ unsigned no_write_same:1; -- cgit v1.2.3 From 8e7ff730dd96519a333d1570edf1c3fabb6d3629 Mon Sep 17 00:00:00 2001 From: Yao Kai Date: Tue, 4 Aug 2026 20:55:30 +0800 Subject: futex: Fix race in futex_pivot_pending() during private hash resize A task performing a custom private hash resize can remain blocked in uninterruptible sleep indefinitely. The hung-task detector reports: INFO: task futex-resizer:314 blocked for more than 10 seconds. task:futex-resizer state:D stack:14824 pid:314 tgid:312 ppid:311 Call Trace: __schedule+0x521/0xf30 schedule+0x22/0xa0 futex_hash_allocate+0x3db/0x490 __do_sys_prctl+0x6f5/0xbd0 do_syscall_64+0xf9/0x530 entry_SYSCALL_64_after_hwframe+0x77/0x7f Kernel panic - not syncing: hung_task: blocked tasks futex_pivot_pending() allows the resize request to continue when either no replacement hash is pending (hash_new == NULL) or the current hash reference count has reached zero. After the final-reference wake, another futex task can complete the pivot between the two observations: T1 T2 futex_hash_allocate() wait_var_event(mm, ...) futex_pivot_pending(mm) hash_new != NULL futex_hash() futex_ref_get(old) -> false futex_pivot_hash(mm) hash_new = NULL __futex_pivot_hash(mm, new) rcu_assign_pointer(hash, new) fph = rcu_dereference(hash) /* new */ futex_ref_is_dead(fph) -> false schedule() The pivot changes the state from hash_new != NULL with a dead current hash to hash_new == NULL with a live current hash. Because futex_pivot_pending() reads hash_new and hash without serialization, the resize task can observe hash_new in the pre-pivot state and hash in the post-pivot state, causing futex_pivot_pending() to return false even though the pivot has completed. The task then goes to sleep after the wakeup has already been consumed. Serialize state reads in futex_pivot_pending() using futex_mm_phash::lock. This guarantees that futex_pivot_pending() observes hash_new and hash atomically, eliminating the race condition. Fixes: bd54df5ea7ca ("futex: Allow to resize the private local hash") Suggested-by: Peter Zijlstra Signed-off-by: Yao Kai Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260804125530.3933754-1-yaokai34@huawei.com --- kernel/futex/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/futex/core.c b/kernel/futex/core.c index 2650d1e52803..128c5752f225 100644 --- a/kernel/futex/core.c +++ b/kernel/futex/core.c @@ -1783,14 +1783,15 @@ void futex_hash_free(struct mm_struct *mm) static bool futex_pivot_pending(struct mm_struct *mm) { + struct futex_mm_phash *mmph = &mm->futex.phash; struct futex_private_hash *fph; - guard(rcu)(); + guard(mutex)(&mmph->lock); - if (!mm->futex.phash.hash_new) + if (!mmph->hash_new) return true; - fph = rcu_dereference(mm->futex.phash.hash); + fph = rcu_dereference_raw(mmph->hash); return futex_ref_is_dead(fph); } -- cgit v1.2.3 From 732cb6bb37fd26863d5786522fb1997e7f5865b4 Mon Sep 17 00:00:00 2001 From: Zizhi Wo Date: Thu, 23 Jul 2026 12:12:38 +0800 Subject: scsi: core: Do not block on tag allocation in scsi_eh_lock_door() scsi_eh_lock_door() is called from scsi_restart_operations() while the host is still in the SHOST_RECOVERY state, i.e. before the host is switched back to SHOST_RUNNING and scsi_run_host_queues() restarts the queues. It allocates a request via scsi_alloc_request() with no flags, so blk_mq_get_tag() may block waiting for a free sched tag when all tags are already in use. Those tags can be held by commands that were just requeued by scsi_eh_flush_done_q() during error handling. Such commands cannot be dispatched until the host leaves SHOST_RECOVERY and scsi_run_host_queues() is called - which only happens *after* scsi_eh_lock_door() returns. This forms a circular dependency: - scsi_eh_lock_door(), running in the SCSI error handler thread, waits for a sched tag held by a requeued command; - the requeued command cannot complete and release its sched tag until the error handler thread leaves scsi_restart_operations() and restart the queues. For devices with a single driver tag (e.g. USB storage) it is a guaranteed deadlock and I/O that can never be submitted. This problem has also been reproduced in our environment. Locking the door is a best-effort operation, and scsi_eh_lock_door() already returns silently when the request allocation fails. Pass BLK_MQ_REQ_NOWAIT to scsi_alloc_request() so the allocation fails instead of blocking when no tag is available. This breaks the circular dependency and allows the error handler to finish restarting the queues, after which the pending commands are dispatched normally. Signed-off-by: Zizhi Wo Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260723041238.1584632-1-wozizhi@huaweicloud.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/scsi_error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 74b70801269b..b729407348f7 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -2161,7 +2161,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) struct scsi_cmnd *scmd; struct request *req; - req = scsi_alloc_request(sdev->request_queue, REQ_OP_DRV_IN, 0); + req = scsi_alloc_request(sdev->request_queue, REQ_OP_DRV_IN, BLK_MQ_REQ_NOWAIT); if (IS_ERR(req)) return; scmd = blk_mq_rq_to_pdu(req); -- cgit v1.2.3 From 24aa630f6259e6a2107936c06fed72063f712b64 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Fri, 7 Aug 2026 16:15:12 +0800 Subject: selftests/ftrace: Convert ELF entry point to file offset in uprobe test The add_remove_uprobe test uses readelf -h to obtain the ELF entry point (e_entry) and passes it directly as the offset to uprobe_events. However, uprobe_events expects a file offset, not a virtual address. For PIE binaries, the virtual address happens to equal the file offset because the first LOAD segment has p_vaddr == p_offset, so the test works by coincidence. But for non-PIE executables, e_entry is an absolute virtual address that can far exceed the file size. When the probe is enabled, uprobe_register() checks offset > i_size_read(inode) and rejects it with -EINVAL. Fix this by converting the virtual address to a file offset using the ELF program headers: scan readelf -lW output for the LOAD segment containing the entry point, then compute file_offset = e_entry - p_vaddr + p_offset. For PIE binaries the result is unchanged; for non-PIE binaries the offset is correctly translated. The conversion uses only POSIX shell primitives, with no dependency on gawk or perl. Link: https://lore.kernel.org/all/20260807081512.2974757-3-qirui.001@bytedance.com/ Fixes: dc4b165855f2 ("selftests/ftrace: Use readelf to find entry point in uprobe test") Cc: stable@vger.kernel.org Signed-off-by: Rui Qi Signed-off-by: Masami Hiramatsu (Google) --- .../ftrace/test.d/dynevent/add_remove_uprobe.tc | 27 ++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_uprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_uprobe.tc index f2048c244526..19430bd5864c 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_uprobe.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_uprobe.tc @@ -12,9 +12,32 @@ echo 0 > events/enable echo > dynamic_events REALBIN=`readlink -f /bin/sh` -ENTRYPOINT=`readelf -h ${REALBIN} | grep Entry | sed -e 's/[^0]*//'` -echo "p:myevent ${REALBIN}:${ENTRYPOINT}" >> uprobe_events +# Get the entry point virtual address from ELF header +ENTRY=`readelf -hW ${REALBIN} | grep "Entry point" | awk '{print $NF}'` + +# Convert virtual address to file offset: find the LOAD segment containing +# the entry point, then compute file_offset = e_entry - p_vaddr + p_offset. +# For PIE binaries this is a no-op (vaddr == file offset), but for non-PIE +# executables the virtual address is much larger than the file size and +# must be converted, otherwise uprobe_register() rejects it with -EINVAL. +ENTRY_DEC=$(printf '%d' "$ENTRY") +OFFSET=$ENTRY +while IFS= read -r line; do + set -- $line + [ "$1" = "LOAD" ] || continue + VA_DEC=$(printf '%d' "$3") + OFF_DEC=$(printf '%d' "$2") + FSZ_DEC=$(printf '%d' "$5") + if [ "$ENTRY_DEC" -ge "$VA_DEC" ] && [ "$ENTRY_DEC" -lt "$((VA_DEC + FSZ_DEC))" ]; then + OFFSET=$(printf '0x%x' "$((ENTRY_DEC - VA_DEC + OFF_DEC))") + break + fi +done << EOF +$(readelf -lW ${REALBIN} | grep LOAD) +EOF + +echo "p:myevent ${REALBIN}:${OFFSET}" >> uprobe_events grep -q myevent uprobe_events test -d events/uprobes/myevent -- cgit v1.2.3 From c4f6916a99cf105c3ff340b6210fcbba3fa66b35 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 3 Aug 2026 17:53:28 +0800 Subject: scsi: scsi_debug: Negate wrapped memcmp() result comp_write_worker() returns true when the compared data matches. memcmp() returns zero for equal data and non-zero for different data, so its result must be negated before it is stored in a bool. The first segment already uses !memcmp(), but the wrapped segment uses memcmp() directly, reversing the match result. Use !memcmp() there as well. Fixes: 38d5c8336e60 ("scsi_debug: add Report supported opcodes+tmfs; Compare and write") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Reviewed-by: John Garry Link: https://patch.msgid.link/E5AD42E9C0E18633+20260803095328.3445311-1-raoxu@uniontech.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/scsi/scsi_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 643051332132..4fb2dc403962 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -4310,8 +4310,8 @@ static bool comp_write_worker(struct sdeb_store_info *sip, u64 lba, u32 num, if (!res) return res; if (rest) - res = memcmp(fsp, arr + ((num - rest) * lb_size), - rest * lb_size); + res = !memcmp(fsp, arr + ((num - rest) * lb_size), + rest * lb_size); if (!res) return res; if (compare_only) -- cgit v1.2.3 From fa091f46c3833fb22384f10eade2b4e1e1d0b278 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Thu, 6 Aug 2026 13:56:55 -0700 Subject: perf: Reject exited events as group leaders perf_event_remove_on_exec() sets remove-on-exec events to the EXIT state and detaches their group relationships. The event's file descriptor can remain open, however, and perf_event_open() currently accepts that event as a group leader because its early validation rejects only REVOKED and DEAD events. A new sibling can consequently be linked to the detached leader. When the leader is closed, perf_group_detach() observes that its PERF_ATTACH_GROUP bit is already clear and skips the new sibling. The sibling then retains a group_leader pointer to the freed event. Reject group leaders in the EXIT state. Perform the check while holding the shared context mutex so that an exec in the target task cannot detach the leader between validation and group attachment. [peterz: make the earlier test fully consistent] Fixes: 037a3c43edfb ("perf/core: Detach event groups during remove_on_exec") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Kyle Zeng Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260806205655.75722-1-kylebot@openai.com --- kernel/events/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index ba5bd6a78fe7..bd25e1998faf 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -13972,7 +13972,7 @@ SYSCALL_DEFINE5(perf_event_open, goto err_fd; } group_leader = fd_file(group)->private_data; - if (group_leader->state <= PERF_EVENT_STATE_REVOKED) { + if (group_leader->state <= PERF_EVENT_STATE_EXIT) { err = -ENODEV; goto err_fd; } @@ -14103,6 +14103,12 @@ SYSCALL_DEFINE5(perf_event_open, if (group_leader->ctx != ctx) goto err_locked; + /* Recheck under ctx::mutex to serialize against remove-on-exec. */ + if (group_leader->state <= PERF_EVENT_STATE_EXIT) { + err = -ENODEV; + goto err_locked; + } + /* * Only a group leader can be exclusive or pinned */ -- cgit v1.2.3 From 42c5ca1f0a288a52878bd72a5595b08261057438 Mon Sep 17 00:00:00 2001 From: Aditya Chillara Date: Fri, 7 Aug 2026 18:11:52 +0530 Subject: perf/core: Fix group leader use-after-free after sibling detach perf_group_detach() handles leader and sibling detach differently. When the group leader is detached, all siblings are promoted to singleton events and their group_leader pointer is reset to themselves. When a sibling is detached, it is removed from the leader's sibling_list, but its group_leader pointer is left pointing at the old leader. That is harmless when the sibling is being closed and freed immediately, as in the DETACH_DEAD path. It is not safe when the sibling is detached but kept alive, such as during CPU hotplug with DETACH_GROUP. In that case the sibling is removed from the context, while its file descriptor can still keep it alive. A typical failing sequence is: - A group contains leader L and sibling S. - CPU hot-unplug detaches S with DETACH_GROUP, removing it from L->sibling_list but leaving S->group_leader == L. - L is later closed and freed. - A PERF_IOC_FLAG_GROUP ioctl on S follows S->group_leader and dereferences the freed leader. This was reproduced by running the perf event fuzzer, CPU hotplug, and a stress workload concurrently: Unable to handle kernel paging request at virtual address 006b6b6b6b6b6cdb CPU: 2 PID: 12489 Comm: perf_fuzzer 6.18.7 PREEMPT pc : perf_ioctl+0x34c/0xc68 x20: ffffff89a3fa2c70 x8 : 6b6b6b6b6b6b6b6b Code: 943c4a0e 340047a0 f9404a94 f9411e88 (f940b908) Call trace: perf_ioctl+0x34c/0xc68 (P) __arm64_sys_ioctl+0xa0/0xf4 invoke_syscall+0x58/0xe4 el0_svc_common+0xa8/0xdc do_el0_svc+0x1c/0x28 el0_svc+0x40/0xc0 el0t_64_sync_handler+0x68/0xdc el0t_64_sync+0x1c4/0x1c8 The fault happened in perf_ioctl(), where perf_event_for_each() follows the stale group_leader pointer and perf_event_for_each_child() then dereferences the freed leader's context. Fix the use-after-free by promoting the detached sibling to a singleton. Also fix __event_disable() cgroup accounting and event state change. Fixes: 8a49542c0554 ("perf_events: Fix races in group composition") Assisted-by: PatchWise:gpt-5.5 Signed-off-by: Aditya Chillara Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dapeng Mi Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260807-fix-group-leader-uaf-v3-1-b0c2310c9a0d@oss.qualcomm.com --- kernel/events/core.c | 67 +++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index bd25e1998faf..1a73ba0747df 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -2343,6 +2343,34 @@ static inline struct list_head *get_event_list(struct perf_event *event) &event->pmu_ctx->flexible_active; } +/* @sibling must already be unlinked from its old leader's sibling_list. */ +static void perf_promote_sibling_to_leader(struct perf_event *sibling, + struct perf_event_context *ctx, + int group_caps) +{ + /* + * Events that have PERF_EV_CAP_SIBLING require being part of + * a group and cannot exist on their own, schedule them out + * and move them into the ERROR state. Also see + * _perf_event_enable(), it will not be able to recover this + * ERROR state. + */ + if (sibling->event_caps & PERF_EV_CAP_SIBLING) + __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR); + + sibling->group_leader = sibling; + sibling->group_caps = group_caps; + + if (sibling->attach_state & PERF_ATTACH_CONTEXT) { + add_event_to_groups(sibling, ctx); + + if (sibling->state == PERF_EVENT_STATE_ACTIVE) + list_add_tail(&sibling->active_list, get_event_list(sibling)); + } + + perf_event__header_size(sibling); +} + static void perf_group_detach(struct perf_event *event) { struct perf_event *leader = event->group_leader; @@ -2366,8 +2394,9 @@ static void perf_group_detach(struct perf_event *event) */ if (leader != event) { list_del_init(&event->sibling_list); - event->group_leader->nr_siblings--; - event->group_leader->group_generation++; + leader->nr_siblings--; + leader->group_generation++; + perf_promote_sibling_to_leader(event, ctx, event->event_caps); goto out; } @@ -2377,32 +2406,14 @@ static void perf_group_detach(struct perf_event *event) * to whatever list we are on. */ list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) { - - /* - * Events that have PERF_EV_CAP_SIBLING require being part of - * a group and cannot exist on their own, schedule them out - * and move them into the ERROR state. Also see - * _perf_event_enable(), it will not be able to recover this - * ERROR state. - */ - if (sibling->event_caps & PERF_EV_CAP_SIBLING) - __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR); - - sibling->group_leader = sibling; list_del_init(&sibling->sibling_list); /* Inherit group flags from the previous leader */ - sibling->group_caps = event->group_caps; - - if (sibling->attach_state & PERF_ATTACH_CONTEXT) { - add_event_to_groups(sibling, event->ctx); - - if (sibling->state == PERF_EVENT_STATE_ACTIVE) - list_add_tail(&sibling->active_list, get_event_list(sibling)); - } + perf_promote_sibling_to_leader(sibling, ctx, event->group_caps); WARN_ON_ONCE(sibling->ctx != event->ctx); } + event->nr_siblings = 0; out: for_each_sibling_event(tmp, leader) @@ -2592,12 +2603,7 @@ __perf_remove_from_context(struct perf_event *event, if (flags & DETACH_DEAD) state = PERF_EVENT_STATE_DEAD; - event_sched_out(event, ctx); - - if (event->state > PERF_EVENT_STATE_OFF) - perf_cgroup_event_disable(event, ctx); - - perf_event_set_state(event, min(event->state, state)); + __event_disable(event, ctx, state); if (flags & DETACH_GROUP) perf_group_detach(event); @@ -2666,8 +2672,9 @@ static void __event_disable(struct perf_event *event, enum perf_event_state state) { event_sched_out(event, ctx); - perf_cgroup_event_disable(event, ctx); - perf_event_set_state(event, state); + if (event->state > PERF_EVENT_STATE_OFF) + perf_cgroup_event_disable(event, ctx); + perf_event_set_state(event, min(event->state, state)); } /* -- cgit v1.2.3 From 3dbb44d88b1e94dd31fe43588af7437b34b44d56 Mon Sep 17 00:00:00 2001 From: Jérémy Jean Date: Tue, 4 Aug 2026 20:00:42 +0000 Subject: sctp: validate cookie AUTH state before use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cookie authentication is disabled, COOKIE_ECHO restores fixed-size AUTH fields directly from peer-controlled cookie bytes. A forged RANDOM length, HMAC list, or CHUNKS list can then reach association consumers with lengths or identifiers that were never validated against the local backing arrays. A forged RANDOM length can cause out-of-bounds reads during key-vector construction. A forged HMAC identifier also caused a 32-byte write past a zero-length AUTH chunk, providing a primitive for a local privilege escalation chain. Validate the cookie's RANDOM, HMACS, and CHUNKS parameters at the cookie trust boundary before copying them into the association. Reject invalid types, malformed lengths, unsupported HMAC identifiers, HMAC lists without SHA1, and forbidden chunk ids. Fixes: bbd0d59809f9 ("[SCTP]: Implement the receive and verification of AUTH chunk") Fixes: 1f485649f529 ("[SCTP]: Implement SCTP-AUTH internals") Signed-off-by: Jérémy Jean Acked-by: Xin Long Link: https://patch.msgid.link/20260804200042.2412009-1-Jeremy.Jean@oss.cyber.gouv.fr Signed-off-by: Jakub Kicinski --- include/net/sctp/auth.h | 3 ++ net/sctp/auth.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ net/sctp/sm_make_chunk.c | 3 ++ 3 files changed, 81 insertions(+) diff --git a/include/net/sctp/auth.h b/include/net/sctp/auth.h index 6f2cd562b1de..74b3790e2a3d 100644 --- a/include/net/sctp/auth.h +++ b/include/net/sctp/auth.h @@ -22,6 +22,7 @@ struct sctp_endpoint; struct sctp_association; struct sctp_authkey; struct sctp_hmacalgo; +struct sctp_cookie; /* Defines an HMAC algorithm supported by SCTP chunk authentication */ struct sctp_hmac { @@ -72,6 +73,8 @@ struct sctp_shared_key *sctp_auth_get_shkey( int sctp_auth_asoc_copy_shkeys(const struct sctp_endpoint *ep, struct sctp_association *asoc, gfp_t gfp); +bool sctp_auth_verify_cookie_params(const struct sctp_endpoint *ep, + const struct sctp_cookie *cookie); const struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id); const struct sctp_hmac * sctp_auth_asoc_get_hmac(const struct sctp_association *asoc); diff --git a/net/sctp/auth.c b/net/sctp/auth.c index c901d373af80..cc4229ee116d 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -377,6 +377,81 @@ nomem: return -ENOMEM; } +static bool sctp_auth_chunk_id_forbidden(__u8 chunk_id) +{ + switch (chunk_id) { + case SCTP_CID_INIT: + case SCTP_CID_INIT_ACK: + case SCTP_CID_SHUTDOWN_COMPLETE: + case SCTP_CID_AUTH: + return true; + default: + return false; + } +} + +/* Verify AUTH parameters copied from a state cookie before they are restored + * into an association. When cookie authentication is disabled these fields + * are peer-controlled, so they must satisfy the same constraints as locally + * generated AUTH parameters. + */ +bool sctp_auth_verify_cookie_params(const struct sctp_endpoint *ep, + const struct sctp_cookie *cookie) +{ + const struct sctp_paramhdr *random; + const struct sctp_hmac_algo_param *hmacs; + const struct sctp_chunks_param *chunks; + u16 hmacs_len, chunks_len; + u16 n_hmacs, n_chunks, i; + bool has_sha1 = false; + + if (sctp_sk(ep->base.sk)->cookie_auth_enable || !ep->auth_enable) + return true; + + random = (const struct sctp_paramhdr *)cookie->auth_random; + if (random->type != SCTP_PARAM_RANDOM || + ntohs(random->length) != sizeof(*random) + SCTP_AUTH_RANDOM_LENGTH) + return false; + + hmacs = (const struct sctp_hmac_algo_param *)cookie->auth_hmacs; + hmacs_len = ntohs(hmacs->param_hdr.length); + if (hmacs->param_hdr.type != SCTP_PARAM_HMAC_ALGO || + hmacs_len < sizeof(struct sctp_paramhdr) + + sizeof(hmacs->hmac_ids[0]) || + hmacs_len > sizeof(cookie->auth_hmacs) || + (hmacs_len - sizeof(struct sctp_paramhdr)) % + sizeof(hmacs->hmac_ids[0])) + return false; + + n_hmacs = (hmacs_len - sizeof(struct sctp_paramhdr)) / + sizeof(hmacs->hmac_ids[0]); + for (i = 0; i < n_hmacs; i++) { + u16 hmac_id = ntohs(hmacs->hmac_ids[i]); + + if (!sctp_hmac_supported(hmac_id)) + return false; + if (hmac_id == SCTP_AUTH_HMAC_ID_SHA1) + has_sha1 = true; + } + if (!has_sha1) + return false; + + chunks = (const struct sctp_chunks_param *)cookie->auth_chunks; + chunks_len = ntohs(chunks->param_hdr.length); + if (chunks->param_hdr.type != SCTP_PARAM_CHUNKS || + chunks_len < sizeof(struct sctp_paramhdr) || + chunks_len > sizeof(cookie->auth_chunks)) + return false; + + n_chunks = chunks_len - sizeof(struct sctp_paramhdr); + for (i = 0; i < n_chunks; i++) { + if (sctp_auth_chunk_id_forbidden(chunks->chunks[i])) + return false; + } + + return true; +} + /* Public interface to create the association shared key. * See code above for the algorithm. diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e25612e9d082..236e25abc7a4 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1852,6 +1852,9 @@ struct sctp_association *sctp_unpack_cookie( /* Set up our peer's port number. */ retval->peer.port = ntohs(chunk->sctp_hdr->source); + if (!sctp_auth_verify_cookie_params(ep, bear_cookie)) + goto malformed; + /* Populate the association from the cookie. */ memcpy(&retval->c, bear_cookie, sizeof(*bear_cookie)); -- cgit v1.2.3 From 994dad686e755477e2b2700cca4e6e1a90a58bdc Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Tue, 21 Jul 2026 23:05:09 +0800 Subject: riscv: hwprobe: Register unaligned probes before usermode The hwprobe vDSO data is populated by the first riscv_hwprobe syscall. Some values, such as MISALIGNED_VECTOR_PERF, may depend on the async vector unaligned access speed probe registered by check_unaligned_access_all_cpus(). That initcall currently runs at late_initcall level. However, rootfs_initcall enables usermode helpers before late initcalls run, so an early helper can execute userspace and call riscv_hwprobe first. In that case complete_hwprobe_vdso_data() consumes the initial pending_boot_probes reference, populates the vDSO cache, and marks it ready before the later async probe is registered. The eventual probe result then cannot update the already-ready cache. Move check_unaligned_access_all_cpus() to fs_initcall_sync. This still runs after clocksource_done_booting(), so the ktime_get_mono_fast_ns() benchmark uses a stable clocksource, but it runs before rootfs_initcall enables usermode helpers. Any async hwprobe probe is therefore registered before userspace can trigger the one-time vDSO cache population. Cc: stable@vger.kernel.org Fixes: 6455c6c11827 ("riscv: Clean up & optimize unaligned scalar access probe") Signed-off-by: Rui Qi Reviewed-by: Nam Cao Link: https://patch.msgid.link/20260721150511.1607105-1-qirui.001@bytedance.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/unaligned_access_speed.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 5a5aa22124e7..36201613d2ca 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -411,4 +411,10 @@ static int __init check_unaligned_access_all_cpus(void) return 0; } -late_initcall(check_unaligned_access_all_cpus); +/* + * Run after clocksource_done_booting() so measure_cycles() uses a stable + * clocksource, but before rootfs_initcall() enables usermode helpers. Those + * helpers can reach hwprobe and populate the vDSO cache, so async hwprobe + * probes must be registered first. + */ +fs_initcall_sync(check_unaligned_access_all_cpus); -- cgit v1.2.3 From 33f2b2eb33d666ecac68031e0f31424fb70528db Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Mon, 3 Aug 2026 22:40:38 -0700 Subject: net: fix skb length accounting after generic XDP frag adjustment Generic XDP exposes non-linear skb fragments through an xdp_buff. If an XDP program adjusts the fragment area, bpf_prog_run_generic_xdp() copies xdp_frags_size back to skb->data_len but leaves skb->len containing the old fragment contribution. After a fragment shrink, this makes skb_headlen() larger than the actual linear area. In the reproduced UDP receive path, __skb_datagram_iter() copied 1024 bytes past the actual linear tail to userspace, starting at struct skb_shared_info. The copied bytes included the affected skb's nr_frags, xdp_frags_size and a kernel pointer from skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same amount and truncated at the end. Subtract the old data_len before replacing it and add the new data_len afterwards, keeping skb->len and skb->data_len synchronized. A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by 1024 bytes from its fragment area. Before the fix, all 10 runs produced corrupted payloads. After the fix, all 10 runs matched the expected payload exactly. Fixes: e6d5dbdd20aa ("xdp: add multi-buff support for xdp running in generic mode") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/ Reviewed-by: Mohsin Bashir Acked-by: Lorenzo Bianconi Signed-off-by: Sun Jian Link: https://patch.msgid.link/20260804054040.613675-2-sun.jian.kdev@gmail.com Signed-off-by: Jakub Kicinski --- net/core/dev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index af260ff5462a..ece6700536d9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5517,12 +5517,16 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp, } /* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers - * (e.g. bpf_xdp_adjust_tail), we need to update data_len here. + * (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution + * from skb->len before updating data_len, then add the new one back. */ - if (xdp_buff_has_frags(xdp)) + skb->len -= skb->data_len; + if (xdp_buff_has_frags(xdp)) { skb->data_len = skb_shinfo(skb)->xdp_frags_size; - else + skb->len += skb->data_len; + } else { skb->data_len = 0; + } /* check if XDP changed eth hdr such SKB needs update */ eth = (struct ethhdr *)xdp->data; -- cgit v1.2.3 From cb6379feaaff11c4e1e79c26c745ffa23182768a Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Mon, 3 Aug 2026 22:40:39 -0700 Subject: veth: fix skb length accounting after XDP frag adjustment veth exposes non-linear skb fragments through an xdp_buff. If an XDP program adjusts the fragment area, veth_xdp_rcv_skb() copies xdp_frags_size back to skb->data_len but leaves skb->len containing the old fragment contribution. After a fragment shrink, this makes skb_headlen() larger than the actual linear area. In the reproduced UDP receive path, __skb_datagram_iter() copied 1024 bytes past the actual linear tail to userspace, starting at struct skb_shared_info. The copied bytes included the affected skb's nr_frags, xdp_frags_size, and a kernel pointer from skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same amount and truncated at the end. Subtract the old data_len before replacing it and add the new data_len afterwards, keeping skb->len and skb->data_len synchronized. Additionally, bpf_xdp_pull_data() can advance data_end while leaving frags present. The skb is then still non-linear, so the old __skb_put(skb, off) triggers SKB_LINEAR_ASSERT(). Use skb_set_tail_pointer() and update skb->len explicitly instead, following bpf_prog_run_generic_xdp(). Unlike __skb_put(), skb_set_tail_pointer() does not require a linear skb. A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by 1024 bytes from its fragment area. Before the fix, all 10 runs produced corrupted payloads. After the fix, all 10 runs matched the expected payload exactly. A forced-tailroom reproducer also exercises bpf_xdp_pull_data() with frags still present; the old code triggers SKB_LINEAR_ASSERT(), while this fix passes 10/10 runs. Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb") Cc: stable@vger.kernel.org Reported-by: Mohsin Bashir Link: https://lore.kernel.org/bpf/80687d9c-9c27-494c-b3f2-efd0230b1895@gmail.com/ Suggested-by: Lorenzo Bianconi Acked-by: Lorenzo Bianconi Signed-off-by: Sun Jian Link: https://patch.msgid.link/20260804054040.613675-3-sun.jian.kdev@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/veth.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 00e34afd858e..f80f6584a6ff 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -865,18 +865,24 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, skb_reset_mac_header(skb); - /* check if bpf_xdp_adjust_tail was used */ - off = xdp->data_end - orig_data_end; - if (off != 0) - __skb_put(skb, off); /* positive on grow, negative on shrink */ - /* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers - * (e.g. bpf_xdp_adjust_tail), we need to update data_len here. + * (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution + * from skb->len before updating data_len, then add the new one back. */ - if (xdp_buff_has_frags(xdp)) + skb->len -= skb->data_len; + if (xdp_buff_has_frags(xdp)) { skb->data_len = skb_shinfo(skb)->xdp_frags_size; - else + skb->len += skb->data_len; + } else { skb->data_len = 0; + } + + /* Synchronize the skb tail with XDP's updated linear area. */ + off = xdp->data_end - orig_data_end; + if (off != 0) { + skb_set_tail_pointer(skb, xdp->data_end - xdp->data); + skb->len += off; /* positive on grow, negative on shrink */ + } skb->protocol = eth_type_trans(skb, rq->dev); -- cgit v1.2.3 From dd057113ac7ba5bdd2aed3d9405305911152f911 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Thu, 6 Aug 2026 12:49:22 -0700 Subject: mailmap: add entries for Christoph Paasch Map the email addresses used for previous kernel contributions to the current OpenAI address. This prevents get_maintainer.pl from listing historical addresses as patch recipients. Suggested-by: Matthieu Baerts Signed-off-by: Christoph Paasch Acked-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260806-b4-mailman-v1-1-b4d7bc0ffd1c@openai.com Signed-off-by: Jakub Kicinski --- .mailmap | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.mailmap b/.mailmap index 01453c5e1ab4..8032f87661f6 100644 --- a/.mailmap +++ b/.mailmap @@ -211,6 +211,10 @@ Christophe Ricard Christopher Obbard Christoph Hellwig Christoph Manszewski +Christoph Paasch +Christoph Paasch +Christoph Paasch +Christoph Paasch Chuck Lever Chuck Lever Chuck Lever -- cgit v1.2.3 From 5b17f3f34391372faf03e79d947e0c50ab6dd258 Mon Sep 17 00:00:00 2001 From: George Wilson Date: Fri, 7 Aug 2026 11:56:21 -0500 Subject: powerpc/pseries: papr-phy-attest - validate cmd.length, plug mem leak In papr_phy_attest_create_handle(), the params->cmd.length is not validated before use, which can result in a buffer overlow. Check it and return -EINVAL if it is either 0 or exceeds sizeof(params->cmd). Also, params is freed on the success path but not error. Free it on errors after memory allocation. And free it on negative fd. Fixes: 86900ab620a4 ("powerpc/pseries: Add a char driver for physical-attestation RTAS") Acked-by: Haren Myneni Acked-by: Nayna Jain Tested-by: R Nageswara Sastry Cc: stable@vger.kernel.org # 6.16 Signed-off-by: George Wilson Signed-off-by: Madhavan Srinivasan --- arch/powerpc/platforms/pseries/papr-phy-attest.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/papr-phy-attest.c b/arch/powerpc/platforms/pseries/papr-phy-attest.c index 20a0e1581302..350ba26e5962 100644 --- a/arch/powerpc/platforms/pseries/papr-phy-attest.c +++ b/arch/powerpc/platforms/pseries/papr-phy-attest.c @@ -230,10 +230,17 @@ static long papr_phy_attest_create_handle(struct papr_phy_attest_io_block __user return -ENOMEM; if (copy_from_user(¶ms->cmd, ulc, - sizeof(struct papr_phy_attest_io_block))) + sizeof(struct papr_phy_attest_io_block))) { + kfree(params); return -EFAULT; + } params->cmd_len = be32_to_cpu(params->cmd.length); + if (params->cmd_len == 0 || params->cmd_len > sizeof(params->cmd)) { + kfree(params); + return -EINVAL; + } + seq = (struct papr_rtas_sequence) { .begin = phy_attest_sequence_begin, .end = phy_attest_sequence_end, @@ -246,6 +253,9 @@ static long papr_phy_attest_create_handle(struct papr_phy_attest_io_block __user &papr_phy_attest_handle_ops, "[papr-physical-attestation]"); + if (fd < 0) + kfree(params); + return fd; } -- cgit v1.2.3 From 649c10bff5cb7a514bf299094833ec8c9190aac3 Mon Sep 17 00:00:00 2001 From: George Wilson Date: Fri, 7 Aug 2026 11:58:36 -0500 Subject: powerpc/pseries: pci - logic bug The checks on num_vfs in pseries_pci_sriov_enable() are ANDed where OR was apparently intended. Change it to OR. Fixes: 9a7f6b438664 ("powerpc/pseries/pci: Associate PEs to VFs in configure SR-IOV") Acked-by: Nayna Jain Tested-by: R Nageswara Sastry Cc: stable@vger.kernel.org # 4.16 Signed-off-by: George Wilson Signed-off-by: Madhavan Srinivasan --- arch/powerpc/platforms/pseries/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c index 84e4ffe957a8..d11a64a086c1 100644 --- a/arch/powerpc/platforms/pseries/pci.c +++ b/arch/powerpc/platforms/pseries/pci.c @@ -132,7 +132,7 @@ static int pseries_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) /* First integer stores max config */ max_config_vfs = of_read_number(&max_vfs[0], 1); - if (max_config_vfs < num_vfs && num_vfs > MAX_VFS_FOR_MAP_PE) { + if (max_config_vfs < num_vfs || num_vfs > MAX_VFS_FOR_MAP_PE) { dev_err(&pdev->dev, "Num VFs %x > %x Configurable VFs\n", num_vfs, (num_vfs > MAX_VFS_FOR_MAP_PE) ? -- cgit v1.2.3 From fb442a6673ff1046bf67754957d95880fdb394b5 Mon Sep 17 00:00:00 2001 From: George Wilson Date: Fri, 7 Aug 2026 11:59:00 -0500 Subject: powerpc/pseries: lparcfg - fix kbuf[] underflow In lparcfg_write(), a count of 0 results in kbuf[] being indexed at -1. Check for count == 0 in the existing check for count > sizeof(kbuf) and return -EINVAL if true. Fixes: 74422e2b1939 ("powerpc/pseries: Remove VLA from lparcfg_write()") Acked-by: Nayna Jain Tested-by: R Nageswara Sastry Cc: stable@vger.kernel.org # 4.20 Signed-off-by: George Wilson Signed-off-by: Madhavan Srinivasan --- arch/powerpc/platforms/pseries/lparcfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c index 54b7ecf375b5..3280b7968cab 100644 --- a/arch/powerpc/platforms/pseries/lparcfg.c +++ b/arch/powerpc/platforms/pseries/lparcfg.c @@ -699,7 +699,7 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf, if (!firmware_has_feature(FW_FEATURE_SPLPAR)) return -EINVAL; - if (count > sizeof(kbuf)) + if (count == 0 || count > sizeof(kbuf)) return -EINVAL; if (copy_from_user(kbuf, buf, count)) -- cgit v1.2.3 From d15d51fb26e830af58f3f21964f1c09c239077ea Mon Sep 17 00:00:00 2001 From: Melbin K Mathew Date: Thu, 2 Jul 2026 01:42:46 +0200 Subject: fbdev: bound mode sysfs output to the sysfs buffer mode_string() uses snprintf() which can return a value larger than the remaining buffer space. show_modes() accumulates the return value into i without checking whether i has reached PAGE_SIZE, causing the offset to advance past the sysfs buffer if the modelist is long enough. Add a size parameter to mode_string() and use scnprintf() to return only the bytes actually written. Add an early return when offset already exceeds the buffer. In show_modes(), stop accumulating once the buffer is full. Cc: stable@vger.kernel.org # v7.1+ Signed-off-by: Melbin K Mathew Signed-off-by: Helge Deller --- drivers/video/fbdev/core/fbsysfs.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index ea196603c7a8..af21dc5052df 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -27,12 +27,15 @@ static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) return 0; } -static int mode_string(char *buf, unsigned int offset, +static int mode_string(char *buf, size_t size, unsigned int offset, const struct fb_videomode *mode) { char m = 'U'; char v = 'p'; + if (offset >= size) + return 0; + if (mode->flag & FB_MODE_IS_DETAILED) m = 'D'; if (mode->flag & FB_MODE_IS_VESA) @@ -45,7 +48,7 @@ static int mode_string(char *buf, unsigned int offset, if (mode->vmode & FB_VMODE_DOUBLE) v = 'd'; - return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n", + return scnprintf(&buf[offset], size - offset, "%c:%dx%d%c-%d\n", m, mode->xres, mode->yres, v, mode->refresh); } @@ -64,7 +67,7 @@ static ssize_t store_mode(struct device *device, struct device_attribute *attr, list_for_each_entry(modelist, &fb_info->modelist, list) { mode = &modelist->mode; - i = mode_string(mstr, 0, mode); + i = mode_string(mstr, sizeof(mstr), 0, mode); if (strncmp(mstr, buf, max(count, i)) == 0) { var = fb_info->var; @@ -86,7 +89,7 @@ static ssize_t show_mode(struct device *device, struct device_attribute *attr, if (!fb_info->mode) return 0; - return mode_string(buf, 0, fb_info->mode); + return mode_string(buf, PAGE_SIZE, 0, fb_info->mode); } static ssize_t store_modes(struct device *device, @@ -136,7 +139,9 @@ static ssize_t show_modes(struct device *device, struct device_attribute *attr, i = 0; list_for_each_entry(modelist, &fb_info->modelist, list) { mode = &modelist->mode; - i += mode_string(buf, i, mode); + i += mode_string(buf, PAGE_SIZE, i, mode); + if (i >= PAGE_SIZE - 1) + break; } return i; } -- cgit v1.2.3 From 95e647d2a5304a8fd11f1ba3c8502de700650131 Mon Sep 17 00:00:00 2001 From: Melbin K Mathew Date: Thu, 2 Jul 2026 01:42:47 +0200 Subject: fbdev: clear fb_info->mode before deleting a videomode fb_set_var() can delete a mode from info->modelist when userspace passes FB_ACTIVATE_INV_MODE through FBIOPUT_VSCREENINFO. The code checks that the mode being deleted is not the current info->var and that fbcon is not using it, but it does not check fb_info->mode. fb_info->mode may still point into the modelist entry being deleted. If the entry is freed, later mode sysfs reads through show_mode() can dereference a stale pointer. Clear fb_info->mode before calling fb_delete_videomode() when it matches the mode being removed. Cc: stable@vger.kernel.org # v7.1+ Signed-off-by: Melbin K Mathew Signed-off-by: Helge Deller --- drivers/video/fbdev/core/fbmem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 2f1c56e5a7a2..c8aa163b0ecf 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -246,8 +246,11 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) ret = fb_mode_is_equal(&mode1, &mode2); if (!ret) { ret = fbcon_mode_deleted(info, &mode1); - if (!ret) + if (!ret) { + if (info->mode && fb_mode_is_equal(info->mode, &mode1)) + info->mode = NULL; fb_delete_videomode(&mode1, &info->modelist); + } } return ret ? -EINVAL : 0; -- cgit v1.2.3 From 061db6b7a910b8378f3b2df64f8c0a3ddc6e85f2 Mon Sep 17 00:00:00 2001 From: Melbin K Mathew Date: Thu, 2 Jul 2026 01:42:48 +0200 Subject: fbdev: serialize mode sysfs access with lock_fb_info() show_mode(), show_modes(), and store_mode() access fb_info->modelist and fb_info->mode without holding lock_fb_info(). store_modes() takes lock_fb_info() while replacing the modelist and freeing the old one. A concurrent reader or writer can load a pointer to an old modelist entry before store_modes() frees it, then dereference freed memory or store a stale freed pointer in fb_info->mode. Take lock_fb_info() in show_mode(), show_modes(), and store_mode() to serialize with store_modes(). In show_mode(), copy the mode to the stack and format after dropping the lock. In store_mode(), split activate() into a _locked variant to avoid double-locking, and hold the locks for the modelist walk, mode conversion, activation, and fb_info->mode assignment together. Cc: stable@vger.kernel.org # v7.1+ Signed-off-by: Melbin K Mathew Signed-off-by: Helge Deller --- drivers/video/fbdev/core/fbsysfs.c | 46 +++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index af21dc5052df..d3d60c555bb8 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -12,19 +12,24 @@ #include "fb_internal.h" #include "fbcon.h" +static int activate_locked(struct fb_info *fb_info, + struct fb_var_screeninfo *var) +{ + var->activate |= FB_ACTIVATE_FORCE; + return fb_set_var_from_user(fb_info, var); +} + static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { int err; - var->activate |= FB_ACTIVATE_FORCE; console_lock(); lock_fb_info(fb_info); - err = fb_set_var_from_user(fb_info, var); + err = activate_locked(fb_info, var); unlock_fb_info(fb_info); console_unlock(); - if (err) - return err; - return 0; + + return err; } static int mode_string(char *buf, size_t size, unsigned int offset, @@ -65,6 +70,9 @@ static ssize_t store_mode(struct device *device, struct device_attribute *attr, memset(&var, 0, sizeof(var)); + console_lock(); + lock_fb_info(fb_info); + list_for_each_entry(modelist, &fb_info->modelist, list) { mode = &modelist->mode; i = mode_string(mstr, sizeof(mstr), 0, mode); @@ -72,12 +80,22 @@ static ssize_t store_mode(struct device *device, struct device_attribute *attr, var = fb_info->var; fb_videomode_to_var(&var, mode); - if ((err = activate(fb_info, &var))) + err = activate_locked(fb_info, &var); + if (err) { + unlock_fb_info(fb_info); + console_unlock(); return err; + } fb_info->mode = mode; + unlock_fb_info(fb_info); + console_unlock(); return count; } } + + unlock_fb_info(fb_info); + console_unlock(); + return -EINVAL; } @@ -85,11 +103,20 @@ static ssize_t show_mode(struct device *device, struct device_attribute *attr, char *buf) { struct fb_info *fb_info = dev_get_drvdata(device); + struct fb_videomode mode; + bool have_mode = false; - if (!fb_info->mode) + lock_fb_info(fb_info); + if (fb_info->mode) { + mode = *fb_info->mode; + have_mode = true; + } + unlock_fb_info(fb_info); + + if (!have_mode) return 0; - return mode_string(buf, PAGE_SIZE, 0, fb_info->mode); + return mode_string(buf, PAGE_SIZE, 0, &mode); } static ssize_t store_modes(struct device *device, @@ -137,12 +164,15 @@ static ssize_t show_modes(struct device *device, struct device_attribute *attr, const struct fb_videomode *mode; i = 0; + lock_fb_info(fb_info); list_for_each_entry(modelist, &fb_info->modelist, list) { mode = &modelist->mode; i += mode_string(buf, PAGE_SIZE, i, mode); if (i >= PAGE_SIZE - 1) break; } + unlock_fb_info(fb_info); + return i; } -- cgit v1.2.3 From 81cc73be40c6f028f1ee3f438ace46afe666dbae Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Tue, 21 Jul 2026 16:19:42 +0800 Subject: fbdev: core: Fix pointer desynchronization in fb_io_read() In fb_io_read(), if copy_to_user() performs a partial copy (e.g., due to a faulty user buffer), the loop adjusts the chunk size 'c' and updates the remaining 'count'. However, the hardware 'src' pointer has already been eagerly advanced by the original chunk size. If the loop is allowed to continue, the read will resume from an incorrect, over-advanced offset. Since the remaining 'count' was only decremented by the successful bytes, this desynchronization causes the next iterations to execute more hardware reads than originally bounded, eventually leading to out-of-bounds I/O reads. Fix this by breaking out of the loop immediately upon a partial copy_to_user(). A partial copy indicates a faulty user buffer, making subsequent read attempts futile. Breaking out ensures we return the number of successfully read bytes without risking out-of-bounds hardware accesses in subsequent mismatched iterations. Fixes: 6121cd9ef911 ("fbdev: Move I/O read and write code into helper functions") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Signed-off-by: Helge Deller --- drivers/video/fbdev/core/fb_io_fops.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/video/fbdev/core/fb_io_fops.c b/drivers/video/fbdev/core/fb_io_fops.c index 6ab60fcd0050..0798e88799eb 100644 --- a/drivers/video/fbdev/core/fb_io_fops.c +++ b/drivers/video/fbdev/core/fb_io_fops.c @@ -61,6 +61,14 @@ ssize_t fb_io_read(struct fb_info *info, char __user *buf, size_t count, loff_t buf += c; cnt += c; count -= c; + + /* + * If there was a partial copy, the user buffer is faulty. + * Break out to avoid over-advancing the src pointer and + * reading out of bounds in the next iteration. + */ + if (trailing) + break; } kfree(buffer); -- cgit v1.2.3 From ef7656e85f1a4400999625cd398b655517368e7e Mon Sep 17 00:00:00 2001 From: Zizhi Wo Date: Wed, 29 Jul 2026 10:12:04 +0800 Subject: fbdev: Fix out-of-bounds access when rotating console after font resize [BUG] Recently, we encountered a KASAN warning as follows: BUG: KASAN: slab-out-of-bounds in ccw_putcs+0x8bd/0xa80 Read of size 1 at addr ff11000110067100 by task bash/1209 CPU: 10 UID: 0 PID: 1209 Comm: bash Not tainted 7.2.0-rc3 #69 PREEMPT(full) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-4.fc41 04/01/2014 Call Trace: ... kasan_report+0xf0/0x120 ? ccw_putcs+0x8bd/0xa80 ccw_putcs+0x8bd/0xa80 ? __pfx_ccw_putcs+0x10/0x10 fbcon_putcs+0x338/0x410 ? __pfx_ccw_putcs+0x10/0x10 do_update_region+0x21d/0x450 invert_screen+0x29d/0x5e0 ? __kmalloc_noprof+0x493/0x640 ? vc_do_resize+0x17c/0xe50 clear_selection+0x4c/0x60 vc_do_resize+0xaee/0xe50 fbcon_modechanged+0x2bd/0x640 rotate_all_store+0x298/0x380 ... reproduce: 1) issue two ioctls: first a KDFONTOP ioctl with op.op = KD_FONT_OP_SET, op.width = 1 and op.height = 1, then a TIOCL_SETSEL ioctl 2) echo 2 > /sys/devices/virtual/graphics/fbcon/rotate_all 3) issue two ioctls: first a KDFONTOP ioctl with op.op = KD_FONT_OP_SET, op.width = 8 and op.height = 1, then a TIOCL_SETSEL ioctl 4) echo 3 > /sys/devices/virtual/graphics/fbcon/rotate_all [CAUSE] The root cause is that fbcon_modechanged() first sets the current rotate's corresponding ops. Subsequently, during vc_resize(), it may trigger clear_selection(), and in fbcon_putcs->ccw_putcs[rotate=3], this can result in an out-of-bounds access to "src". This happens because par->rotated.buf is reallocated in fbcon_rotate_font(): 1) When rotate=2, its size is (width + 7) / 8 * height 2) When rotate=3, its size is (height + 7) / 8 * width And the call to fbcon_rotate_font() occurs after clear_selection(). In other words, the fontbuffer is allocated using the size calculated from the previous rotation 2, but before reallocating it with the new size, con_putcs is already using the new rotation 3: rotate_all_store fbcon_rotate_all fbcon_set_all_vcs fbcon_modechanged set_blitting_type ... par->bitops = &ccw_fbcon_bitops vc_resize ... clear_selection highlight ... do_update_region fbcon_putcs ... image.dy = vyres - ((xx + count) * vc->vc_font.width) [1] // overflow! ccw_putcs_aligned // old buf size is still being used during the read! src = par->rotated.buf + (scr_readw(s--) & charmask) * cellsize fb_pad_aligned_buffer----[src KASAN!!!] [2] info->fbops->fb_imageblit(info, image) sys_imageblit fb_imageblit fb_address_forward // offset: image->dy * bits_per_line + image->dx * bpp unsigned int bits = (unsigned int)adr->bits + offset adr->address += (bits & ~(BITS_PER_LONG - 1u)) / BITS_PER_BYTE [3] fb_bitmap_imageblit ... fb_read_offset // page fault! [4] update_screen redraw_screen ... ccw_cursor soft_cursor memcpy(src, image->data, dsize)----[src KASAN again!!!] [5] fbcon_switch fbcon_rotate_font font_data_rotate dst = kmalloc_array(charcount, d_cellsize, GFP_KERNEL) // the new size is allocated only here! par->rotated.buf = buf [6] [FIX] A fairly obvious approach is to follow fbcon_switch(): in fbcon_modechanged(), call rotate_font() before vc_resize() so that a correctly sized buffer is allocated in time, as done in [6]. This fix is necessary, but it is not sufficient on its own. In [1] it causes an image.dy overflow (ccw_putcs: vyres = 768, image.dy = 4294967040), because vc_cols has not been updated in time at this point (it is likewise only updated after clear_selection()). This allows (xx + count) * width to exceed vyres, causing image.dy to overflow. Subsequently, address in [3] is incremented by an even larger amount, which triggers a page fault at [4]. Therefore, a second fix is required in combination with the first: move clear_selection() earlier, before set_blitting_type() in fbcon_set_all_vcs(), to prevent the out-of-bounds access. fbcon_rotate() has a similar problem, so add the same clear there. Since vc_is_sel() is not exported, the fbdev side is currently forced to call clear_selection() unconditionally, causing the global selection to be cleared prematurely. And this will not cause any other significant impact. Signed-off-by: Zizhi Wo Signed-off-by: Helge Deller --- drivers/video/fbdev/core/fbcon.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 9f5c4c101581..23b3c536d53d 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2641,9 +2641,31 @@ static void fbcon_modechanged(struct fb_info *info) fbcon_info_from_console(par->currcon) != info) return; + /* + * Clear the selection before switching bitops. Without this, the + * clear_selection() inside vc_resize() below repaints the highlighted + * cells through the new bitops while the console geometry(vc_rows/vc_cols) + * has not been updated to match, so the repaint is computed from a + * half-switched geometry and overflows the framebuffer address. + * Pre-clearing makes that repaint a no-op. + */ + clear_selection(); + p = &fb_display[vc->vc_num]; set_blitting_type(vc, info); + /* + * Rebuild par->rotated.buf for the new rotation now that bitops have + * switched. The new putcs/cursor ops read this buffer; if it is still + * sized for the old rotation, fbcon_putcs() and the cursor path reached + * via update_screen() below overflow it. Mirrors fbcon_switch(); fall + * back to unrotated rendering on allocation failure. + */ + if (par->bitops->rotate_font && par->bitops->rotate_font(info, vc)) { + par->rotate = FB_ROTATE_UR; + set_blitting_type(vc, info); + } + if (con_is_visible(vc)) { var_to_display(p, &info->var, info); cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres); @@ -2675,6 +2697,9 @@ static void fbcon_set_all_vcs(struct fb_info *info) if (!par || par->currcon < 0) return; + /* See the comment in fbcon_modechanged(). */ + clear_selection(); + for (i = first_fb_vc; i <= last_fb_vc; i++) { vc = vc_cons[i].d; if (!vc || vc->vc_mode != KD_TEXT || -- cgit v1.2.3 From e033cbf3975a8465f879ebd5989dc35b04423a4d Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Fri, 7 Aug 2026 22:19:56 -0400 Subject: fbdev: bitblit: bound-check glyph index in bit_cursor() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bit_cursor() fetches the glyph under the cursor with c = scr_readw(vc_pos); src = vc_font.data + ((c & charmask) * w * height); where charmask is 0x1ff when vc_hi_font_mask is set. The screen buffer value comes directly from scr_readw() and may be larger than the current font's glyph count. Syzkaller triggers this via vcs_write(). The Call Trace shows vcs_write() in vc_screen.c writing an arbitrary 16-bit value with writev() to /dev/vcsa, which vcs_write_buf() in vc_screen.c stores via vcs_scr_writew() without checking charcount. The stored value is later read in bit_cursor() in bitblit.c. When the font is changed from a font with 512 glyphs to a font with 256 glyphs, the screen buffer can retain characters with the high bit set from the previous mode, which could also produce the same out-of-bounds access. BUG: KASAN: global-out-of-bounds in soft_cursor+0x378/0x6bc drivers/video/fbdev/core/softcursor.c:70 Read of size 16 at addr ffff800086c57970 Call Trace: soft_cursor+0x378/0x6bc drivers/video/fbdev/core/softcursor.c:70 bit_cursor+0xa90/0x1108 drivers/video/fbdev/core/bitblit.c:365 fbcon_cursor+0x344/0x498 drivers/video/fbdev/core/fbcon.c:1427 hide_cursor+0xdc/0x2d0 drivers/tty/vt/vt.c:883 update_region+0x100/0x18c drivers/tty/vt/vt.c:669 vcs_write+0x8ec/0xaf0 drivers/tty/vt/vc_screen.c:685 bit_putcs_aligned() and bit_putcs_unaligned() already clamp the glyph index to vc_font.charcount. Apply the same clamp in bit_cursor() after extracting the attribute and masking, before indexing fontdata. The fix completes the bounds checking started in commit 18c4ef4e765a ("fbdev: bitblit: bound-check glyph index in bit_putcs*"), which missed the cursor path. This change should be safe because the clamp reuses the existing contract from fbcon: charcount is maintained under console_lock in con_font_set() and fbcon_font_set(), and hi_font_mask is cleared when switching from 512 to 256 glyphs. When stale screen data with high bits remains after a font switch, or when vcs_write() stores an arbitrary value, clamping the index to 0 prevents the out-of-bounds read without changing cursor semantics — the same fallback bit_putcs uses. Reported-by: syzbot+61b1db46218109869c14@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=61b1db46218109869c14 Link: https://lore.kernel.org/all/6a75205c.01d0871a.3a0d52.0032.GAE@google.com/ Fixes: 18c4ef4e765a ("fbdev: bitblit: bound-check glyph index in bit_putcs*") Cc: stable@vger.kernel.org Assisted-by: Hermes:muse-spark-1.2 syzkaller Signed-off-by: Rik van Riel Signed-off-by: Helge Deller --- drivers/video/fbdev/core/bitblit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c index 65681dcc5930..39f44258d793 100644 --- a/drivers/video/fbdev/core/bitblit.c +++ b/drivers/video/fbdev/core/bitblit.c @@ -273,9 +273,14 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable, if (!vc->vc_font.data) return; - c = scr_readw((u16 *) vc->vc_pos); + c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); - src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); + c &= charmask; + + /* Clamp to font size, same as bit_putcs_aligned() */ + if (c >= vc->vc_font.charcount) + c = 0; + src = vc->vc_font.data + (c * (w * vc->vc_font.height)); if (par->cursor_state.image.data != (const char *)src || par->cursor_reset) { -- cgit v1.2.3 From fd73b691702170d37d66f4b0278530cea8ed419a Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Wed, 5 Aug 2026 22:27:19 -0400 Subject: eventfs: Fix use-after-free in eventfs_remove_rec() eventfs_remove_rec() recursively removes the child at the current loop position. After the recursive call returns, list_for_each_entry() advances by reading list.next from the removed child. If free_ei() drops the final reference, release_ei() reuses the list/rcu union to queue an SRCU callback. The child may be freed before that read. The eventfs_mutex serializes list updates, but it does not keep the removed child alive or prevent the SRCU callback from running. Use list_for_each_entry_safe() to save the next sibling before recursively removing the current child. Cc: stable@vger.kernel.org Fixes: 43aa6f97c2d0 ("eventfs: Get rid of dentry pointers without refcounts") Link: https://patch.msgid.link/20260806022719.375354-1-shuangpeng.kernel@gmail.com Signed-off-by: Shuangpeng Bai Acked-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- fs/tracefs/event_inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 39c7a34531e8..93bc4f83b73e 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -822,7 +822,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry */ static void eventfs_remove_rec(struct eventfs_inode *ei, int level) { - struct eventfs_inode *ei_child; + struct eventfs_inode *ei_child, *tmp; /* * Check recursion depth. It should never be greater than 3: @@ -835,7 +835,7 @@ static void eventfs_remove_rec(struct eventfs_inode *ei, int level) return; /* search for nested folders or files */ - list_for_each_entry(ei_child, &ei->children, list) + list_for_each_entry_safe(ei_child, tmp, &ei->children, list) eventfs_remove_rec(ei_child, level + 1); list_del_rcu(&ei->list); -- cgit v1.2.3 From f0ece16ffca7384787b692431961ce202907acf5 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Sat, 8 Aug 2026 09:42:15 -0400 Subject: eventfs: Use children field for rcu head and add memory barriers When an eventfs inode is freed, it sets ei->is_freed and then uses its ei->list to add it to the srcu link list as the list field is a union with the rcu list head. As the ei->list is used to iterate over an SRCU protected list without taking the eventfs_mutex, there's nothing stopping the iteration over that list to see the ei->rcu instead of the ei->list and it will read a corrupt target. To fix this, change the union of the rcu list head with the children list. On freeing the eventfs inode, set the is_free and execute a smp_wmb() before adding the eventfs inode to the SRCU list. On iteration of the ei->children list, at the start, execute a smp_rmb() and then read the is_freed of the ei to see if the children list is still valid. If is_freed is set, then the ei_child read is not valid and the loop should exit immediately. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260808094215.4252430d@robin Fixes: 704f960dbee2f ("eventfs: Read ei->entries before ei->children in eventfs_iterate()") Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260806022719.375354-1-shuangpeng.kernel%40gmail.com Reviewed-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- fs/tracefs/event_inode.c | 24 ++++++++++++++++++++++++ fs/tracefs/internal.h | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 93bc4f83b73e..a52458435327 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -124,7 +124,17 @@ static inline void put_ei(struct eventfs_inode *ei) static inline void free_ei(struct eventfs_inode *ei) { if (ei) { + /* The ei should have no children if it is being freed. */ + WARN_ON_ONCE(!list_empty(&ei->children)); ei->is_freed = 1; + /* + * The SRCU iteration has a smp_rmb() to make sure it + * sees a child (that may have already been freed) + * before it reads is_free. If is_free is set, it must + * not use the child it acquired from ei->children, as + * the list may be used for SRCU. + */ + smp_wmb(); put_ei(ei); } } @@ -627,6 +637,20 @@ static int eventfs_iterate(struct file *file, struct dir_context *ctx) list_for_each_entry_srcu(ei_child, &ei->children, list, srcu_read_lock_held(&eventfs_srcu)) { + /* + * If the ei is being freed, then the ei->children may be + * being used as the rcu list, which means the next element + * may be garbage. The ei->is_free is set before switching + * the ei->children over to ei->rcu. The read memory barrier + * here makes sure the ei_child is read before is_free is + * updated. + * + * Matches the smp_wmb() in free_ei() + */ + smp_rmb(); + if (ei->is_freed) + return -EINVAL; + if (c > 0) { c--; continue; diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h index a4a7f8431aff..c61481d04c8e 100644 --- a/fs/tracefs/internal.h +++ b/fs/tracefs/internal.h @@ -46,11 +46,11 @@ struct eventfs_attr { * @ino: The saved inode number */ struct eventfs_inode { + struct list_head list; union { - struct list_head list; + struct list_head children; struct rcu_head rcu; }; - struct list_head children; const struct eventfs_entry *entries; const char *name; struct eventfs_attr *entry_attrs; -- cgit v1.2.3 From 63444b7617c09aeed36282e061c3f80818f2b600 Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Thu, 30 Jul 2026 23:04:08 +0800 Subject: ftrace: Protect direct_functions in ftrace_find_rec_direct Fix accessing the __rcu pointer direct_functions with RCU protection. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260730150411.88667-2-leon.hwang@linux.dev Fixes: d05cb470663a ("ftrace: Fix modification of direct_function hash while in use") Acked-by: Jiri Olsa Suggested-by: Steven Rostedt Signed-off-by: Leon Hwang Signed-off-by: Steven Rostedt --- kernel/trace/ftrace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 6c47a94f5924..c5d1d0d42ccc 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2645,7 +2645,8 @@ unsigned long ftrace_find_rec_direct(unsigned long ip) { struct ftrace_func_entry *entry; - entry = __ftrace_lookup_ip(direct_functions, ip); + guard(preempt_notrace)(); + entry = __ftrace_lookup_ip(rcu_dereference_sched(direct_functions), ip); if (!entry) return 0; -- cgit v1.2.3 From f26e5fa75fccd54bb95793c6519d405cf83233b2 Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Thu, 30 Jul 2026 23:04:09 +0800 Subject: ftrace: Protect direct_functions in update_ftrace_direct_del Fix accessing the __rcu pointer direct_functions with RCU protection. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260730150411.88667-3-leon.hwang@linux.dev Fixes: 8d2c1233f371 ("ftrace: Add update_ftrace_direct_del function") Acked-by: Jiri Olsa Signed-off-by: Leon Hwang Signed-off-by: Steven Rostedt --- kernel/trace/ftrace.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index c5d1d0d42ccc..9ea39110927f 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6512,6 +6512,7 @@ int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) struct ftrace_hash *new_direct_functions; struct ftrace_hash *new_filter_hash = NULL; struct ftrace_hash *old_filter_hash; + struct ftrace_hash *direct_hash; struct ftrace_func_entry *entry; struct ftrace_func_entry *del; unsigned long size; @@ -6523,11 +6524,13 @@ int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) return -EINVAL; if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) return -EINVAL; - if (direct_functions == EMPTY_HASH) - return -EINVAL; mutex_lock(&direct_mutex); + direct_hash = rcu_dereference_protected(direct_functions, lockdep_is_held(&direct_mutex)); + if (direct_hash == EMPTY_HASH) + goto out_unlock; + old_filter_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL; if (!hash_count(old_filter_hash)) @@ -6537,7 +6540,7 @@ int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) size = 1 << hash->size_bits; for (int i = 0; i < size; i++) { hlist_for_each_entry(entry, &hash->buckets[i], hlist) { - del = __ftrace_lookup_ip(direct_functions, entry->ip); + del = __ftrace_lookup_ip(direct_hash, entry->ip); if (!del || del->direct != entry->direct) goto out_unlock; } @@ -6548,7 +6551,7 @@ int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) if (!new_filter_hash) goto out_unlock; - new_direct_functions = hash_sub(direct_functions, hash); + new_direct_functions = hash_sub(direct_hash, hash); if (!new_direct_functions) goto out_unlock; @@ -6575,7 +6578,7 @@ int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash) /* free the new_direct_functions */ old_direct_functions = new_direct_functions; } else { - old_direct_functions = direct_functions; + old_direct_functions = direct_hash; rcu_assign_pointer(direct_functions, new_direct_functions); } -- cgit v1.2.3 From 092f8ec7dbdc71f5bde9bb0f8dead384d2115a44 Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Thu, 30 Jul 2026 23:04:10 +0800 Subject: ftrace: Protect direct_functions in update_ftrace_direct_mod Fix accessing the __rcu pointer direct_functions with RCU protection. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260730150411.88667-4-leon.hwang@linux.dev Fixes: e93672f770d7 ("ftrace: Add update_ftrace_direct_mod function") Acked-by: Jiri Olsa Signed-off-by: Leon Hwang Signed-off-by: Steven Rostedt --- kernel/trace/ftrace.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 9ea39110927f..414e425c2d80 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6617,6 +6617,7 @@ int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, b .func = ftrace_stub, .flags = FTRACE_OPS_FL_STUB, }; + struct ftrace_hash *direct_hash; struct ftrace_hash *orig_hash; unsigned long size, i; int err = -EINVAL; @@ -6627,8 +6628,6 @@ int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, b return -EINVAL; if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) return -EINVAL; - if (direct_functions == EMPTY_HASH) - return -EINVAL; /* * We can be called from within ops_func callback with direct_mutex @@ -6636,6 +6635,12 @@ int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, b */ if (do_direct_lock) mutex_lock(&direct_mutex); + else + lockdep_assert_held_once(&direct_mutex); + + direct_hash = rcu_dereference_protected(direct_functions, lockdep_is_held(&direct_mutex)); + if (direct_hash == EMPTY_HASH) + goto unlock; orig_hash = ops->func_hash ? ops->func_hash->filter_hash : NULL; if (!orig_hash) @@ -6667,7 +6672,7 @@ int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, b size = 1 << hash->size_bits; for (i = 0; i < size; i++) { hlist_for_each_entry(entry, &hash->buckets[i], hlist) { - tmp = __ftrace_lookup_ip(direct_functions, entry->ip); + tmp = __ftrace_lookup_ip(direct_hash, entry->ip); if (!tmp) continue; tmp->direct = entry->direct; -- cgit v1.2.3 From 48f2fd0d938651f600dce4a4f76f6e84730c5378 Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Thu, 30 Jul 2026 23:04:11 +0800 Subject: ftrace: Drop extra comma in trace_buffered_event_enable Drop the extra comma in "scoped_guard()" to cleanup the code. Link: https://patch.msgid.link/20260730150411.88667-5-leon.hwang@linux.dev Acked-by: Jiri Olsa Signed-off-by: Leon Hwang 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 01a5e87af299..395238b2b715 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1788,7 +1788,7 @@ void trace_buffered_event_enable(void) per_cpu(trace_buffered_event, cpu) = event; - scoped_guard(preempt,) { + scoped_guard(preempt) { if (cpu == smp_processor_id() && __this_cpu_read(trace_buffered_event) != per_cpu(trace_buffered_event, cpu)) -- cgit v1.2.3 From f8a80cfb68613fb7e6452b66447dbc63f435d140 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 8 Aug 2026 17:20:06 +0200 Subject: ALSA: usb: Fix UAF at delayed release of MIDI2 EPs The recent fix for UAF in ump_to_endpoint() caused another UAF because it tries to dereference the UMP endpoint object, but this might be executed at a delayed context where the endpoint has been already released. Add private_free to clear the associated data for avoiding the further dereference for delayed releases. Fixes: 4a05b2d1b464 ("ALSA: usb-audio: fix use-after-free in ump_to_endpoint()") Reported-by: syzbot+565b1138cfbe549d4422@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=565b1138cfbe549d4422 Cc: Link: https://patch.msgid.link/20260808152009.1947835-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/midi2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 83980fb83ac8..1dc78f536ef9 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -677,6 +677,14 @@ static int parse_midi_2_0_endpoints(struct snd_usb_midi2_interface *umidi) return 0; } +static void free_ump_private_data(struct snd_ump_endpoint *ump) +{ + struct snd_usb_midi2_ump *rmidi = ump->private_data; + + if (rmidi) + rmidi->ump = NULL; +} + static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi) { struct snd_usb_midi2_ump *rmidi; @@ -727,6 +735,7 @@ static int create_midi2_ump(struct snd_usb_midi2_interface *umidi, ump->private_data = rmidi; ump->ops = &snd_usb_midi_v2_ump_ops; + ump->private_free = free_ump_private_data; rmidi->eps[STR_IN] = ep_in; rmidi->eps[STR_OUT] = ep_out; -- cgit v1.2.3 From 885c22d259c8b245c479f3e19e9eeee54dee8b24 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 8 Aug 2026 17:22:54 +0200 Subject: ALSA: usb-audio: Fix mixer regression on SteelSeries Arctis Nova 5 The recent "sticky mixer" sanity check in USB-audio driver caused a regression on SteelSeries Arctis Nova 5 (1038:2232); because the firmware doesn't handle GET_CUR requests, some mixers are effectively disabled, leading to the too low / soft volumes: usb 5-1.1: 9:0: sticky mixer values (-19712/0/256 => 0), disabling usb 5-1.1: 10:0: sticky mixer values (-21248/0/256 => 0), disabling Restore the functionality by ignoring GET_CUR errors intentionally with MIXER_GET_CUR_BROKEN quirk. Fixes: 86aa1ea1f15c ("ALSA: usb-audio: Do not expose sticky mixers") Reported-by: Gert Burger Closes: https://lore.kernel.org/CAEQ1D3kdA3mkQx7ei9Kq0gwky0qroJqCLKrkvgfkqgTbeu086A@mail.gmail.com Link: https://bbs.archlinux.org/viewtopic.php?id=314220 Link: https://patch.msgid.link/20260808152258.1948767-1-tiwai@suse.de 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 90ca39dbed18..4d2957e62fbd 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2367,6 +2367,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x1038, 0x1294, /* SteelSeries Arctis Pro Wireless */ QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), + DEVICE_FLG(0x1038, 0x2232, /* SteelSeries Arctis Nova 5 */ + QUIRK_FLAG_MIXER_GET_CUR_BROKEN), DEVICE_FLG(0x1101, 0x0003, /* Audioengine D1 */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x12d1, 0x3a07, /* HUAWEI USB-C HEADSET */ -- cgit v1.2.3 From 5d588c684833e678a0008eb69c33190f01a65f4b Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Mon, 13 Apr 2026 01:07:38 +0000 Subject: riscv: lib: Fix ZBB strnlen reading past count boundary The ZBB-optimized strnlen loop loads one word ahead before checking the aligned boundary: REG_L t1, SZREG(t0) // load next word addi t0, t0, SZREG // advance orc.b t1, t1 bgeu t0, t4, 4f // boundary check AFTER load where t4 = (s + count) & -SZREG. When s is aligned and count is a multiple of SZREG, t4 equals s + count and the loop loads a full word starting at exactly s + count. If s + count falls on a page boundary with the next page unmapped, this faults. Fix by computing the aligned boundary from the last valid byte (s + count - 1) instead of s + count. This makes the loop stop at the word containing the last valid byte rather than potentially loading the word after it. The count == 0 case is already handled by the beqz early exit. Also add a pre-loop guard (bgeu t0, t4) for the case where all valid bytes fit within the first word. With the adjusted boundary, t4 can equal t0, and entering the loop with stale register state from the first-word processing would produce incorrect results. The final minu clamp ensures the result is still correct when the last loaded word extends past s + count - 1 within the same aligned word. Fixes: 5ba15d419fab ("riscv: lib: add strnlen() implementation") Signed-off-by: Michael Neuling Assisted-by: Claude Opus4.6 High Thinking Link: https://patch.msgid.link/20260413010738.1622423-1-mikey@neuling.org Signed-off-by: Paul Walmsley --- arch/riscv/lib/strnlen.S | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/riscv/lib/strnlen.S b/arch/riscv/lib/strnlen.S index 53afa7b5b314..a8911605c248 100644 --- a/arch/riscv/lib/strnlen.S +++ b/arch/riscv/lib/strnlen.S @@ -83,8 +83,13 @@ strnlen_zbb: sub t3, t3, t2 slli t2, t2, 3 - /* Aligned boundary. */ + /* + * Aligned boundary. Use the address of the last valid byte + * (s + count - 1) to avoid loading a word past the count + * boundary in the loop below. count == 0 is handled above. + */ add t4, a0, a1 + addi t4, t4, -1 andi t4, t4, -SZREG /* Get the first word. */ @@ -120,6 +125,9 @@ strnlen_zbb: bgtu t3, a0, 2f + /* All remaining bytes are in the first word, no loop needed. */ + bgeu t0, t4, 2f + /* Prepare for the word comparison loop. */ addi t2, t0, SZREG li t3, -1 -- cgit v1.2.3 From f27bdc43077e4fcb5557dfc315ee8d91e741f483 Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Mon, 3 Aug 2026 00:56:39 +0000 Subject: ring-buffer: Use current_context for safe per-CPU buffer swap The ring_buffer_swap_cpu() function currently checks the per-CPU committing counter to determine if a buffer is actively being written to before performing the swap. However, there exists a race window where this check can be bypassed: ring_buffer_lock_reserve cpu_buffer = buffer->buffers[cpu]; // cpu_buffer_a rb_reserve_next_event rb_start_commit // inc committing if (unlikely(READ_ONCE(cpu_buffer->buffer) != buffer)) {...} __rb_reserve_next rb_move_tail rb_end_commit(cpu_buffer); // dec committing => 0 /* interrupt hits here, successfully swaps! */ local_inc(&cpu_buffer->committing); ring_buffer_unlock_commit cpu_buffer = buffer->buffers[cpu]; // cpu_buffer_b rb_commit rb_end_commit RB_WARN_ON(cpu_buffer, !local_read(&cpu_buffer->committing)) // triggers warning The committing counter can temporarily drop to 0 during a single write operation (within rb_move_tail), creating a window where swap can succeed even though the write is still in progress. This leads to inconsistent buffer state and triggers the RB_WARN_ON in rb_commit(). Replace the committing counter check with current_context checks, which are set at the entry of ring_buffer_lock_reserve() and remain valid throughout the entire write operation, providing a reliable indicator of buffer busy state during swap. Cc: stable@vger.kernel.org Fixes: 4239c38fe0b3 ("ring-buffer: Process commits whenever moving to a new page.") Link: https://patch.msgid.link/20260803005640.2445666-2-wutengda@huaweicloud.com Signed-off-by: Tengda Wu Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 8e2485bb3aa8..58dc8995a88d 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -6852,7 +6852,7 @@ int ring_buffer_swap_cpu(struct trace_buffer *buffer_a, { struct ring_buffer_per_cpu *cpu_buffer_a; struct ring_buffer_per_cpu *cpu_buffer_b; - int ret = -EINVAL; + int ret = -EBUSY; if (!cpumask_test_cpu(cpu, buffer_a->cpumask) || !cpumask_test_cpu(cpu, buffer_b->cpumask)) @@ -6893,10 +6893,10 @@ int ring_buffer_swap_cpu(struct trace_buffer *buffer_a, atomic_inc(&cpu_buffer_a->record_disabled); atomic_inc(&cpu_buffer_b->record_disabled); - ret = -EBUSY; - if (local_read(&cpu_buffer_a->committing)) + /* Do not swap if either buffer is in the process of writing */ + if (cpu_buffer_a->current_context) goto out_dec; - if (local_read(&cpu_buffer_b->committing)) + if (cpu_buffer_b->current_context) goto out_dec; /* -- cgit v1.2.3 From 8b8292d6487c81bd57c2605a9b404b1cf8f1edfb Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 5 Aug 2026 21:56:46 -0700 Subject: ftrace: Fix off-by-one fentry site disable in ftrace_free_mem() When a module's init text is freed, do_init_module() calls ftrace_free_mem() with a half-open [start, end) range. However the ftrace_cmp_recs() comparator treats the upper bound as inclusive, as all its other users do, passing 'ip + size - 1'. So ftrace_free_mem() can delete a record sitting exactly at 'end', which is outside the freed range. For a kernel without CFI or IBT, the first record of a function is at the function start, which for the first function in a module is also the base of its text allocation. As the module allocator packs its regions, that address is often the 'end' passed by a neighboring module's do_init_module(), causing the first function's ftrace location to get disabled, preventing an attempt to livepatch it: livepatch: failed to find location for function 'pcspkr_probe' Convert the exclusive end to the inclusive 'end - 1' the comparator expects, and return early for an empty range to avoid the subtraction from underflowing when the init text size is zero. Cc: stable@vger.kernel.org Fixes: 42c269c88dc1 ("ftrace: Allow for function tracing to record init functions on boot up") Link: https://patch.msgid.link/1b5ccfa8095bdb1277f84af1c2c2e2205aca03ae.1785992188.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf Acked-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/ftrace.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 414e425c2d80..7c50f8ae5a0c 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -8305,7 +8305,8 @@ static void add_to_clear_hash_list(struct list_head *clear_list, void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr) { unsigned long start = (unsigned long)(start_ptr); - unsigned long end = (unsigned long)(end_ptr); + /* end is inclusive and end_ptr is exclusive */ + unsigned long end = (unsigned long)(end_ptr) - 1; struct ftrace_page **last_pg = &ftrace_pages_start; struct ftrace_page *tmp_page = NULL; struct ftrace_page *pg; @@ -8315,6 +8316,9 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr) struct ftrace_init_func *func, *func_next; LIST_HEAD(clear_hash); + if (start_ptr >= end_ptr) + return; + key.ip = start; key.flags = end; /* overload flags, as it is unsigned long */ -- cgit v1.2.3 From 7c727dfce6be04dd009b29091a4a17d952dbfe03 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 6 Aug 2026 22:13:01 +0100 Subject: ring-buffer: Prevent resizing of persistent ring buffer Dynamically resizing a persistent ring buffer is not possible. Disable the feature. Cc: stable@vger.kernel.org Fixes: be68d63a139b ("ring-buffer: Add ring_buffer_alloc_range()") Link: https://patch.msgid.link/20260806211306.3704194-2-vdonnefort@google.com Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 58dc8995a88d..09d502ef4c55 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2528,6 +2528,8 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) if (cpu_buffer->ring_meta->head_buffer) rb_meta_buffer_update(cpu_buffer, bpage); bpage->range = 1; + + atomic_inc(&cpu_buffer->resize_disabled); } else if (buffer->remote) { struct ring_buffer_desc *desc = ring_buffer_desc(buffer->remote->desc, cpu); -- cgit v1.2.3 From bf98d7b0d5a99991e47e66cee4eb1d3fa514be97 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 6 Aug 2026 22:13:02 +0100 Subject: ring-buffer: Prevent subbuf order change when resizing is disabled Because ring_buffer_subbuf_order_set() frees buffer pages, we can't allow it when resizing is disabled. A non-consuming reader is at risk of use-after-free (rb_advance_iter()). Return -EBUSY on resize_disabled, matching ring_buffer_resize() behaviour. Cc: stable@vger.kernel.org Fixes: f9b94daa542a ("ring-buffer: Set new size of the ring buffer sub page") Link: https://patch.msgid.link/20260806211306.3704194-3-vdonnefort@google.com Reported-by: syzbot+e0cc44465d6bae735679@syzkaller.appspotmail.com Signed-off-by: Vincent Donnefort 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 09d502ef4c55..6cbd80ccef37 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -7360,7 +7360,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) cpu_buffer = buffer->buffers[cpu]; - if (cpu_buffer->mapped) { + if (atomic_read(&cpu_buffer->resize_disabled)) { err = -EBUSY; goto error; } -- cgit v1.2.3 From 6d014e44b68ddd43f71288d2a4dbb1a259869149 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 6 Aug 2026 22:13:03 +0100 Subject: ring-buffer: Initialise reader page order in rb_allocate_cpu_buffer() In rb_allocate_cpu_buffer(), bpage->order was omitted, leaving it as 0. This is an issue for a ring-buffer with subbufs bigger than PAGE_SIZE if when freed: free_buffer_page() relies on this value. Align the value with the actual allocation size (buffer::subbuf_order). Cc: stable@vger.kernel.org Fixes: f9b94daa542a ("ring-buffer: Set new size of the ring buffer sub page") Link: https://patch.msgid.link/20260806211306.3704194-4-vdonnefort@google.com Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 6cbd80ccef37..760a00e8505c 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2510,6 +2510,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) bpage = alloc_cpu_page(cpu); if (!bpage) return NULL; + bpage->order = cpu_buffer->buffer->subbuf_order; rb_check_bpage(cpu_buffer, bpage); -- cgit v1.2.3 From 91542863abade2fd4f2b361991f5386ad9d19c8c Mon Sep 17 00:00:00 2001 From: Hui Su Date: Fri, 7 Aug 2026 23:41:46 +0800 Subject: ring-buffer: Fix crash passing ERR_PTR to kthread_stop() In test_ringbuffer()'s out_free cleanup loop, the check `!rb_threads[cpu]` only catches NULL entries and misses entries that hold an ERR_PTR. rb_threads[] is static, so unassigned slots are NULL. But when kthread_run_on_cpu() fails for a cpu, it stores ERR_PTR(-ENOMEM) (or -EINTR) in rb_threads[cpu] before the creation loop jumps to out_free. That entry is non-NULL, so the old `!ptr` check does not break, and the cleanup proceeds to call kthread_stop() on the ERR_PTR. kthread_stop() then dereferences the bogus pointer, crashing the kernel during the late_initcall self-test. crash logs: BUG: kernel NULL pointer dereference, address: 000000000000001c Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 1 Comm: swapper/0 Not tainted 7.2.0-rc6-dirty #7 PREEMPT(lazy) RIP: 0010:kthread_stop+0x2e/0x220 RBX: fffffffffffffff4 CR2: 000000000000001c Call Trace: test_ringbuffer+0x1ec/0x650 do_one_initcall+0x6c/0x2c0 kernel_init_freeable+0x21d/0x420 kernel_init+0x15/0x1c0 ret_from_fork+0x21b/0x320 Kernel panic - not syncing: Fatal exception Cc: stable@vger.kernel.org Fixes: 64ed3a049e3e ("ring-buffer: make use of the helper function kthread_run_on_cpu()") Link: https://patch.msgid.link/20260807154145.2846521-2-sh_def@163.com Signed-off-by: Hui Su Reviewed-by: Vincent Donnefort Acked-by: Masami Hiramatsu (Google) 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 760a00e8505c..2667992f0aa2 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -8217,7 +8217,7 @@ static __init int test_ringbuffer(void) out_free: for_each_online_cpu(cpu) { - if (!rb_threads[cpu]) + if (IS_ERR_OR_NULL(rb_threads[cpu])) break; kthread_stop(rb_threads[cpu]); } -- cgit v1.2.3 From e9714db8041763f59dde152c812b96b3de05c6d9 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Fri, 7 Aug 2026 09:55:43 +0200 Subject: ovpn: run deferred work on a module-owned workqueue ovpn queues several work items whose callbacks execute module text. These works currently run on the global system workqueues, so module exit has no driver-owned drain point that guarantees the callbacks have fully returned before the module text can be freed. Object references protect the objects used by the callbacks, but they do not prove that a workqueue function has returned. In particular, a worker can drop the final reference that unblocks device teardown while it is still executing ovpn code. Add a module-owned workqueue and queue all ovpn work items on it. During module exit, unregister rtnl and netlink first, flush the workqueue so ordinary ovpn workers finish, run the final RCU barrier, and destroy the workqueue last. This keeps the workqueue available for cleanup work queued from RCU callbacks, while ensuring no ovpn work item can outlive the module text. The per-device delayed keepalive work remains explicitly disabled during netdev teardown (disable_delayed_work_sync in ndo_uninit), since flush_workqueue does not flush delayed work that is still only pending on its timer. Fixes: 3ecfd9349f40 ("ovpn: implement keepalive mechanism") Fixes: 11851cbd60ea ("ovpn: implement TCP transport") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 19 ++++++++++++++++++- drivers/net/ovpn/ovpnpriv.h | 4 ++++ drivers/net/ovpn/peer.c | 8 ++++---- drivers/net/ovpn/tcp.c | 9 ++++----- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 168cfe9b59a9..0708249e9607 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,9 @@ #include "tcp.h" #include "udp.h" +/* module-owned workqueue on which all ovpn-specific work is queued */ +struct workqueue_struct *ovpn_wq; + static void ovpn_priv_free(struct net_device *net) { struct ovpn_priv *ovpn = netdev_priv(net); @@ -264,10 +268,16 @@ static int __init ovpn_init(void) ovpn_tcp_init(); + ovpn_wq = alloc_workqueue("ovpn", WQ_PERCPU, 0); + if (!ovpn_wq) { + pr_err("ovpn: cannot allocate workqueue\n"); + return -ENOMEM; + } + err = rtnl_link_register(&ovpn_link_ops); if (err) { pr_err("ovpn: can't register rtnl link ops: %d\n", err); - return err; + goto destroy_wq; } err = ovpn_nl_register(); @@ -280,6 +290,9 @@ static int __init ovpn_init(void) unreg_rtnl: rtnl_link_unregister(&ovpn_link_ops); +destroy_wq: + destroy_workqueue(ovpn_wq); + ovpn_wq = NULL; return err; } @@ -288,7 +301,11 @@ static __exit void ovpn_cleanup(void) ovpn_nl_unregister(); rtnl_link_unregister(&ovpn_link_ops); + flush_workqueue(ovpn_wq); rcu_barrier(); + + destroy_workqueue(ovpn_wq); + ovpn_wq = NULL; } module_init(ovpn_init); diff --git a/drivers/net/ovpn/ovpnpriv.h b/drivers/net/ovpn/ovpnpriv.h index 5898f6adada7..84499140e4bd 100644 --- a/drivers/net/ovpn/ovpnpriv.h +++ b/drivers/net/ovpn/ovpnpriv.h @@ -15,6 +15,10 @@ #include #include +struct workqueue_struct; + +extern struct workqueue_struct *ovpn_wq; + /** * struct ovpn_peer_collection - container of peers for MultiPeer mode * @by_id: table of peers index by ID diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index b0519f9840d8..c95656ca7c35 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -62,7 +62,7 @@ void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout) /* now that interval and timeout have been changed, kick * off the worker so that the next delay can be recomputed */ - mod_delayed_work(system_percpu_wq, &peer->ovpn->keepalive_work, 0); + mod_delayed_work(ovpn_wq, &peer->ovpn->keepalive_work, 0); } /** @@ -1371,7 +1371,7 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer, peer->id); if (WARN_ON(!ovpn_peer_hold(peer))) return 0; - if (!schedule_work(&peer->keepalive_work)) + if (!queue_work(ovpn_wq, &peer->keepalive_work)) ovpn_peer_put(peer); } @@ -1463,8 +1463,8 @@ void ovpn_peer_keepalive_work(struct work_struct *work) netdev_dbg(ovpn->dev, "scheduling keepalive work: now=%llu next_run=%llu delta=%llu\n", next_run, now, next_run - now); - schedule_delayed_work(&ovpn->keepalive_work, - (next_run - now) * HZ); + queue_delayed_work(ovpn_wq, &ovpn->keepalive_work, + (next_run - now) * HZ); } unlock_ovpn(ovpn, &release_list); } diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 0af14055c39a..8fe8a8e750a4 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -151,7 +151,7 @@ err: /* take reference for deferred peer deletion. should never fail */ if (WARN_ON(!ovpn_peer_hold(peer))) goto err_nopeer; - if (!schedule_work(&peer->tcp.defer_del_work)) + if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work)) ovpn_peer_put(peer); ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); err_nopeer: @@ -284,13 +284,12 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk) * stream therefore we abort the connection */ ovpn_peer_hold(peer); - if (!schedule_work(&peer->tcp.defer_del_work)) + if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work)) ovpn_peer_put(peer); /* we bail out immediately and keep tx_in_progress set * to true. This way we prevent more TX attempts - * which would lead to more invocations of - * schedule_work() + * which would lead to more invocations of queue_work() */ return; } @@ -487,7 +486,7 @@ static void ovpn_tcp_write_space(struct sock *sk) rcu_read_lock(); sock = rcu_dereference_sk_user_data(sk); if (likely(sock && sock->peer)) { - schedule_work(&sock->tcp_tx_work); + queue_work(ovpn_wq, &sock->tcp_tx_work); sock->peer->tcp.sk_cb.sk_write_space(sk); } rcu_read_unlock(); -- cgit v1.2.3 From 2da3dfa1ddfe55a065f484750c83660e3bd4ac00 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 29 Jul 2026 12:21:46 +0200 Subject: ovpn: defer key slot crypto freeing to workqueue Key slots are released through a kref and the existing release path frees the AEAD transforms from an RCU callback. That is not safe for all crypto implementations: crypto_free_aead can sleep, for example when an async or hardware implementation has teardown work to complete. Use queue_rcu_work for key-slot release. This keeps the RCU grace period needed by lockless key-slot readers, but runs the actual crypto teardown from workqueue context where sleeping is allowed. Once the rcu_work callback runs, pre-existing RCU readers are gone, and the final kref put already proves that no transform user remains, so the worker can release the AEAD transforms and free the slot directly. The previous patch drains ovpn_wq during module exit, so queued key-slot teardown work cannot outlive module text. Fixes: 8534731dbf2d ("ovpn: implement packet processing") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/crypto.c | 10 +--------- drivers/net/ovpn/crypto.h | 4 +++- drivers/net/ovpn/crypto_aead.c | 19 ++++++++++++++----- drivers/net/ovpn/crypto_aead.h | 1 - 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c index 2e95f29514fc..7e545428900a 100644 --- a/drivers/net/ovpn/crypto.c +++ b/drivers/net/ovpn/crypto.c @@ -18,20 +18,12 @@ #include "crypto_aead.h" #include "crypto.h" -static void ovpn_ks_destroy_rcu(struct rcu_head *head) -{ - struct ovpn_crypto_key_slot *ks; - - ks = container_of(head, struct ovpn_crypto_key_slot, rcu); - ovpn_aead_crypto_key_slot_destroy(ks); -} - void ovpn_crypto_key_slot_release(struct kref *kref) { struct ovpn_crypto_key_slot *ks; ks = container_of(kref, struct ovpn_crypto_key_slot, refcount); - call_rcu(&ks->rcu, ovpn_ks_destroy_rcu); + queue_rcu_work(ovpn_wq, &ks->free_work); } /* can only be invoked when all peer references have been dropped (i.e. RCU diff --git a/drivers/net/ovpn/crypto.h b/drivers/net/ovpn/crypto.h index 0e284fec3a75..e3feb16d5498 100644 --- a/drivers/net/ovpn/crypto.h +++ b/drivers/net/ovpn/crypto.h @@ -10,6 +10,8 @@ #ifndef _NET_OVPN_OVPNCRYPTO_H_ #define _NET_OVPN_OVPNCRYPTO_H_ +#include + #include "pktid.h" #include "proto.h" @@ -45,8 +47,8 @@ struct ovpn_crypto_key_slot { struct ovpn_pktid_recv pid_recv ____cacheline_aligned_in_smp; struct ovpn_pktid_xmit pid_xmit ____cacheline_aligned_in_smp; + struct rcu_work free_work; struct kref refcount; - struct rcu_head rcu; }; struct ovpn_crypto_state { diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c index 8f07c418622b..74eaf6fac2f5 100644 --- a/drivers/net/ovpn/crypto_aead.c +++ b/drivers/net/ovpn/crypto_aead.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -380,13 +381,19 @@ error: return ERR_PTR(ret); } -void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks) +static void ovpn_aead_crypto_key_slot_free(struct ovpn_crypto_key_slot *ks) { - if (!ks) - return; - crypto_free_aead(ks->encrypt); crypto_free_aead(ks->decrypt); +} + +static void ovpn_aead_crypto_key_slot_free_work(struct work_struct *work) +{ + struct ovpn_crypto_key_slot *ks; + + ks = container_of(to_rcu_work(work), struct ovpn_crypto_key_slot, + free_work); + ovpn_aead_crypto_key_slot_free(ks); kfree(ks); } @@ -420,6 +427,7 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc) ks->encrypt = NULL; ks->decrypt = NULL; + INIT_RCU_WORK(&ks->free_work, ovpn_aead_crypto_key_slot_free_work); kref_init(&ks->refcount); ks->key_id = kc->key_id; @@ -453,7 +461,8 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc) return ks; destroy_ks: - ovpn_aead_crypto_key_slot_destroy(ks); + ovpn_aead_crypto_key_slot_free(ks); + kfree(ks); return ERR_PTR(ret); } diff --git a/drivers/net/ovpn/crypto_aead.h b/drivers/net/ovpn/crypto_aead.h index 65a2ff307898..fae3b585a43b 100644 --- a/drivers/net/ovpn/crypto_aead.h +++ b/drivers/net/ovpn/crypto_aead.h @@ -22,7 +22,6 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks, struct ovpn_crypto_key_slot * ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc); -void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks); enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks); -- cgit v1.2.3 From db2ddb87143519e20a95aa36c60b36107b736a58 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 9 Aug 2026 14:54:50 -0700 Subject: Linux 7.2-rc7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 902f3f3d54b7..0e6fc0803775 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = -rc7 NAME = Baby Opossum Posse # *DOCUMENTATION* -- cgit v1.2.3 From 63981fc786daaa626cb14d9be1406f674d79f98f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 2 Aug 2026 20:00:43 +0200 Subject: ovl: don't warn when the mount is completed from another user namespace fsopen() records the caller's user namespace in fc->user_ns and hands back an ordinary file descriptor. Nothing ties the task that calls fsconfig(FSCONFIG_CMD_CREATE) to the task that created the context. The fd is inherited across fork() and exec() and it can be passed over a unix socket. Completing a context from another user namespace is allowed on purpose. vfs_cmd_create() authorizes the create with mount_capable(), which for FS_USERNS_MOUNT checks ns_capable(fc->user_ns, CAP_SYS_ADMIN), and that succeeds for a task holding CAP_SYS_ADMIN in an ancestor of fc->user_ns. So an unprivileged task can reach the WARN_ON() in ovl_fill_super(): create a user and a mount namespace in a child, call fsopen("overlay") there, send the fscontext fd to the parent and let the parent issue FSCONFIG_CMD_CREATE. Both namespaces come from a plain unshare(1) and no capability is needed anywhere: WARNING: fs/overlayfs/super.c:1551 at ovl_fill_super+0x7b9/0x1e20 [overlay] CPU: 3 UID: 1000 PID: 3243376 Comm: fswarn Call Trace: get_tree_nodev+0x71/0xa0 ovl_get_tree+0x15/0x20 [overlay] vfs_get_tree+0x2a/0x100 vfs_cmd_create+0x60/0xf0 __do_sys_fsconfig+0x4b2/0x500 The child needs the mount namespace because fsopen() itself gates on may_mount(), which asks for CAP_SYS_ADMIN in the user namespace owning the caller's mount namespace. fsconfig() doesn't repeat that check. It is a WARN_ON() and not a WARN_ON_ONCE(), so the condition can be raised in a loop to taint the kernel and flood the log, and it panics a kernel booted with panic_on_warn. Keep refusing the mount and stop warning about it. ovl_parse_param() already spells a user namespace check this way for Opt_override_creds. Fixes: 1784fbc2ed9c ("ovl: port to new mount api") Cc: stable@vger.kernel.org # v6.5+ Link: https://patch.msgid.link/20260802-work-fill_super-warn-v1-1-4e987911a39a@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/overlayfs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 60f0b7ceef0a..60b808b85fc4 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1544,7 +1544,8 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) int err; err = -EIO; - if (WARN_ON(fc->user_ns != current_user_ns())) + /* The fscontext fd may have been passed to another user namespace. */ + if (fc->user_ns != current_user_ns()) goto out_err; ovl_set_d_op(sb); -- cgit v1.2.3 From 79fdf39f1a31f88cb3833b6f8091fbf6acdca2c6 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 2 Aug 2026 20:00:44 +0200 Subject: binfmt_misc: don't warn when the mount is completed from another user namespace fsopen() records the caller's user namespace in fc->user_ns and hands back an ordinary file descriptor. Nothing ties the task that calls fsconfig(FSCONFIG_CMD_CREATE) to the task that created the context. The fd is inherited across fork() and exec() and it can be passed over a unix socket. Completing a context from another user namespace is allowed on purpose. vfs_cmd_create() authorizes the create with mount_capable(), which for FS_USERNS_MOUNT checks ns_capable(fc->user_ns, CAP_SYS_ADMIN), and that succeeds for a task holding CAP_SYS_ADMIN in an ancestor of fc->user_ns. So an unprivileged task can reach the WARN_ON() in bm_fill_super(): create a user and a mount namespace in a child, call fsopen("binfmt_misc") there, send the fscontext fd to the parent and let the parent issue FSCONFIG_CMD_CREATE. Both namespaces come from a plain unshare(1) and no capability is needed anywhere: WARNING: fs/binfmt_misc.c:938 at bm_fill_super+0xa2/0xc0 [binfmt_misc] CPU: 15 UID: 1000 PID: 3243382 Comm: fswarn Call Trace: get_tree_keyed+0x7d/0xb0 bm_get_tree+0x34/0x90 [binfmt_misc] vfs_get_tree+0x2a/0x100 vfs_cmd_create+0x60/0xf0 __do_sys_fsconfig+0x4b2/0x500 The child needs the mount namespace because fsopen() itself gates on may_mount(), which asks for CAP_SYS_ADMIN in the user namespace owning the caller's mount namespace. fsconfig() doesn't repeat that check. It is a WARN_ON() and not a WARN_ON_ONCE(), so the condition can be raised in a loop to taint the kernel and flood the log, and it panics a kernel booted with panic_on_warn. Keep refusing the mount and stop warning about it. Nothing in bm_fill_super() depends on the two namespaces matching, it derives everything from sb->s_user_ns. Fixes: 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts") Cc: stable@vger.kernel.org # v6.7+ Link: https://patch.msgid.link/20260802-work-fill_super-warn-v1-2-4e987911a39a@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index c97f10b48b5b..613dd28e3f1a 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -937,7 +937,8 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc) /* last one */ {""} }; - if (WARN_ON(user_ns != current_user_ns())) + /* The fscontext fd may have been passed to another user namespace. */ + if (user_ns != current_user_ns()) return -EINVAL; /* Never exec off this instance and never let anything stack on it. */ -- cgit v1.2.3 From d64ba78b749ad6bae0e9a6f40614eda1c7db2f04 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 2 Aug 2026 20:00:45 +0200 Subject: selftests/filesystems: test completing a context from another user namespace fsopen() records the caller's user namespace in fc->user_ns and hands back an ordinary file descriptor, so the task that issues FSCONFIG_CMD_CREATE need not be the one that created the context. mount_capable() authorizes that for a caller holding CAP_SYS_ADMIN in an ancestor of fc->user_ns, which any unprivileged user has over a user namespace it just created. binfmt_misc and overlayfs used to WARN_ON() the mismatch. Add a test for both. Also cover the handover within one user namespace. That is a supported thing to do and has to keep working. Link: https://patch.msgid.link/20260802-work-fill_super-warn-v1-3-4e987911a39a@kernel.org Signed-off-by: Christian Brauner (Amutable) --- tools/testing/selftests/Makefile | 1 + .../selftests/filesystems/fscontext_ns/Makefile | 10 + .../filesystems/fscontext_ns/fscontext_ns_test.c | 239 +++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 tools/testing/selftests/filesystems/fscontext_ns/Makefile create mode 100644 tools/testing/selftests/filesystems/fscontext_ns/fscontext_ns_test.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 8d4db2241cc2..b622052ec3e9 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -42,6 +42,7 @@ TARGETS += filesystems/fuse TARGETS += filesystems/move_mount TARGETS += filesystems/empty_mntns TARGETS += filesystems/fsmount_ns +TARGETS += filesystems/fscontext_ns TARGETS += firmware TARGETS += fpu TARGETS += ftrace diff --git a/tools/testing/selftests/filesystems/fscontext_ns/Makefile b/tools/testing/selftests/filesystems/fscontext_ns/Makefile new file mode 100644 index 000000000000..7e3506294757 --- /dev/null +++ b/tools/testing/selftests/filesystems/fscontext_ns/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_GEN_PROGS := fscontext_ns_test + +CFLAGS += -Wall -O0 -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES) +LDLIBS := -lcap + +include ../../lib.mk + +$(OUTPUT)/fscontext_ns_test: fscontext_ns_test.c ../utils.c + $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) diff --git a/tools/testing/selftests/filesystems/fscontext_ns/fscontext_ns_test.c b/tools/testing/selftests/filesystems/fscontext_ns/fscontext_ns_test.c new file mode 100644 index 000000000000..0f30a3e6e197 --- /dev/null +++ b/tools/testing/selftests/filesystems/fscontext_ns/fscontext_ns_test.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Christian Brauner + * + * Test that completing a filesystem context from another user namespace + * doesn't warn. + * + * fsopen() records the caller's user namespace in fc->user_ns and hands + * back an ordinary file descriptor. The task that issues + * FSCONFIG_CMD_CREATE need not be the one that created the context: the fd + * is inherited across fork() and exec() and it can be passed over a unix + * socket. vfs_cmd_create() authorizes the create with mount_capable(), + * which for FS_USERNS_MOUNT checks ns_capable(fc->user_ns, CAP_SYS_ADMIN), + * and that succeeds for a task holding CAP_SYS_ADMIN in an ancestor of + * fc->user_ns. + * + * binfmt_misc and overlayfs used to WARN_ON() that mismatch, which let an + * unprivileged user taint the kernel, flood the log and panic a kernel + * booted with panic_on_warn. The mount must still be refused, but it must + * not warn. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../wrappers.h" +#include "../utils.h" +#include "../../kselftest_harness.h" + +#ifndef FSCONFIG_CMD_CREATE +#define FSCONFIG_CMD_CREATE 6 +#endif + +/* TAINT_WARN, i.e. bit 9 of /proc/sys/kernel/tainted. */ +#define TAINT_WARN_BIT 9 + +static bool taint_warn_set(void) +{ + unsigned long taint = 0; + FILE *f; + + f = fopen("/proc/sys/kernel/tainted", "r"); + if (!f) + return false; + if (fscanf(f, "%lu", &taint) != 1) + taint = 0; + fclose(f); + + return taint & (1UL << TAINT_WARN_BIT); +} + +static int send_fd(int sock, int fd) +{ + char cmsgbuf[CMSG_SPACE(sizeof(int))] = {}; + char b[1] = { 'x' }; + struct iovec iov = { .iov_base = b, .iov_len = sizeof(b) }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + }; + struct cmsghdr *cmsg; + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + return sendmsg(sock, &msg, 0) < 0 ? -1 : 0; +} + +static int recv_fd(int sock) +{ + char cmsgbuf[CMSG_SPACE(sizeof(int))] = {}; + char b[1]; + struct iovec iov = { .iov_base = b, .iov_len = sizeof(b) }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + }; + struct cmsghdr *cmsg; + int fd = -1; + + if (recvmsg(sock, &msg, 0) <= 0) + return -1; + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS) + return -1; + memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); + + return fd; +} + +/* + * Create a context for @fsname in a child and complete it here. With @nest + * the child first creates its own user namespace, so that the context is + * created in a descendant of the namespace completing it. The child needs a + * mount namespace of its own as well: fsopen() gates on may_mount(), which + * asks for CAP_SYS_ADMIN in the user namespace owning the caller's mount + * namespace. + * + * Returns the result of FSCONFIG_CMD_CREATE with errno set, or -ENODATA if + * the child could not create the context at all. + */ +static int create_from_child(const char *fsname, bool nest) +{ + int sock[2], fd, ret, status; + pid_t pid; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock)) + return -ENODATA; + + pid = fork(); + if (pid < 0) { + close(sock[0]); + close(sock[1]); + return -ENODATA; + } + + if (pid == 0) { + close(sock[0]); + + if (nest && unshare(CLONE_NEWUSER | CLONE_NEWNS)) + _exit(1); + + fd = sys_fsopen(fsname, 0); + if (fd < 0) + _exit(1); + if (send_fd(sock[1], fd)) + _exit(1); + _exit(0); + } + + close(sock[1]); + fd = recv_fd(sock[0]); + close(sock[0]); + wait_for_pid(pid); + waitpid(pid, &status, WNOHANG); + + if (fd < 0) + return -ENODATA; + + errno = 0; + ret = sys_fsconfig(fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + status = errno; + close(fd); + errno = status; + + return ret; +} + +FIXTURE(fscontext_ns) { + bool warn_before; +}; + +FIXTURE_SETUP(fscontext_ns) +{ + self->warn_before = taint_warn_set(); + + if (setup_userns() != 0) + SKIP(return, "setup_userns failed"); +} + +FIXTURE_TEARDOWN(fscontext_ns) +{ +} + +/* + * The condition the kernel used to WARN about. It has to be refused, and it + * has to be refused quietly: an unprivileged task reaches this. + */ +FIXTURE_VARIANT(fscontext_ns) { + const char *fsname; + int expected_errno; +}; + +FIXTURE_VARIANT_ADD(fscontext_ns, binfmt_misc) { + .fsname = "binfmt_misc", + .expected_errno = EINVAL, +}; + +FIXTURE_VARIANT_ADD(fscontext_ns, overlay) { + .fsname = "overlay", + .expected_errno = EIO, +}; + +TEST_F(fscontext_ns, create_from_descendant_userns) +{ + int ret; + + ret = create_from_child(variant->fsname, true); + if (ret == -ENODATA) + SKIP(return, "%s unavailable", variant->fsname); + + ASSERT_EQ(-1, ret); + ASSERT_EQ(variant->expected_errno, errno); + + /* + * Only meaningful if nothing had warned before us. Note that an + * unrelated warning racing this test would look like a failure. + */ + if (self->warn_before) + TH_LOG("TAINT_WARN already set, not checking for a new warning"); + else + ASSERT_FALSE(taint_warn_set()); +} + +/* + * The same handover within one user namespace is a supported thing to do and + * has to keep working. binfmt_misc takes no options, so the create succeeds + * outright and this also shows the test really drives the create path. + */ +TEST(create_from_same_userns) +{ + int ret; + + if (setup_userns() != 0) + SKIP(return, "setup_userns failed"); + + ret = create_from_child("binfmt_misc", false); + if (ret == -ENODATA) + SKIP(return, "binfmt_misc unavailable"); + + ASSERT_EQ(0, ret); +} + +TEST_HARNESS_MAIN -- cgit v1.2.3 From ada54c2ba652348c590d1ace6a2f4ff77cbbf809 Mon Sep 17 00:00:00 2001 From: Niels Pressel Date: Sun, 2 Aug 2026 14:44:23 +0200 Subject: rseq: Prevent hard lockup on granted time slice extension __exit_to_user_mode_loop() invokes rseq_grant_timeslice_extension() with interrupts enabled. If the extension is granted it invokes hrtimer_rearm_deferred_tif() to ensure that a pending deferred hrtimer rearm is handled before exiting to user space. Though this invokes __hrtimer_rearm_deferred() which expects to be invoked with interrupts disabled as it takes hrtimer_cpu_base::lock with raw_spin_lock(). That's a livelock waiting to happen and caught by lockdep: WARNING: ./include/linux/hrtimer_rearm.h:17 at irqentry_exit, CPU#1: slice_test WARNING: inconsistent lock state inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. Prevent this by disabling interrupts around the invocation of hrtimer_rearm_deferred_tif() in rseq_grant_timeslice_extension(). [ tglx: Massaged change log ] Fixes: 15dd3a948855 ("hrtimer: Push reprogramming timers into the interrupt return path") Signed-off-by: Niels Pressel Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260802124423.51616-1-npressel@ethz.ch --- include/linux/rseq_entry.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/rseq_entry.h b/include/linux/rseq_entry.h index ed9da6e41a2a..31ce349ed42c 100644 --- a/include/linux/rseq_entry.h +++ b/include/linux/rseq_entry.h @@ -233,6 +233,7 @@ efault: static __always_inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { if (unlikely(__rseq_grant_slice_extension(ti_work & mask))) { + guard(irq)(); hrtimer_rearm_deferred_tif(ti_work); return true; } -- cgit v1.2.3 From 9ecf8ba763d0ffe0673538eb4bf7806f20455d19 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Thu, 23 Jul 2026 15:06:52 -0700 Subject: drm/connector/hdmi: Fix out of bounds memory read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A helper function was copying a given audio infoframe into the connector's copy but using the size of the destination (a generic target, sized to accept many different data blocks) not the source (a very specific type of data block). Thus, it was copying 60 bytes of data from a 28 byte allocation. Fix that by using the source size instead, together with a build bug on the source size actually being smaller than the destination. I hit this running KUnit tests under KASAN (while debugging something else entirely). In the real world, it seems unlikely to cause an actual problem. It is a read not a write so it can't corrupt any memory. However, it could potentially fall off the end of a page and cause an accvio bug. Fixes: f378b77227bc ("drm/connector: hdmi: Add Infoframes generation") Cc: Ville Syrjälä Cc: Dmitry Baryshkov Cc: Maxime Ripard Cc: Maarten Lankhorst Cc: Thomas Zimmermann Cc: David Airlie Cc: Simona Vetter Cc: Dmitry Baryshkov Cc: Daniel Stone Cc: Nicolas Frattaroli Cc: Jani Nikula Cc: José Expósito Cc: Laurent Pinchart Cc: dri-devel@lists.freedesktop.org Cc: stable@vger.kernel.org # v6.11+ Signed-off-by: John Harrison Link: https://patch.msgid.link/20260723220652.533345-1-John.Harrison@Igalia.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/display/drm_hdmi_state_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index 4867edbf2622..cae0d85fb440 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -1096,7 +1096,8 @@ drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *co mutex_lock(&connector->hdmi.infoframes.lock); - memcpy(&infoframe->data, frame, sizeof(infoframe->data)); + BUILD_BUG_ON(sizeof(*frame) > sizeof(infoframe->data)); + memcpy(&infoframe->data, frame, sizeof(*frame)); infoframe->set = true; ret = write_infoframe(connector, &funcs->audio, "Audio", infoframe); -- cgit v1.2.3 From 600411ea1f2443fdf5b1af9b6480f616d7aff9d0 Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Fri, 31 Jul 2026 11:27:47 +0800 Subject: gpio: ml-ioh: use raw_spinlock_t for the register lock ioh_irq_type() is registered as the irq_chip .irq_set_type callback and takes chip->spinlock with spin_lock_irqsave(). This callback is reached from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is an rtmutex-backed sleeping lock, so acquiring it there is invalid. ioh_irq_enable() and ioh_irq_disable() take the same lock from the .irq_enable/.irq_disable callbacks, which are likewise invoked with desc->lock held. Convert the register lock to raw_spinlock_t. The same lock also serializes the GPIO direction/value callbacks and the suspend/resume register save/restore, and those critical sections only perform short sequences of MMIO register accesses (ioread32()/iowrite32()); the .irq_set_type callback additionally emits a dev_warn() on an unsupported type. None of these are sleepable operations, so keeping this register lock non-sleeping is appropriate for the irqchip callbacks and does not change the GPIO-side locking contract. This is the same fix as commit a02b8950d619 ("gpio: pch: use raw_spinlock_t for the register lock"); this driver shares the same structure as gpio-pch. Fixes: 54be566317b6 ("gpio-ml-ioh: Support interrupt function") Cc: stable@vger.kernel.org Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260731032747.2987292-1-junjie.cao@intel.com Signed-off-by: Junjie Cao --- drivers/gpio/gpio-ml-ioh.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 6576e5dcb0ee..0a9d34b7636e 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -84,7 +84,7 @@ struct ioh_gpio { u32 gpio_use_sel; int ch; int irq_base; - spinlock_t spinlock; + raw_spinlock_t spinlock; }; static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; @@ -95,7 +95,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) struct ioh_gpio *chip = gpiochip_get_data(gpio); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); reg_val = ioread32(&chip->reg->regs[chip->ch].po); if (val) reg_val |= BIT(nr); @@ -103,7 +103,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -123,7 +123,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, u32 reg_val; unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm); pm &= BIT(num_ports[chip->ch]) - 1; pm |= BIT(nr); @@ -136,7 +136,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -147,12 +147,12 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) u32 pm; unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm); pm &= BIT(num_ports[chip->ch]) - 1; pm &= ~BIT(nr); iowrite32(pm, &chip->reg->regs[chip->ch].pm); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -256,7 +256,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n", __func__, irq, type, ch, im_pos, type); - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -296,7 +296,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) ien = ioread32(&chip->reg->regs[chip->ch].ien); iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien); end: - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -326,11 +326,11 @@ static void ioh_irq_disable(struct irq_data *d) unsigned long flags; u32 ien; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); ien &= ~BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); } static void ioh_irq_enable(struct irq_data *d) @@ -340,11 +340,11 @@ static void ioh_irq_enable(struct irq_data *d) unsigned long flags; u32 ien; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); ien |= BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); } static irqreturn_t ioh_gpio_handler(int irq, void *dev_id) @@ -440,7 +440,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, chip->base = base; chip->reg = chip->base; chip->ch = i; - spin_lock_init(&chip->spinlock); + raw_spin_lock_init(&chip->spinlock); ioh_gpio_setup(chip, num_ports[i]); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); if (ret) { @@ -484,9 +484,9 @@ static int ioh_gpio_suspend(struct device *dev) struct ioh_gpio *chip = dev_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); ioh_gpio_save_reg_conf(chip); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } @@ -496,11 +496,11 @@ static int ioh_gpio_resume(struct device *dev) struct ioh_gpio *chip = dev_get_drvdata(dev); unsigned long flags; - spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(&chip->spinlock, flags); iowrite32(0x01, &chip->reg->srst); iowrite32(0x00, &chip->reg->srst); ioh_gpio_restore_reg_conf(chip); - spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(&chip->spinlock, flags); return 0; } -- cgit v1.2.3 From a9253ee6771c8ab3c6de07ea75d9e2c1cef3cd97 Mon Sep 17 00:00:00 2001 From: Junjie Cao Date: Tue, 4 Aug 2026 17:59:35 +0800 Subject: gpio: ml-ioh: share the register lock across channels Suspend and resume hold channel 0's lock while saving and restoring registers for all eight channels. Code paths using the other seven locks can therefore run concurrently with PM. Use one controller-wide lock shared by all channels. Fixes: b490fa0bf86e ("gpio-ml-ioh: Fix suspend/resume issue") Reported-by: sashiko-bot Link: https://lore.kernel.org/r/20260731033956.EE6F61F000E9@smtp.kernel.org Signed-off-by: Junjie Cao Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260804095935.2132215-1-junjie.cao@intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ml-ioh.c | 69 +++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 0a9d34b7636e..391188c0f2c2 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -73,7 +73,7 @@ struct ioh_gpio_reg_data { * @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM * @ch: Indicate GPIO channel * @irq_base: Save base of IRQ number for interrupt - * @spinlock: Used for register access protection + * @spinlock: Shared register access lock */ struct ioh_gpio { void __iomem *base; @@ -84,7 +84,12 @@ struct ioh_gpio { u32 gpio_use_sel; int ch; int irq_base; + raw_spinlock_t *spinlock; +}; + +struct ioh_gpio_device { raw_spinlock_t spinlock; + struct ioh_gpio chip[8]; }; static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; @@ -95,7 +100,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) struct ioh_gpio *chip = gpiochip_get_data(gpio); unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); reg_val = ioread32(&chip->reg->regs[chip->ch].po); if (val) reg_val |= BIT(nr); @@ -103,7 +108,7 @@ static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); return 0; } @@ -123,7 +128,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, u32 reg_val; unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm); pm &= BIT(num_ports[chip->ch]) - 1; pm |= BIT(nr); @@ -136,7 +141,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->regs[chip->ch].po); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); return 0; } @@ -147,12 +152,12 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) u32 pm; unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); pm = ioread32(&chip->reg->regs[chip->ch].pm); pm &= BIT(num_ports[chip->ch]) - 1; pm &= ~BIT(nr); iowrite32(pm, &chip->reg->regs[chip->ch].pm); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); return 0; } @@ -256,7 +261,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n", __func__, irq, type, ch, im_pos, type); - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); switch (type) { case IRQ_TYPE_EDGE_RISING: @@ -296,7 +301,7 @@ static int ioh_irq_type(struct irq_data *d, unsigned int type) ien = ioread32(&chip->reg->regs[chip->ch].ien); iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien); end: - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); return 0; } @@ -326,11 +331,11 @@ static void ioh_irq_disable(struct irq_data *d) unsigned long flags; u32 ien; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); ien &= ~BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); } static void ioh_irq_enable(struct irq_data *d) @@ -340,11 +345,11 @@ static void ioh_irq_enable(struct irq_data *d) unsigned long flags; u32 ien; - raw_spin_lock_irqsave(&chip->spinlock, flags); + raw_spin_lock_irqsave(chip->spinlock, flags); ien = ioread32(&chip->reg->regs[chip->ch].ien); ien |= BIT(d->irq - chip->irq_base); iowrite32(ien, &chip->reg->regs[chip->ch].ien); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_unlock_irqrestore(chip->spinlock, flags); } static irqreturn_t ioh_gpio_handler(int irq, void *dev_id) @@ -407,8 +412,8 @@ static int ioh_gpio_probe(struct pci_dev *pdev, int ret; int i, j; struct ioh_gpio *chip; + struct ioh_gpio_device *priv; void __iomem *base; - void *chip_save; int irq_base; ret = pcim_enable_device(pdev); @@ -429,18 +434,18 @@ static int ioh_gpio_probe(struct pci_dev *pdev, return -ENOMEM; } - chip_save = devm_kcalloc(dev, 8, sizeof(*chip), GFP_KERNEL); - if (chip_save == NULL) { + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - } - chip = chip_save; + raw_spin_lock_init(&priv->spinlock); + chip = priv->chip; for (i = 0; i < 8; i++, chip++) { chip->dev = dev; chip->base = base; chip->reg = chip->base; chip->ch = i; - raw_spin_lock_init(&chip->spinlock); + chip->spinlock = &priv->spinlock; ioh_gpio_setup(chip, num_ports[i]); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); if (ret) { @@ -449,7 +454,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, } } - chip = chip_save; + chip = priv->chip; for (j = 0; j < 8; j++, chip++) { irq_base = devm_irq_alloc_descs(dev, -1, IOH_IRQ_BASE, num_ports[j], NUMA_NO_NODE); @@ -466,7 +471,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, return ret; } - chip = chip_save; + chip = priv->chip; ret = devm_request_irq(dev, pdev->irq, ioh_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); if (ret != 0) { @@ -474,33 +479,33 @@ static int ioh_gpio_probe(struct pci_dev *pdev, return ret; } - pci_set_drvdata(pdev, chip); + pci_set_drvdata(pdev, priv); return 0; } static int ioh_gpio_suspend(struct device *dev) { - struct ioh_gpio *chip = dev_get_drvdata(dev); + struct ioh_gpio_device *priv = dev_get_drvdata(dev); unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); - ioh_gpio_save_reg_conf(chip); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_lock_irqsave(&priv->spinlock, flags); + ioh_gpio_save_reg_conf(priv->chip); + raw_spin_unlock_irqrestore(&priv->spinlock, flags); return 0; } static int ioh_gpio_resume(struct device *dev) { - struct ioh_gpio *chip = dev_get_drvdata(dev); + struct ioh_gpio_device *priv = dev_get_drvdata(dev); unsigned long flags; - raw_spin_lock_irqsave(&chip->spinlock, flags); - iowrite32(0x01, &chip->reg->srst); - iowrite32(0x00, &chip->reg->srst); - ioh_gpio_restore_reg_conf(chip); - raw_spin_unlock_irqrestore(&chip->spinlock, flags); + raw_spin_lock_irqsave(&priv->spinlock, flags); + iowrite32(0x01, &priv->chip->reg->srst); + iowrite32(0x00, &priv->chip->reg->srst); + ioh_gpio_restore_reg_conf(priv->chip); + raw_spin_unlock_irqrestore(&priv->spinlock, flags); return 0; } -- cgit v1.2.3 From 44f3468a0aef1aabdad551898ab7cfa2a9d20e99 Mon Sep 17 00:00:00 2001 From: Cengiz Can Date: Fri, 31 Jul 2026 01:02:57 +0300 Subject: gpio: sloppy-logic-analyzer: fix use-after-free via debugfs trigger on unbind The "trigger" debugfs file has a hand-rolled ->write handler (trigger_write()) that dereferences the per-device gpio_la_poll_priv. The file is created with debugfs_create_file_unsafe(), and the handler never takes a debugfs reference. Nothing keeps the object alive while the handler runs. priv is allocated with devm_kzalloc(). devres frees it when the platform device is unbound. debugfs_create_file_unsafe() installs no full_proxy wrapper, so debugfs_remove_recursive() in gpio_la_poll_remove() does not wait for an in-flight trigger_write(). The blob_lock taken there does not help, because trigger_write() never takes it. A write that races an unbind therefore writes into freed memory: trigger_write() gpio_la_poll_remove() priv = m->private buf = memdup_user() [may sleep] mutex_lock(&priv->blob_lock) debugfs_remove_recursive() [no wait] mutex_unlock(&priv->blob_lock) (remove returns; devres frees priv) priv->trig_data = buf <-- use-after-free write priv->trig_len = count The race is reachable by root via /sys/bus/platform/drivers/gpio-sloppy-logic-analyzer/unbind. Create "trigger" with debugfs_create_file() instead. Its full_proxy wrapper makes debugfs_remove_recursive() drain any in-flight ->write before it returns. The use-after-free is confirmed under KASAN with a minimal reproducer of the same debugfs_create_file_unsafe() plus devm_kzalloc() pattern (available on request); it produces a slab-use-after-free write in the handler. Fixes: 7828b7bbbf20 ("gpio: add sloppy logic analyzer using polling") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cengiz Can Reviewed-by: Wolfram Sang Link: https://patch.msgid.link/20260730220258.358169-2-cengiz.can@canonical.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sloppy-logic-analyzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index aa23b5779802..2f9a1f0384ad 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -300,7 +300,7 @@ static int gpio_la_poll_probe(struct platform_device *pdev) debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->acq_delay); debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size); debugfs_create_file_unsafe("capture", 0200, priv->debug_dir, priv, &fops_capture); - debugfs_create_file_unsafe("trigger", 0200, priv->debug_dir, priv, &fops_trigger); + debugfs_create_file("trigger", 0200, priv->debug_dir, priv, &fops_trigger); return 0; } -- cgit v1.2.3 From 566fec6a33075a0ea5c441c26571221f17f4ed98 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Fri, 7 Aug 2026 08:03:04 +0800 Subject: ASoC: tas2781: fix clang build error for goto bypassing cleanup variable Remove invalid goto exit paths that jump across guard(mutex) cleanup variable initialization, replace them with direct kfree(src) and return, to fix the s390 clang build error in acoustic_ctl_write(). Fixes: d75d38dc4604 ("ASoC: tas2781: Add a debugfs node for acoustic tuning") Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20260807000304.826-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 209067e98e1f..95d875c44b91 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1544,8 +1544,8 @@ static ssize_t acoustic_ctl_write(struct file *file, if (src[0] > max_pkg_len && src[0] != count) { dev_err(priv->dev, "pkg(%u), max(%u), count(%u) mismatch.\n", src[0], max_pkg_len, (unsigned int)count); - ret = 0; - goto exit; + kfree(src); + return 0; } switch (src[1]) { @@ -1559,14 +1559,14 @@ static ssize_t acoustic_ctl_write(struct file *file, break; default: dev_err(priv->dev, "%s Wrong code %02x.\n", __func__, src[1]); - ret = 0; - goto exit; + kfree(src); + return 0; } if (len < 1) { dev_err(priv->dev, "pkg fmt invalid %02x.\n", len); - ret = 0; - goto exit; + kfree(src); + return 0; } for (j = 0; j < priv->ndev; j++) @@ -1576,8 +1576,8 @@ static ssize_t acoustic_ctl_write(struct file *file, } if (j >= priv->ndev) { dev_err(priv->dev, "no such device 0x%02x.\n", src[2]); - ret = 0; - goto exit; + kfree(src); + return 0; } reg = TASDEVICE_REG(src[3], src[4], src[5]); @@ -1608,7 +1608,7 @@ static ssize_t acoustic_ctl_write(struct file *file, dev_err(priv->dev, "i2c communication error.\n"); else ret = count; -exit: + kfree(src); return ret; } -- cgit v1.2.3 From fe21db8c5e7c2a9815a9be54a1f5d556f905506e Mon Sep 17 00:00:00 2001 From: Lin Xianglin <1021538027@qq.com> Date: Fri, 7 Aug 2026 22:19:43 +0800 Subject: ASoC: amd: yc: Add DMI quirk for HyperX OMEN Gaming Laptop 16-ap1xxx The HyperX OMEN Gaming Laptop 16-ap1xxx (HP board 8F06) has an internal digital microphone array attached to the AMD ACP PDM controller, but the acp6x machine driver does not register the DMIC sound card because this board is missing from the DMI quirk table, leaving the internal microphone unusable. Add a DMI quirk entry for the HP board "8F06" so the acp6x DMIC capture card gets registered. Signed-off-by: Lin Xianglin <1021538027@qq.com> Link: https://patch.msgid.link/tencent_428392223C2AD3BF23E7ABAA7521FE5C0C07@qq.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 14b970f16020..ae90e9f513d1 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -731,6 +731,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8E35"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8F06"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From 8560d458509f798d8518d11cfee5c5cc58170558 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Thu, 6 Aug 2026 18:57:41 +0800 Subject: ASoC: Intel: soc-acpi: Add entry for HDMI_In capture support in NVL match table Adding HDMI-In capture via I2S feature support in NVL platform. Signed-off-by: Balamurugan C Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260806105742.2676322-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 10 ++++++++++ sound/soc/intel/common/soc-acpi-intel-nvl-match.c | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index e9ee752d5ec3..9b016136c639 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -847,6 +847,16 @@ static const struct platform_device_id board_ids[] = { SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | SOF_ES8336_JD_INVERTED), }, + { + .name = "nvl_es83x6_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) | + SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | + SOF_ES8336_JD_INVERTED), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index a7f4097d426c..51bbae5ed8fc 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -16,12 +16,24 @@ static const struct snd_soc_acpi_codecs nvl_essx_83x6 = { .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, }; +static const struct snd_soc_acpi_codecs nvl_lt6911_hdmi = { + .num_codecs = 1, + .codecs = {"INTC10B0"} +}; + static const struct snd_soc_acpi_codecs nvl_rt5682_rt5682s_hp = { .num_codecs = 2, .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID}, }; struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { + { + .comp_ids = &nvl_essx_83x6, + .drv_name = "nvl_es83x6_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &nvl_lt6911_hdmi, + .sof_tplg_filename = "sof-nvl-es83x6-ssp1-hdmi-ssp02.tplg", + }, { .comp_ids = &nvl_essx_83x6, .drv_name = "sof-essx8336", -- cgit v1.2.3 From d10549c4bcaad7ef69641c58db15470ec6ec5761 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Thu, 6 Aug 2026 18:57:42 +0800 Subject: ASoC: Intel: sof_rt5682: Add HDMI-In capture with rt5682 support for NVL. Added match table entry on nvl machines to support HDMI-In capture with rt5682 I2S audio codec. also added the respective quirk configuration in rt5682 machine driver. Signed-off-by: Balamurugan C Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260806105742.2676322-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 7 +++++++ sound/soc/intel/common/soc-acpi-intel-nvl-match.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 649378957b20..7899f7ffd99b 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -915,6 +915,13 @@ static const struct platform_device_id board_ids[] = { /* SSP 0 and SSP 2 are used for HDMI IN */ SOF_SSP_MASK_HDMI_CAPTURE(0x5)), }, + { + .name = "nvl_rt5682_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_SSP_PORT_CODEC(1) | + /* SSP 0 and SSP 2 are used for HDMI IN */ + SOF_SSP_MASK_HDMI_CAPTURE(0x5)), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index 51bbae5ed8fc..4a6208f821eb 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -42,6 +42,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, + { + .comp_ids = &nvl_rt5682_rt5682s_hp, + .drv_name = "nvl_rt5682_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &nvl_lt6911_hdmi, + .sof_tplg_filename = "sof-nvl-rt5682-ssp1-hdmi-ssp02.tplg", + }, { .comp_ids = &nvl_rt5682_rt5682s_hp, .drv_name = "sof_rt5682", -- cgit v1.2.3 From 54b279699279411c77c8afbc73b83c70740a7303 Mon Sep 17 00:00:00 2001 From: Rudi Heitbaum Date: Wed, 5 Aug 2026 15:21:02 +0000 Subject: ASoC: rt5645: Perform the initial jack detect at probe The only initial jack detect is the rt5645_irq(0, rt5645) at the end of rt5645_set_jack_detect(). A card described with simple-audio-card has no machine driver to call that, so jack state is only ever sampled from an edge on hp-detect-gpios. A headphone already in the socket at boot is therefore never noticed, and the card is silent with every mixer control set correctly. rt5645_jack_detect() is what force enables the "LDO2" and "Mic Det Power" supplies that the "HP amp" widget depends on, and what programs RT5645_CHARGE_PUMP away from its reset value, so without it "HP amp" cannot power up. Unplugging and replugging the jack is the only way to recover. Do the detect at the end of the component probe when the driver owns a hp-detect GPIO and the codec's own jack detect is unused, which is the case that has no other trigger. A machine driver calling rt5645_set_jack_detect() later just repeats it. Signed-off-by: Rudi Heitbaum Link: https://patch.msgid.link/anNU3tOUR7rOReSB@5e001e58230e Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e9819653b30d..e7e39671a576 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3496,6 +3496,10 @@ static int rt5645_probe(struct snd_soc_component *component) if (!rt5645->eq_param) return -ENOMEM; + /* no machine driver to call rt5645_set_jack_detect(), so detect here */ + if (!rt5645->pdata.jd_mode && rt5645->gpiod_hp_det) + rt5645_irq(0, rt5645); + return 0; } -- cgit v1.2.3 From 3314c90a2eda3df7da4ab6f4388e667b2758de7f Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Wed, 5 Aug 2026 01:27:39 +0530 Subject: pmdomain: arm: Fix -EINVAL from scmi_pd_set_perf_state() on state 0 Currently, scmi_pd_set_perf_state() treats a performance state of 0 as invalid and returns -EINVAL. As a result, devices attached to SCMI performance domains can report failures when relinquishing their performance vote. The OPP framework use performance state 0 to indicate that no performance vote is required. For example, dev_pm_opp_set_rate(dev, 0) is commonly used (by firmware or linux) when a device is runtime suspended. A zero performance state does not require any SCMI performance request to be sent. Treat it as a no-op and return success instead of reporting an error. Signed-off-by: Praveen Talari Fixes: 2af23ceb8624 ("pmdomain: arm: Add the SCMI performance domain") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/arm/scmi_perf_domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/arm/scmi_perf_domain.c b/drivers/pmdomain/arm/scmi_perf_domain.c index 3693423459c9..e390f902a444 100644 --- a/drivers/pmdomain/arm/scmi_perf_domain.c +++ b/drivers/pmdomain/arm/scmi_perf_domain.c @@ -33,7 +33,7 @@ scmi_pd_set_perf_state(struct generic_pm_domain *genpd, unsigned int state) return 0; if (!state) - return -EINVAL; + return 0; ret = pd->perf_ops->level_set(pd->ph, pd->domain_id, state, false); if (ret) -- cgit v1.2.3 From abe7c8b09bd72a9c726016257c6281f129b4c02d Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Thu, 6 Aug 2026 08:54:57 -0700 Subject: x86/CPU: Add a tlbi= cmdline switch With the recently found INVLPGB / TLBSYNC issue, there has been some interest in disabling INVLPGB-based TLB flushing, in order to rule out that CPU issue as a cause of userspace crashes. Add a kernel command line option to control the TLB flushing behavior. If the need arises, we will add a "tlbi=broadcast" for the case when TLB invalidation broadcasts need to be explicitly selected, but this is not needed now yet. [ bp: Rewrite commit message, move to cpu/common.c, add documentation. ] Fixes: 767ae437a32d ("x86/mm: Add INVLPGB feature and Kconfig entry") Suggested-by: Borislav Petkov Signed-off-by: Rik van Riel Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/20260729204341.3eb0b5ea@fangorn --- Documentation/admin-guide/kernel-parameters.txt | 4 ++++ arch/x86/kernel/cpu/common.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 3d35270dddef..e8ff36982d97 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -7568,6 +7568,10 @@ Kernel parameters See Documentation/admin-guide/mm/transhuge.rst for more details. + tlbi= [X86-64] + Format: {ipi} + ipi: switch to IPI-based TLB flushing + topology= [S390,EARLY] Format: {off | on} Specify if the kernel should make use of the cpu diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index a3df21d26460..a3caddd411ec 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -2664,3 +2664,13 @@ void __init arch_cpu_finalize_init(void) */ mem_encrypt_init(); } + +/* Control TLB flushing methods */ +static int __init tlbi_setup(char *str) +{ + if (!strcmp(str, "ipi")) + setup_clear_cpu_cap(X86_FEATURE_INVLPGB); + + return 1; +} +__setup("tlbi=", tlbi_setup); -- cgit v1.2.3 From 45f8dffc0714c3ef49c83e5bba4c56a4499bd5fc Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Wed, 22 Jul 2026 08:21:41 +0200 Subject: tick: Include ktime.h and jiffies.h in linux/tick.h The !CONFIG_NO_HZ_COMMON stubs use ktime_add(), ktime_get() and TICK_NSEC, but tick.h includes neither nor . Most configurations build only because those declarations arrive transitively. Commit 6440966067dc ("cpuset: Remove cpuset_cpu_is_isolated()") removed from . The include chain had been satisfying these declarations before was parsed. Commit 8aa76aa41589 ("ring-buffer: Use a housekeeping CPU to wake up waiters") then added to ring_buffer.c ahead of any header which provides them. Neither change is wrong on its own: the failure requires both and appeared in v7.0. ARM rpc_defconfig + CONFIG_FUNCTION_TRACER fails to build: $ make ARCH=arm rpc_defconfig $ ./scripts/config -e FTRACE -e FUNCTION_TRACER $ make ARCH=arm olddefconfig $ make ARCH=arm kernel/trace/ring_buffer.o In file included from include/linux/sched/isolation.h:6, from kernel/trace/ring_buffer.c:8: include/linux/tick.h: In function 'tick_nohz_get_next_hrtimer': include/linux/tick.h:156:9: error: implicit declaration of function 'ktime_add'; did you mean 'size_add'? include/linux/tick.h:156:19: error: implicit declaration of function 'ktime_get'; did you mean 'time_init'? include/linux/tick.h:156:32: error: 'TICK_NSEC' undeclared Include the headers the file actually uses. Fixes: 8aa76aa41589 ("ring-buffer: Use a housekeeping CPU to wake up waiters") Signed-off-by: Karl Mehltretter Signed-off-by: Thomas Gleixner Assisted-by: Codex:gpt-5.6-sol Link: https://patch.msgid.link/20260722062141.19671-1-kmehltretter@gmail.com --- include/linux/tick.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/tick.h b/include/linux/tick.h index 1cf4651f09ad..47750ccd1314 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -7,6 +7,8 @@ #include #include +#include +#include #include #include #include -- cgit v1.2.3 From 9b30521074f01aff856f539c1241a48342b69f7c Mon Sep 17 00:00:00 2001 From: Sergey Lebedev Date: Tue, 4 Aug 2026 22:59:24 +0000 Subject: ASoC: rt1320: run the initialisation preset on the first hardware init rt1320_io_init() applies the vendor initialisation preset only when the amplifier's SDCA function status has FUNCTION_NEEDS_INITIALIZATION set: if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) { Its two sibling drivers guard the same write differently, also running the preset on the first hardware init: rt712-sdca.c: if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { rt722-sdca.c: if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) { On the Microsoft Surface Pro 11 (Intel) the RT1320 never sets that bit. Its function status reads back 0x41 on every boot, cold or warm: rt1320-sdca sdw:0:0:025d:1320:01: rt1320_io_init amp func_status=0x41 which is NEWLY_ATTACHED | FUNCTION_HAS_BEEN_RESET: the function reports that it has been reset and does not consider itself in need of initialisation. Bit 5 is never set, so the preset never runs, rt1320_vc_preset() and the MCU patch load are skipped, and the amplifier is left unprogrammed. rt712 and rt722 would have run it via their first_hw_init fallback. Add the same fallback. With it rt1320_vc_preset() executes and the amplifier reports RT1320_KR0_INT_READY=0x1f where previously it did not. Signed-off-by: Sergey Lebedev Link: https://patch.msgid.link/20260804225853.31585-2-lsa.uz@pm.me Signed-off-by: Mark Brown --- sound/soc/codecs/rt1320-sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 1e930b27c67a..42acb6cef77a 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1899,7 +1899,7 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status); /* initialization write */ - if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) { + if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || !rt1320->first_hw_init) { switch (rt1320->dev_id) { case RT1320_DEV_ID: if (rt1320->version_id < RT1320_VC) -- cgit v1.2.3 From 0c88868271653537ed443272dd8e7d13634d214b Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Wed, 22 Jul 2026 22:38:32 +0000 Subject: netfilter: ipset: fix refcount race between list:set GC and swap __ip_set_put_byindex() resolved the index to a set pointer under RCU, then took ip_set_ref_lock in __ip_set_put() to decrement set->ref. ip_set_swap() holds that same lock while swapping both the ip_set_list slots and the two sets' ref counters, so it can interleave between the dereference and the lock acquisition, leaving the caller to decrement a set whose reference already moved to the other index and hit BUG_ON(set->ref == 0). list_set_gc() reaches this from timer softirq, which the nfnl mutex does not serialize against swap: an expiring list:set member calls list_set_del() -> ip_set_put_byindex() while IPSET_CMD_SWAP runs on the referenced sets. Resolve the index and decrement under ip_set_ref_lock, as ip_set_swap() already does, keeping the refcount tied to the index rather than to a stale set pointer. kernel BUG at net/netfilter/ipset/ip_set_core.c:685! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ip_set_put_byindex (net/netfilter/ipset/ip_set_core.c:870) Call Trace: list_set_del (net/netfilter/ipset/ip_set_list_set.c:159) set_cleanup_entries (net/netfilter/ipset/ip_set_list_set.c:181) list_set_gc (net/netfilter/ipset/ip_set_list_set.c:578) call_timer_fn (kernel/time/timer.c:1748) __run_timers (kernel/time/timer.c:1799 kernel/time/timer.c:2374) run_timer_softirq (kernel/time/timer.c:2405) Kernel panic - not syncing: Fatal exception in interrupt Fixes: 9076aea76538 ("netfilter: ipset: Increase the number of maximal sets automatically") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_core.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 543851a923d0..0a86a170ba90 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -680,11 +680,18 @@ __ip_set_get(struct ip_set *set) } static void -__ip_set_put(struct ip_set *set) +__ip_set_put_locked(struct ip_set *set) { - write_lock_bh(&ip_set_ref_lock); + lockdep_assert_held(&ip_set_ref_lock); BUG_ON(set->ref == 0); set->ref--; +} + +static void +__ip_set_put(struct ip_set *set) +{ + write_lock_bh(&ip_set_ref_lock); + __ip_set_put_locked(set); write_unlock_bh(&ip_set_ref_lock); } @@ -855,11 +862,11 @@ __ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index) { struct ip_set *set; - rcu_read_lock(); - set = rcu_dereference(inst->ip_set_list)[index]; + write_lock_bh(&ip_set_ref_lock); + set = ip_set(inst, index); if (set) - __ip_set_put(set); - rcu_read_unlock(); + __ip_set_put_locked(set); + write_unlock_bh(&ip_set_ref_lock); } void -- cgit v1.2.3 From d45cc8020d7c0a9f01dee42ff5c40bc14c9af72f Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Fri, 31 Jul 2026 14:36:53 +0800 Subject: netfilter: bridge: release template ct on non-IP path A bridge nftables ct zone set rule can attach a conntrack template to an skb before nf_ct_bridge_pre() sees it. For non-IPv4 and non-IPv6 EtherTypes, nf_ct_bridge_pre() currently overwrites skb->_nfct with IP_CT_UNTRACKED without releasing the existing template reference. That makes the per-cpu template, and any temporary templates allocated for concurrent use, unreachable and leaks memory until the host runs out of slab. Reset the skb conntrack state before marking the frame untracked so the existing template reference is dropped on the non-IP path. Fixes: 3c171f496ef5 ("netfilter: bridge: add connection tracking system") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/nf_conntrack_bridge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c index 58a33d0380b0..7ecb8a26bfa3 100644 --- a/net/bridge/netfilter/nf_conntrack_bridge.c +++ b/net/bridge/netfilter/nf_conntrack_bridge.c @@ -280,6 +280,7 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb, ret = nf_ct_br_defrag6(skb, &bridge_state); break; default: + nf_reset_ct(skb); nf_ct_set(skb, NULL, IP_CT_UNTRACKED); return NF_ACCEPT; } -- cgit v1.2.3 From 04d2feaed8d0103c498727191ba04001d5100e67 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Fri, 31 Jul 2026 22:27:41 +0800 Subject: ipvs: add totalconns for dest Replace the inactconns dest counter with totalconns, now inactconns can be obtained from totalconns - activeconns. This reduces the atomic inc/dec ops for TCP/SCTP from 6 to 4 if the connection is established and then closed. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Julian Anastasov Signed-off-by: Yizhou Zhao Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 17 ++++++++++++----- net/netfilter/ipvs/ip_vs_conn.c | 24 +++++++----------------- net/netfilter/ipvs/ip_vs_ctl.c | 10 +++++----- net/netfilter/ipvs/ip_vs_lc.c | 4 ++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 -- net/netfilter/ipvs/ip_vs_proto_tcp.c | 2 -- net/netfilter/ipvs/ip_vs_sync.c | 7 ++----- 7 files changed, 28 insertions(+), 38 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index d2813eb795be..11f430646db8 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -987,7 +987,7 @@ struct ip_vs_dest { /* connection counters and thresholds */ atomic_t activeconns; /* active connections */ - atomic_t inactconns; /* inactive connections */ + atomic_t totalconns; /* total connections */ atomic_t persistconns; /* persistent connections */ __u32 u_threshold; /* upper threshold */ __u32 l_threshold; /* lower threshold */ @@ -2220,14 +2220,21 @@ void ip_vs_unregister_hooks(struct netns_ipvs *ipvs, unsigned int af); static inline int ip_vs_dest_conn_overhead(struct ip_vs_dest *dest) { - /* We think the overhead of processing active connections is 256 + /* We think the overhead of processing active connections is 257 * times higher than that of inactive connections in average. (This - * 256 times might not be accurate, we will change it later) We + * 257 times might not be accurate, we will change it later) We * use the following formula to estimate the overhead now: - * dest->activeconns*256 + dest->inactconns + * dest->activeconns*256 + dest->totalconns */ return (atomic_read(&dest->activeconns) << 8) + - atomic_read(&dest->inactconns); + atomic_read(&dest->totalconns); +} + +static inline int +ip_vs_dest_inactconns(const struct ip_vs_dest *dest) +{ + return max(atomic_read(&dest->totalconns) - + atomic_read(&dest->activeconns), 0); } #ifdef CONFIG_IP_VS_PROTO_TCP diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 36c5cba03f5b..4d0a6f718ced 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1081,12 +1081,6 @@ static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp) #endif -static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest) -{ - return atomic_read(&dest->activeconns) - + atomic_read(&dest->inactconns); -} - /* * Bind a connection entry with a virtual service destination * Called just after a new connection entry is created. @@ -1153,8 +1147,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) */ if (!(flags & IP_VS_CONN_F_INACTIVE)) atomic_inc(&dest->activeconns); - else - atomic_inc(&dest->inactconns); + atomic_inc(&dest->totalconns); } else { /* It is a persistent connection/template, so increase the persistent connection counter */ @@ -1162,7 +1155,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) } if (dest->u_threshold != 0 && - ip_vs_dest_totalconns(dest) >= dest->u_threshold) + atomic_read(&dest->totalconns) >= dest->u_threshold) dest->flags |= IP_VS_DEST_F_OVERLOAD; } @@ -1244,13 +1237,10 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) /* Update the connection counters */ if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { - /* It is a normal connection, so decrease the inactconns - or activeconns counter */ - if (cp->flags & IP_VS_CONN_F_INACTIVE) { - atomic_dec(&dest->inactconns); - } else { + /* It is a normal connection, so decrease the counters */ + if (!(cp->flags & IP_VS_CONN_F_INACTIVE)) atomic_dec(&dest->activeconns); - } + atomic_dec(&dest->totalconns); } else { /* It is a persistent connection/template, so decrease the persistent connection counter */ @@ -1258,10 +1248,10 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) } if (dest->l_threshold != 0) { - if (ip_vs_dest_totalconns(dest) < dest->l_threshold) + if (atomic_read(&dest->totalconns) < dest->l_threshold) dest->flags &= ~IP_VS_DEST_F_OVERLOAD; } else if (dest->u_threshold != 0) { - if (ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3) + if (atomic_read(&dest->totalconns) * 4 < dest->u_threshold * 3) dest->flags &= ~IP_VS_DEST_F_OVERLOAD; } else { if (dest->flags & IP_VS_DEST_F_OVERLOAD) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index bcf40b8c41cf..45f534427d23 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1445,7 +1445,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) dest->port = udest->port; atomic_set(&dest->activeconns, 0); - atomic_set(&dest->inactconns, 0); + atomic_set(&dest->totalconns, 0); atomic_set(&dest->persistconns, 0); refcount_set(&dest->refcnt, 1); @@ -3031,7 +3031,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v) ip_vs_fwd_name(atomic_read(&dest->conn_flags)), atomic_read(&dest->weight), atomic_read(&dest->activeconns), - atomic_read(&dest->inactconns)); + ip_vs_dest_inactconns(dest)); else #endif seq_printf(seq, @@ -3042,7 +3042,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v) ip_vs_fwd_name(atomic_read(&dest->conn_flags)), atomic_read(&dest->weight), atomic_read(&dest->activeconns), - atomic_read(&dest->inactconns)); + ip_vs_dest_inactconns(dest)); } } @@ -3670,7 +3670,7 @@ __ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests * entry.u_threshold = dest->u_threshold; entry.l_threshold = dest->l_threshold; entry.activeconns = atomic_read(&dest->activeconns); - entry.inactconns = atomic_read(&dest->inactconns); + entry.inactconns = ip_vs_dest_inactconns(dest); entry.persistconns = atomic_read(&dest->persistconns); ip_vs_copy_stats(&kstats, &dest->stats); ip_vs_export_stats_user(&entry.stats, &kstats); @@ -4282,7 +4282,7 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) nla_put_u32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS, atomic_read(&dest->activeconns)) || nla_put_u32(skb, IPVS_DEST_ATTR_INACT_CONNS, - atomic_read(&dest->inactconns)) || + ip_vs_dest_inactconns(dest)) || nla_put_u32(skb, IPVS_DEST_ATTR_PERSIST_CONNS, atomic_read(&dest->persistconns)) || nla_put_u16(skb, IPVS_DEST_ATTR_ADDR_FAMILY, dest->af)) diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index 38cc38c5d8bb..18b976a7c4d2 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -30,7 +30,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, /* * Simply select the server with the least number of - * (activeconns<<5) + inactconns + * (activeconns*256) + totalconns * Except whose weight is equal to zero. * If the weight is equal to zero, it means that the server is * quiesced, the existing connections to the server still get @@ -56,7 +56,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), - atomic_read(&least->inactconns)); + ip_vs_dest_inactconns(least)); return least; } diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index c80567c73469..fb8af6b15a39 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -446,12 +446,10 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && (next_state != IP_VS_SCTP_S_ESTABLISHED)) { atomic_dec(&dest->activeconns); - atomic_inc(&dest->inactconns); cp->flags |= IP_VS_CONN_F_INACTIVE; } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) && (next_state == IP_VS_SCTP_S_ESTABLISHED)) { atomic_inc(&dest->activeconns); - atomic_dec(&dest->inactconns); cp->flags &= ~IP_VS_CONN_F_INACTIVE; } } diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 99a286fdc90c..fec0e8b47b71 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -526,12 +526,10 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && !tcp_state_active(new_state)) { atomic_dec(&dest->activeconns); - atomic_inc(&dest->inactconns); cp->flags |= IP_VS_CONN_F_INACTIVE; } else if ((cp->flags & IP_VS_CONN_F_INACTIVE) && tcp_state_active(new_state)) { atomic_inc(&dest->activeconns); - atomic_dec(&dest->inactconns); cp->flags &= ~IP_VS_CONN_F_INACTIVE; } } diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 93038abbf5e0..ea5fdd4f4ce7 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -878,13 +878,10 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *pa spin_lock_bh(&cp->lock); if ((cp->flags ^ flags) & IP_VS_CONN_F_INACTIVE && !(flags & IP_VS_CONN_F_TEMPLATE) && dest) { - if (flags & IP_VS_CONN_F_INACTIVE) { + if (flags & IP_VS_CONN_F_INACTIVE) atomic_dec(&dest->activeconns); - atomic_inc(&dest->inactconns); - } else { + else atomic_inc(&dest->activeconns); - atomic_dec(&dest->inactconns); - } } flags &= IP_VS_CONN_F_BACKUP_UPD_MASK; flags |= cp->flags & ~IP_VS_CONN_F_BACKUP_UPD_MASK; -- cgit v1.2.3 From 8f843441c4e7eae8ea83491e8c203c2b192edcf5 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Fri, 31 Jul 2026 22:27:42 +0800 Subject: ipvs: properly update the overload flag on dest edit The upper/lower connection thresholds for dest can be changed, so use ip_vs_dest_update_overload() to properly update the dest overload flag. The thresholds were not limited, fit them in the 0 .. INT_MAX range as already done in ipvsadm. As the thresholds are also read when connections are created and expired, use WRITE_ONCE/READ_ONCE to access them. As the lower threshold is optional, use (u - (u >> 2)) to calculate the 75% default value based on the upper threshold by preserving the integer rounding, as suggested by Yizhou Zhao. Trigger flag update when totalconns reaches one of the thresholds and use dst_lock to serialize the updating. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Julian Anastasov Signed-off-by: Yizhou Zhao Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 3 ++ net/netfilter/ipvs/ip_vs_conn.c | 27 ++++++----------- net/netfilter/ipvs/ip_vs_ctl.c | 67 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 25 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 11f430646db8..e99382930617 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -991,6 +991,7 @@ struct ip_vs_dest { atomic_t persistconns; /* persistent connections */ __u32 u_threshold; /* upper threshold */ __u32 l_threshold; /* lower threshold */ + __u32 l_threshold_val;/* used lower threshold */ /* for destination cache */ spinlock_t dst_lock; /* lock of dst_cache */ @@ -1907,6 +1908,8 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest) kfree(dest); } +void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode); + /* IPVS sync daemon data and function prototypes * (from ip_vs_sync.c) */ diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 4d0a6f718ced..abf52a226fee 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1141,22 +1141,22 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) /* Update the connection counters */ if (!(flags & IP_VS_CONN_F_TEMPLATE)) { + int tc; + /* It is a normal connection, so modify the counters * according to the flags, later the protocol can * update them on state change */ if (!(flags & IP_VS_CONN_F_INACTIVE)) atomic_inc(&dest->activeconns); - atomic_inc(&dest->totalconns); + tc = atomic_inc_return(&dest->totalconns); + if (tc == READ_ONCE(dest->u_threshold)) + ip_vs_dest_update_overload(dest, 1); } else { /* It is a persistent connection/template, so increase the persistent connection counter */ atomic_inc(&dest->persistconns); } - - if (dest->u_threshold != 0 && - atomic_read(&dest->totalconns) >= dest->u_threshold) - dest->flags |= IP_VS_DEST_F_OVERLOAD; } @@ -1237,27 +1237,20 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) /* Update the connection counters */ if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { + int tc; + /* It is a normal connection, so decrease the counters */ if (!(cp->flags & IP_VS_CONN_F_INACTIVE)) atomic_dec(&dest->activeconns); - atomic_dec(&dest->totalconns); + tc = atomic_fetch_dec(&dest->totalconns); + if (tc == READ_ONCE(dest->l_threshold_val)) + ip_vs_dest_update_overload(dest, -1); } else { /* It is a persistent connection/template, so decrease the persistent connection counter */ atomic_dec(&dest->persistconns); } - if (dest->l_threshold != 0) { - if (atomic_read(&dest->totalconns) < dest->l_threshold) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; - } else if (dest->u_threshold != 0) { - if (atomic_read(&dest->totalconns) * 4 < dest->u_threshold * 3) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; - } else { - if (dest->flags & IP_VS_DEST_F_OVERLOAD) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; - } - ip_vs_dest_put(dest); } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 45f534427d23..974773642af8 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1304,6 +1304,40 @@ void ip_vs_stats_free(struct ip_vs_stats *stats) } } +/* Update overload flag based on number of dest conns and lower/upper + * connection thresholds: + * - conns reach u_threshold and exceed it: set the flag + * - conns go below l_threshold (or 75% of u_threshold): clear the flag + */ +static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode) +{ + int conns; + u32 l, u; + + lockdep_assert_held(&dest->dst_lock); + u = READ_ONCE(dest->u_threshold); + if (!u) + goto unset; + l = READ_ONCE(dest->l_threshold_val); + conns = atomic_read(&dest->totalconns); + if (conns >= (mode > 0 ? l : u)) { + dest->flags |= IP_VS_DEST_F_OVERLOAD; + return; + } + if (conns >= (mode < 0 ? u : l)) + return; + +unset: + dest->flags &= ~IP_VS_DEST_F_OVERLOAD; +} + +void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode) +{ + spin_lock_bh(&dest->dst_lock); + __ip_vs_dest_update_overload(dest, mode); + spin_unlock_bh(&dest->dst_lock); +} + /* * Update a destination in the given service */ @@ -1370,10 +1404,19 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, /* set the dest status flags */ dest->flags |= IP_VS_DEST_F_AVAILABLE; - if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold) - dest->flags &= ~IP_VS_DEST_F_OVERLOAD; - dest->u_threshold = udest->u_threshold; - dest->l_threshold = udest->l_threshold; + if (READ_ONCE(dest->u_threshold) != udest->u_threshold || + READ_ONCE(dest->l_threshold) != udest->l_threshold) { + spin_lock_bh(&dest->dst_lock); + WRITE_ONCE(dest->u_threshold, udest->u_threshold); + WRITE_ONCE(dest->l_threshold, udest->l_threshold); + /* Low threshold defaults to 75% of upper threshold */ + WRITE_ONCE(dest->l_threshold_val, + udest->l_threshold ? : + (udest->u_threshold - + (udest->u_threshold >> 2))); + __ip_vs_dest_update_overload(dest, 0); + spin_unlock_bh(&dest->dst_lock); + } dest->af = udest->af; @@ -1486,6 +1529,9 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) return -ERANGE; } + if (udest->u_threshold > INT_MAX) + return -EINVAL; + if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) { if (udest->tun_port == 0) { pr_err("%s(): tunnel port is zero\n", __func__); @@ -1559,6 +1605,9 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) return -ERANGE; } + if (udest->u_threshold > INT_MAX) + return -EINVAL; + if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) { if (udest->tun_port == 0) { pr_err("%s(): tunnel port is zero\n", __func__); @@ -3667,8 +3716,8 @@ __ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests * entry.port = dest->port; entry.conn_flags = atomic_read(&dest->conn_flags); entry.weight = atomic_read(&dest->weight); - entry.u_threshold = dest->u_threshold; - entry.l_threshold = dest->l_threshold; + entry.u_threshold = READ_ONCE(dest->u_threshold); + entry.l_threshold = READ_ONCE(dest->l_threshold); entry.activeconns = atomic_read(&dest->activeconns); entry.inactconns = ip_vs_dest_inactconns(dest); entry.persistconns = atomic_read(&dest->persistconns); @@ -4277,8 +4326,10 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) dest->tun_port) || nla_put_u16(skb, IPVS_DEST_ATTR_TUN_FLAGS, dest->tun_flags) || - nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold) || - nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold) || + nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH, + READ_ONCE(dest->u_threshold)) || + nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH, + READ_ONCE(dest->l_threshold)) || nla_put_u32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS, atomic_read(&dest->activeconns)) || nla_put_u32(skb, IPVS_DEST_ATTR_INACT_CONNS, -- cgit v1.2.3 From cdcc4e46180df8161f4d2f3c6fd6beaf6990133d Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Fri, 31 Jul 2026 22:27:43 +0800 Subject: ipvs: separate destination availability state IPVS configuration paths update destination availability while connection accounting updates destination overload state. The two independent states share dest->flags, so their read-modify-write updates can race and lose one another. Keep OVERLOAD in flags, where the preceding patch serializes its updates with dst_lock, and move AVAILABLE to cflags. This keeps configuration- controlled availability out of the scheduler hot cacheline until a scheduler needs to check it. It also prevents availability updates from clobbering overload state. The destination status bits are not exposed through the IPVS sockopt or netlink interfaces, so keep their definitions in the internal IPVS header. Readers can still observe stale destination state; this does not provide a cross-field snapshot. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") 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 Link: https://lore.kernel.org/all/8913381c-1e02-35c7-0ec4-61de5a12fd35@ssi.bg/ Assisted-by: Claude-Code:GLM-5.2 Suggested-by: Julian Anastasov Signed-off-by: Yizhou Zhao Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 7 +++++++ include/uapi/linux/ip_vs.h | 6 ------ net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- net/netfilter/ipvs/ip_vs_dh.c | 4 ++-- net/netfilter/ipvs/ip_vs_lblc.c | 2 +- net/netfilter/ipvs/ip_vs_lblcr.c | 8 ++++---- net/netfilter/ipvs/ip_vs_xmit.c | 4 ++-- 9 files changed, 23 insertions(+), 22 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e99382930617..fc2ef5ef31a6 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -36,6 +36,12 @@ #define IP_VS_HDR_INVERSE 1 #define IP_VS_HDR_ICMP 2 +/* Destination Server Flags */ +#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ + +/* Destination Server Config Flags */ +#define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */ + /* conn_tab limits (as per Kconfig) */ #define IP_VS_CONN_TAB_MIN_BITS 8 #if BITS_PER_LONG > 32 @@ -976,6 +982,7 @@ struct ip_vs_dest { volatile unsigned int flags; /* dest status flags */ atomic_t conn_flags; /* flags to copy to conn */ atomic_t weight; /* server weight */ + unsigned long cflags; /* config flags */ atomic_t last_weight; /* server latest weight */ __u16 tun_type; /* tunnel type */ __be16 tun_port; /* tunnel port */ diff --git a/include/uapi/linux/ip_vs.h b/include/uapi/linux/ip_vs.h index 1ed234e7f251..2c37c6ac7525 100644 --- a/include/uapi/linux/ip_vs.h +++ b/include/uapi/linux/ip_vs.h @@ -28,12 +28,6 @@ #define IP_VS_SVC_F_SCHED_SH_FALLBACK IP_VS_SVC_F_SCHED1 /* SH fallback */ #define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2 /* SH use port */ -/* - * Destination Server Flags - */ -#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */ -#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ - /* * IPVS sync daemon states */ diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index abf52a226fee..6fa3e1dc534c 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1279,7 +1279,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct, struct ip_vs_dest *cdest) * Checking the dest server status. */ if ((dest == NULL) || - !(dest->flags & IP_VS_DEST_F_AVAILABLE) || + !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) || expire_quiescent_template(ipvs, dest) || (cdest && (dest != cdest))) { IP_VS_DBG_BUF(9, "check_template: dest not available for " @@ -2020,7 +2020,7 @@ repeat: cp = ip_vs_hn0_to_conn(hn); resched_score++; dest = cp->dest; - if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE)) + if (!dest || (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) continue; if (atomic_read(&cp->n_control)) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 0bdaeb4ed61e..95af77b68851 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -302,7 +302,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) struct ip_vs_dest *dest = cp->dest; struct netns_ipvs *ipvs = cp->ipvs; - if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { struct ip_vs_cpu_stats *s; struct ip_vs_service *svc; @@ -338,7 +338,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) struct ip_vs_dest *dest = cp->dest; struct netns_ipvs *ipvs = cp->ipvs; - if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { struct ip_vs_cpu_stats *s; struct ip_vs_service *svc; @@ -2210,7 +2210,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state } /* Check the server status */ - if (cp && cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (cp && cp->dest && !(cp->dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { /* the destination server is not available */ if (sysctl_expire_nodest_conn(ipvs)) { bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 974773642af8..8f9a8e491ad6 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1402,7 +1402,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, } /* set the dest status flags */ - dest->flags |= IP_VS_DEST_F_AVAILABLE; + dest->cflags |= IP_VS_DEST_CF_AVAILABLE; if (READ_ONCE(dest->u_threshold) != udest->u_threshold || READ_ONCE(dest->l_threshold) != udest->l_threshold) { @@ -1662,7 +1662,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, int svcupd) { - dest->flags &= ~IP_VS_DEST_F_AVAILABLE; + dest->cflags &= ~IP_VS_DEST_CF_AVAILABLE; spin_lock_bh(&dest->dst_lock); __ip_vs_dst_cache_reset(dest); diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index e1f62f6b25e2..43abed7a26a6 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -219,8 +219,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, s = (struct ip_vs_dh_state *) svc->sched_data; dest = ip_vs_dh_get(svc->af, s, &iph->daddr); - if (!dest - || !(dest->flags & IP_VS_DEST_F_AVAILABLE) + if (!dest || + !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) || atomic_read(&dest->weight) <= 0 || is_overloaded(dest)) { ip_vs_scheduler_err(svc, "no destination available"); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 15ccb2b2fa1f..693bcc82ccb7 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -502,7 +502,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ dest = en->dest; - if ((dest->flags & IP_VS_DEST_F_AVAILABLE) && + if ((dest->cflags & IP_VS_DEST_CF_AVAILABLE) && atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc)) goto out; } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index c90ea897c3f7..f53f05ceea36 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -169,8 +169,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) if (least->flags & IP_VS_DEST_F_OVERLOAD) continue; - if ((atomic_read(&least->weight) > 0) - && (least->flags & IP_VS_DEST_F_AVAILABLE)) { + if ((atomic_read(&least->weight) > 0) && + (least->cflags & IP_VS_DEST_CF_AVAILABLE)) { loh = ip_vs_dest_conn_overhead(least); goto nextstage; } @@ -186,8 +186,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) doh = ip_vs_dest_conn_overhead(dest); if (((__s64)loh * atomic_read(&dest->weight) > - (__s64)doh * atomic_read(&least->weight)) - && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + (__s64)doh * atomic_read(&least->weight)) && + (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { least = dest; loh = doh; } diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index c4508f3f43dd..fc7403186394 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -351,7 +351,7 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, * stored in dest_trash. */ if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) && - dest->flags & IP_VS_DEST_F_AVAILABLE) + dest->cflags & IP_VS_DEST_CF_AVAILABLE) __ip_vs_dst_set(dest, dest_dst, &rt->dst, 0); else noref = 0; @@ -530,7 +530,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, * stored in dest_trash. */ if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) && - dest->flags & IP_VS_DEST_F_AVAILABLE) + dest->cflags & IP_VS_DEST_CF_AVAILABLE) __ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie); else noref = 0; -- cgit v1.2.3 From 2d19b95c9723001f214f7a47d67b09f46238f200 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sat, 1 Aug 2026 14:27:17 +0000 Subject: netfilter: nf_conntrack: defer invalid log until after unlock TCP and SCTP conntrack paths can emit invalid-packet logs while ct->lock is still held. When invalid logging is routed to nfnetlink_log and conntrack export is enabled, the log path can re-enter conntrack netlink glue and dump the same conntrack again. Protocol attribute dumping may take ct->lock, so logging while holding that lock can deadlock. Defer the TCP invalid logs by storing only the minimal log context while ct->lock is held and emitting the log after unlocking. Also make the TCP timeout-lowering invalid path return whether a log is needed, then emit that log after unlocking. Do the same for the SCTP invalid state-transition log that can be reached while ct->lock is held. Add a lockdep assertion to nf_ct_l4proto_log_invalid() so future callers that log invalid conntracks while holding ct->lock are caught outside TCP and SCTP as well. Fixes: 628d694344a0 ("netfilter: conntrack: reduce timeout when receiving out-of-window fin or rst") Fixes: d9a6f0d0df18 ("netfilter: conntrack: prepare tcp_in_window for ternary return value") Fixes: f71cb8f45d09 ("netfilter: conntrack: sctp: use nf log infrastructure for invalid packets") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto.c | 6 ++ net/netfilter/nf_conntrack_proto_sctp.c | 12 ++- net/netfilter/nf_conntrack_proto_tcp.c | 132 +++++++++++++++++++++----------- 3 files changed, 102 insertions(+), 48 deletions(-) diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index ad96896516b6..7a40e4e0e33e 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -79,6 +79,12 @@ void nf_ct_l4proto_log_invalid(const struct sk_buff *skb, struct net *net; va_list args; + /* nfnetlink_log may re-enter conntrack attribute dumping and try to + * take ct->lock again via helpers such as tcp_to_nlattr(), so invalid + * conntrack logs must only be emitted after dropping ct->lock. + */ + lockdep_assert_not_held(&ct->lock); + net = nf_ct_net(ct); if (likely(net->ct.sysctl_log_invalid == 0)) return; diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 7e10fa65cbdd..71cc920f6856 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -336,10 +336,12 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, struct sctphdr _sctph; const struct sctp_chunkhdr *sch; struct sctp_chunkhdr _sch; + bool log_invalid = false; u_int32_t offset, count; unsigned int *timeouts; unsigned long map[256 / sizeof(unsigned long)] = { 0 }; bool ignore = false; + u8 invalid_type = 0; if (sctp_error(skb, dataoff, state)) return -NF_ACCEPT; @@ -451,10 +453,8 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, /* Invalid */ if (new_state == SCTP_CONNTRACK_MAX) { - nf_ct_l4proto_log_invalid(skb, ct, state, - "Invalid, old_state %d, dir %d, type %d", - old_state, dir, sch->type); - + log_invalid = true; + invalid_type = sch->type; goto out_unlock; } @@ -529,6 +529,10 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, out_unlock: spin_unlock_bh(&ct->lock); + if (log_invalid) + nf_ct_l4proto_log_invalid(skb, ct, state, + "Invalid, old_state %d, dir %d, type %d", + old_state, dir, invalid_type); out: return -NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index ceeed3d7fe52..30b970e2ade5 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -480,37 +480,81 @@ static void tcp_init_sender(struct ip_ct_tcp_state *sender, } } -__printf(6, 7) -static enum nf_ct_tcp_action nf_tcp_log_invalid(const struct sk_buff *skb, - const struct nf_conn *ct, - const struct nf_hook_state *state, - const struct ip_ct_tcp_state *sender, - enum nf_ct_tcp_action ret, - const char *fmt, ...) +enum nf_tcp_invalid_log_type { + NF_TCP_LOG_NONE, + NF_TCP_LOG_OVERSHOT, + NF_TCP_LOG_SEQ_OVER, + NF_TCP_LOG_ACK_OVER, + NF_TCP_LOG_SEQ_UNDER, + NF_TCP_LOG_ACK_UNDER, +}; + +struct nf_tcp_invalid_log { + enum nf_tcp_invalid_log_type type; + u32 value; +}; + +static enum nf_ct_tcp_action +nf_tcp_store_invalid(const struct nf_conn *ct, + const struct ip_ct_tcp_state *sender, + struct nf_tcp_invalid_log *log, + enum nf_ct_tcp_action ret, + enum nf_tcp_invalid_log_type type, + u32 value) { const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct)); - struct va_format vaf; - va_list args; bool be_liberal; be_liberal = sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || tn->tcp_be_liberal; if (be_liberal) return NFCT_TCP_ACCEPT; - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; - nf_ct_l4proto_log_invalid(skb, ct, state, "%pV", &vaf); - va_end(args); - + log->type = type; + log->value = value; return ret; } +static void nf_tcp_log_invalid(const struct sk_buff *skb, + const struct nf_conn *ct, + const struct nf_hook_state *state, + const struct nf_tcp_invalid_log *log) +{ + switch (log->type) { + case NF_TCP_LOG_OVERSHOT: + nf_ct_l4proto_log_invalid(skb, ct, state, + "%u bytes more than expected", + log->value); + break; + case NF_TCP_LOG_SEQ_OVER: + nf_ct_l4proto_log_invalid(skb, ct, state, + "SEQ is over upper bound %u (over the window of the receiver)", + log->value); + break; + case NF_TCP_LOG_ACK_OVER: + nf_ct_l4proto_log_invalid(skb, ct, state, + "ACK is over upper bound %u (ACKed data not seen yet)", + log->value); + break; + case NF_TCP_LOG_SEQ_UNDER: + nf_ct_l4proto_log_invalid(skb, ct, state, + "SEQ is under lower bound %u (already ACKed data retransmitted)", + log->value); + break; + case NF_TCP_LOG_ACK_UNDER: + nf_ct_l4proto_log_invalid(skb, ct, state, + "ignored ACK under lower bound %u (possible overly delayed)", + log->value); + break; + case NF_TCP_LOG_NONE: + break; + } +} + static enum nf_ct_tcp_action tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir, unsigned int index, const struct sk_buff *skb, unsigned int dataoff, const struct tcphdr *tcph, - const struct nf_hook_state *hook_state) + struct nf_tcp_invalid_log *log) { struct ip_ct_tcp *state = &ct->proto.tcp; struct ip_ct_tcp_state *sender = &state->seen[dir]; @@ -640,31 +684,29 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir, sender->td_end = end; sender->flags |= IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED; - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE, - "%u bytes more than expected", overshot); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE, + NF_TCP_LOG_OVERSHOT, overshot); } - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_INVALID, - "SEQ is over upper bound %u (over the window of the receiver)", - sender->td_maxend + 1); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_INVALID, + NF_TCP_LOG_SEQ_OVER, sender->td_maxend + 1); } if (!before(sack, receiver->td_end + 1)) - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_INVALID, - "ACK is over upper bound %u (ACKed data not seen yet)", - receiver->td_end + 1); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_INVALID, + NF_TCP_LOG_ACK_OVER, receiver->td_end + 1); /* Is the ending sequence in the receive window (if available)? */ in_recv_win = !receiver->td_maxwin || after(end, sender->td_end - receiver->td_maxwin - 1); if (!in_recv_win) - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE, - "SEQ is under lower bound %u (already ACKed data retransmitted)", - sender->td_end - receiver->td_maxwin - 1); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE, + NF_TCP_LOG_SEQ_UNDER, + sender->td_end - receiver->td_maxwin - 1); if (!after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1)) - return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE, - "ignored ACK under lower bound %u (possible overly delayed)", - receiver->td_end - MAXACKWINDOW(sender) - 1); + return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE, + NF_TCP_LOG_ACK_UNDER, + receiver->td_end - MAXACKWINDOW(sender) - 1); /* Take into account window scaling (RFC 1323). */ if (!tcph->syn) @@ -719,11 +761,8 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir, return NFCT_TCP_ACCEPT; } -static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, - enum ip_conntrack_dir dir, - int index, - const struct sk_buff *skb, - const struct nf_hook_state *hook_state) +static bool __cold +nf_tcp_handle_invalid(struct nf_conn *ct, enum ip_conntrack_dir dir, int index) { const unsigned int *timeouts; const struct nf_tcp_net *tn; @@ -732,7 +771,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, if (!test_bit(IPS_ASSURED_BIT, &ct->status) || test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) - return; + return false; /* We don't want to have connections hanging around in ESTABLISHED * state for long time 'just because' conntrack deemed a FIN/RST @@ -747,7 +786,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, case TCP_FIN_SET: break; default: - return; + return false; } if (ct->proto.tcp.last_dir != dir && @@ -755,7 +794,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, ct->proto.tcp.last_index == TCP_RST_SET)) { expires = nf_ct_expires(ct); if (expires < 120 * HZ) - return; + return false; tn = nf_tcp_pernet(nf_ct_net(ct)); timeouts = nf_ct_timeout_lookup(ct); @@ -764,16 +803,15 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct, timeout = READ_ONCE(timeouts[TCP_CONNTRACK_UNACK]); if (expires > timeout) { - nf_ct_l4proto_log_invalid(skb, ct, hook_state, - "packet (index %d, dir %d) response for index %d lower timeout to %u", - index, dir, ct->proto.tcp.last_index, timeout); - WRITE_ONCE(ct->timeout, timeout + nfct_time_stamp); + return true; } } else { ct->proto.tcp.last_index = index; ct->proto.tcp.last_dir = dir; } + + return false; } /* table of valid flag combinations - PUSH, ECE and CWR are always valid */ @@ -969,7 +1007,9 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, struct net *net = nf_ct_net(ct); struct nf_tcp_net *tn = nf_tcp_pernet(net); enum tcp_conntrack new_state, old_state; + struct nf_tcp_invalid_log log = {}; unsigned int index, *timeouts; + bool lowered_timeout = false; enum nf_ct_tcp_action res; enum ip_conntrack_dir dir; const struct tcphdr *th; @@ -1252,14 +1292,18 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, } res = tcp_in_window(ct, dir, index, - skb, dataoff, th, state); + skb, dataoff, th, &log); switch (res) { case NFCT_TCP_IGNORE: spin_unlock_bh(&ct->lock); + nf_tcp_log_invalid(skb, ct, state, &log); return NF_ACCEPT; case NFCT_TCP_INVALID: - nf_tcp_handle_invalid(ct, dir, index, skb, state); + lowered_timeout = nf_tcp_handle_invalid(ct, dir, index); spin_unlock_bh(&ct->lock); + nf_tcp_log_invalid(skb, ct, state, &log); + if (lowered_timeout) + nf_ct_l4proto_log_invalid(skb, ct, state, "lowered timeout to UNACK"); return -NF_ACCEPT; case NFCT_TCP_ACCEPT: break; -- cgit v1.2.3 From 33d1469b0124cc0baaea7a2032123b77a81e0940 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 3 Aug 2026 10:43:27 +0200 Subject: netfilter: nfnetlink_log: wait for rcu grace period before freeing pernet state sashiko reports: "nfnl_log_net_exit() calls nf_log_unset(), which clears the logger pointer without an RCU grace period. Immediately after, ops_free_list() frees the per-net state while concurrent packets might still be executing nf_log_packet() under rcu_read_lock()." Clear the pointer via .pre_exit to make sure rcu readers have completed before pernet storage is free'd. The change in nf_log_syslog.c is only done for consistency: it doesn't use pernet data. Link: https://sashiko.dev/#/patchset/20260731151806.849724-1-pablo%40netfilter.org Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_log_syslog.c | 4 ++-- net/netfilter/nfnetlink_log.c | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c index e37b09b3203b..5ffde27d450a 100644 --- a/net/netfilter/nf_log_syslog.c +++ b/net/netfilter/nf_log_syslog.c @@ -1014,7 +1014,7 @@ err1: return ret; } -static void __net_exit nf_log_syslog_net_exit(struct net *net) +static void __net_exit nf_log_syslog_net_pre_exit(struct net *net) { nf_log_unset(net, &nf_ip_logger); nf_log_unset(net, &nf_arp_logger); @@ -1025,7 +1025,7 @@ static void __net_exit nf_log_syslog_net_exit(struct net *net) static struct pernet_operations nf_log_syslog_net_ops = { .init = nf_log_syslog_net_init, - .exit = nf_log_syslog_net_exit, + .pre_exit = nf_log_syslog_net_pre_exit, }; static int __init nf_log_syslog_init(void) diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 5fee61b3813c..6c7fa2ed34f5 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -1170,21 +1170,26 @@ static int __net_init nfnl_log_net_init(struct net *net) return 0; } -static void __net_exit nfnl_log_net_exit(struct net *net) +static void __net_exit nfnl_log_net_pre_exit(struct net *net) { - struct nfnl_log_net *log = nfnl_log_pernet(net); - unsigned int i; - #ifdef CONFIG_PROC_FS remove_proc_entry("nfnetlink_log", net->nf.proc_netfilter); #endif nf_log_unset(net, &nfulnl_logger); +} + +static void __net_exit nfnl_log_net_exit(struct net *net) +{ + struct nfnl_log_net *log = nfnl_log_pernet(net); + unsigned int i; + for (i = 0; i < INSTANCE_BUCKETS; i++) WARN_ON_ONCE(!hlist_empty(&log->instance_table[i])); } static struct pernet_operations nfnl_log_net_ops = { .init = nfnl_log_net_init, + .pre_exit = nfnl_log_net_pre_exit, .exit = nfnl_log_net_exit, .id = &nfnl_log_net_id, .size = sizeof(struct nfnl_log_net), -- cgit v1.2.3 From e0ba936287dfe9783426aac27e5fd76fe35b38c9 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Tue, 4 Aug 2026 06:10:55 +0000 Subject: ipvs: clear IPv4 options after rebasing tunnel ICMP errors ip_vs_in_icmp() rebases an skb from the outer ICMP packet to the quoted original request before passing it to icmp_send(). However, IPCB(skb)->opt still describes the outer IPv4 header. A timestamp option in the outer header can therefore leave an offset that points into the quoted transport header after the rebase. __ip_options_echo() treats a byte at that stale location as the option length and copies it into the fixed-size option storage on the __icmp_send() stack, causing a stack out-of-bounds write. Clear the stale option metadata after resetting the network header. Keep the remaining control block fields, including the ingress interface used by the ICMP response path. Fixes: f2edb9f7706d ("ipvs: implement passive PMTUD for IPIP packets") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6-sol Codex:gpt-5.5-cyber Signed-off-by: Kyle Zeng Co-developed-by: David Lee Signed-off-by: David Lee Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 95af77b68851..a46e7acdd8e1 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1950,6 +1950,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (pskb_pull(skb, offset2) == NULL) goto ignore_tunnel; skb_reset_network_header(skb); + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); /* Ensure the IP header is present in headroom */ if (!pskb_may_pull(skb, hlen_orig)) goto ignore_tunnel; -- cgit v1.2.3 From d93660df4dd1d116f608ada4a29a80a5d6f0a6ed Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 6 Aug 2026 13:52:11 +0300 Subject: ipvs: revalidate ihl to prevent out-of-bounds access While the outer IP header is already pulled into the skb head, we must be careful and revalidate the embedded headers after reading them from the skb frags to prevent out-of-bounds access. One such place reported by Sashiko is ip_vs_nat_icmp() where local process can change the ihl field and after skb_ensure_writable() we can see larger value which is a problem for the ip_send_check(cih) calls. Add check to drop the packet if the ihl field is changed. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260730183506.87473-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_core.c | 11 +++++++++-- net/netfilter/ipvs/ip_vs_xmit.c | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index fc2ef5ef31a6..be3a6617adf4 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -2068,7 +2068,7 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) !(cp->flags & IP_VS_CONN_F_TEMPLATE); } -void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +bool ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir, unsigned int toff, bool has_ports, struct ip_vs_iphdr *ciph); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index a46e7acdd8e1..eb806813292a 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -923,7 +923,7 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, * Packet has been made sufficiently writable in caller * - inout: 1=in->out, 0=out->in */ -void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, +bool ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int inout, unsigned int toff, bool has_ports, struct ip_vs_iphdr *ciph) { @@ -931,6 +931,11 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); struct iphdr *cih = (struct iphdr *)(icmph + 1); + /* Before now we may used ihl from skb frag, revalidate it after + * copying it into skb head to prevent out-of-bounds access + */ + if (cih->ihl * 4 != ciph->len - ciph->off) + return false; if (inout) { iph->saddr = cp->vaddr.ip; ip_send_check(iph); @@ -964,6 +969,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, else IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off, "Forwarding altered incoming ICMP"); + return true; } #ifdef CONFIG_IP_VS_IPV6 @@ -1055,7 +1061,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb, ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); else #endif - ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph); + if (!ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph)) + goto out; if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) goto out; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index fc7403186394..04450a48f01a 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1580,7 +1580,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph); + if (!ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph)) + goto tx_error; /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; -- cgit v1.2.3 From d02f592064347e0c1e0d84f24941ad338838cc48 Mon Sep 17 00:00:00 2001 From: Alexey Velichayshiy Date: Thu, 6 Aug 2026 19:11:38 +0300 Subject: netfilter: nf_tables_offload: suppress WARN_ON_ONCE for ENOMEM in abort path In nft_flow_rule_offload_abort(), WARN_ON_ONCE(err) is triggered on every error during rollback, including -ENOMEM. Memory allocation failures are expected under low-memory conditions and do not indicate a kernel bug. Trace for example: nft_flow_offload_chain() // FLOW_BLOCK_BIND nft_flow_block_chain() nft_chain_offload_cmd() nft_block_offload_cmd() ->ndo_setup_tc() nsim_setup_tc() flow_block_cb_setup_simple() flow_block_cb_alloc() // fails to -ENOMEM The warning was reproduced on the 5.10 stable kernel under memory pressure via fault injection, but the underlying bug exists in mainline as well, as demonstrated by the ENOMEM trace above. The following splat was triggered during nf_tables transaction processing: WARNING: CPU: 0 PID: 8567 at net/netfilter/nf_tables_offload.c:532 nft_flow_rule_offload_abort net/netfilter/nf_tables_offload.c:532 [inline] WARNING: CPU: 0 PID: 8567 at net/netfilter/nf_tables_offload.c:532 nft_flow_rule_offload_commit+0x971/0xcd0 net/netfilter/nf_tables_offload.c:591 Modules linked in: CPU: 0 PID: 8567 Comm: syz-executor.0 Not tainted 5.10.260-syzkaller #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 RIP: 0010:nft_flow_rule_offload_abort net/netfilter/nf_tables_offload.c:532 [inline] RIP: 0010:nft_flow_rule_offload_commit+0x971/0xcd0 net/netfilter/nf_tables_offload.c:591 Call Trace: nf_tables_commit+0x3bd/0x4bd0 net/netfilter/nf_tables_api.c:8604 nfnetlink_rcv_batch+0xb1e/0x1f20 net/netfilter/nfnetlink.c:509 nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:579 [inline] nfnetlink_rcv+0x3b3/0x420 net/netfilter/nfnetlink.c:597 netlink_unicast_kernel net/netlink/af_netlink.c:1314 [inline] netlink_unicast+0x6cd/0xa00 net/netfilter/af_netlink.c:1340 netlink_sendmsg+0x906/0xe10 net/netfilter/af_netlink.c:1919 sock_sendmsg_nosec net/socket.c:651 [inline] __sock_sendmsg+0x155/0x190 net/socket.c:663 ____sys_sendmsg+0x705/0x870 net/socket.c:2379 ___sys_sendmsg+0x100/0x170 net/socket.c:2433 __sys_sendmsg+0xe9/0x1c0 net/socket.c:2462 do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x67/0xd1 Change the condition to WARN_ON_ONCE(err && err != -ENOMEM) so that warnings are only emitted for unexpected errors. This aligns with the common kernel practice of not warning on -ENOMEM. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: 63b48c73ff56 ("netfilter: nf_tables_offload: undo updates if transaction fails") Signed-off-by: Alexey Velichayshiy Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c index 8998a24651ff..0ac3c26dfb3d 100644 --- a/net/netfilter/nf_tables_offload.c +++ b/net/netfilter/nf_tables_offload.c @@ -558,7 +558,7 @@ static void nft_flow_rule_offload_abort(struct net *net, break; } - if (WARN_ON_ONCE(err)) + if (WARN_ON_ONCE(err && err != -ENOMEM)) break; } } -- cgit v1.2.3 From 2014ac62df9d45bb9a004a043e85df7be09ed780 Mon Sep 17 00:00:00 2001 From: Jérémy Jean Date: Sat, 8 Aug 2026 12:40:02 +0000 Subject: netfilter: flowtable: publish GC-visible tuple last MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nf_flow_table_iterate() only treats original-direction tuple nodes as owning entries. Publishing the original node first lets GC observe and free a flow while flow_offload_add() is still inserting the reply node. Publish the reply node first and the original node last so GC never sees a partially installed flow. KASAN can trigger slab-use-after-free read and write reports in the flowtable/rhashtable path (rht_deferred_worker, jhash, flow_offload_del, flow_offload_lookup, etc.). Fixes: ac2a66665e23 ("netfilter: add generic flow table infrastructure") Signed-off-by: Jérémy Jean Assisted-by: Codex:gpt-5 Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_flow_table_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index b66e65439341..8b1165f2b5a4 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -332,17 +332,18 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow); err = rhashtable_insert_fast(&flow_table->rhashtable, - &flow->tuplehash[0].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, nf_flow_offload_rhash_params); if (err < 0) return err; + /* GC only iterates original-direction entries; publish original last. */ err = rhashtable_insert_fast(&flow_table->rhashtable, - &flow->tuplehash[1].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node, nf_flow_offload_rhash_params); if (err < 0) { rhashtable_remove_fast(&flow_table->rhashtable, - &flow->tuplehash[0].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, nf_flow_offload_rhash_params); return err; } -- cgit v1.2.3 From 4cbd69766b35a089664cadb1f613bb85f7ef77a9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 6 Aug 2026 15:53:41 +0200 Subject: netfilter: ipset: fix list type element drift bug If list_set_uadd() calls list_set_replace() to swap an expired entry, the element count remains the same, therefore the increment must be elided. Fixes: 702b71e7c666 ("netfilter: ipset: Add element count to all set types header") Link: https://sashiko.dev/#/patchset/20260806101947.2802-1-fw%40strlen.de Signed-off-by: Florian Westphal Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_list_set.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index ca3ef9479e83..0bc2370773af 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -301,9 +301,12 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, e->set = set; INIT_LIST_HEAD(&e->list); list_set_init_extensions(set, ext, e); - if (n) + if (n) { list_set_replace(set, e, n); - else if (next) + return 0; + } + + if (next) list_add_tail_rcu(&e->list, &next->list); else if (prev) list_add_rcu(&e->list, &prev->list); -- cgit v1.2.3 From 490937b88cb592cc0c5367758edd700fd5abd15c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 6 Aug 2026 16:17:50 +0200 Subject: netfilter: ipset: let destroy callbacks adjust ext mem size For bitmap this change makes no difference, because destructors are called synchronously. List type however calls them via call_rcu() so accounting decrement can happen after list_set_flush() set ext_size to 0. 'set->elements = 0' can be removed for the same reason in the list type case, it calls 'set->elements--' for each element. Fixes: 9e41f26a505c ("netfilter: ipset: Count non-static extension memory for userspace") Suggested-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_bitmap_gen.h | 2 +- net/netfilter/ipset/ip_set_list_set.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 226fdf17b683..d6a7e6604542 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set) mtype_ext_cleanup(set); bitmap_zero(map->members, map->elements); set->elements = 0; - atomic64_set(&set->ext_size, 0); + DEBUG_NET_WARN_ON_ONCE(atomic64_read(&set->ext_size) > 0); } /* Calculate the actual memory size of the set data */ diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 0bc2370773af..f070088742d6 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -423,8 +423,7 @@ list_set_flush(struct ip_set *set) list_for_each_entry_safe(e, n, &map->members, list) list_set_del(set, e); - set->elements = 0; - atomic64_set(&set->ext_size, 0); + DEBUG_NET_WARN_ON_ONCE(set->elements > 0); } static void -- cgit v1.2.3 From c703f62c30f2db7f40ad575f0034636d8a401fac Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Aug 2026 12:31:32 -0700 Subject: eth: bnxt: cancel IRQ notifier before freeing affinity mask bnxt_irq_affinity_notify() copies into irq->cpu_mask. Cancel the notifier before freeing irq->cpu_mask. Fixes: c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver") Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260803193135.2030368-2-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 25099077fe4f..98c5d6d4b2cf 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11878,14 +11878,14 @@ static void bnxt_free_irq(struct bnxt *bp) irq = &bp->irq_tbl[map_idx]; if (irq->requested) { + bnxt_release_irq_notifier(irq); + if (irq->have_cpumask) { irq_update_affinity_hint(irq->vector, NULL); free_cpumask_var(irq->cpu_mask); irq->have_cpumask = 0; } - bnxt_release_irq_notifier(irq); - free_irq(irq->vector, bp->bnapi[i]); } -- cgit v1.2.3 From 4b5cb58a4443fff67aa18a0d7b645b2220f2fcf8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Aug 2026 12:31:33 -0700 Subject: eth: bnxt: keep the aRFS rmap updated when TPH is enabled The TPH support must have broken aRFS in bnxt. IRQ can only have one notifier, so installing the TPH notifier is overriding the one implicitly installed by irq_cpu_rmap_add(). Make sure we call cpu_rmap_update() from the TPH notifier. We need to be careful with the ordering and not free the rmap until we unregistered the notifier. Note that moving the rmap freeing after the early return in bnxt_free_irq() is fine - there's no path that could leave rmap with irq_tbl being NULL. Fixes: c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver") Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260803193135.2030368-3-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 98c5d6d4b2cf..505d1afea71c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11793,6 +11793,16 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, irq = container_of(notify, struct bnxt_irq, affinity_notify); +#ifdef CONFIG_RFS_ACCEL + if (irq->bp->dev->rx_cpu_rmap && irq->ring_nr < irq->bp->rx_nr_rings) { + err = cpu_rmap_update(irq->bp->dev->rx_cpu_rmap, irq->ring_nr, + mask); + if (err) + netdev_warn(irq->bp->dev, + "aRFS rmap update failed: %d\n", err); + } +#endif + if (!irq->bp->tph_mode) return; @@ -11866,10 +11876,6 @@ static void bnxt_free_irq(struct bnxt *bp) struct bnxt_irq *irq; int i; -#ifdef CONFIG_RFS_ACCEL - free_irq_cpu_rmap(bp->dev->rx_cpu_rmap); - bp->dev->rx_cpu_rmap = NULL; -#endif if (!bp->irq_tbl || !bp->bnapi) return; @@ -11895,6 +11901,11 @@ static void bnxt_free_irq(struct bnxt *bp) /* Disable TPH support */ pcie_disable_tph(bp->pdev); bp->tph_mode = 0; + +#ifdef CONFIG_RFS_ACCEL + free_irq_cpu_rmap(bp->dev->rx_cpu_rmap); + bp->dev->rx_cpu_rmap = NULL; +#endif } static int bnxt_request_irq(struct bnxt *bp) -- cgit v1.2.3 From 0fd562c7b9e48e7958f7405e54bf98e8e22ad184 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Aug 2026 12:31:34 -0700 Subject: eth: bnxt: decrease indent in bnxt_request_irq() bnxt_request_irq() has unnecessary level of indentation. Use continue instead. No need to re-fetch NUMA node for each IRQ, move to the function level. No functional changes. Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260803193135.2030368-4-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 505d1afea71c..b06cd8fd19dd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11910,6 +11910,7 @@ static void bnxt_free_irq(struct bnxt *bp) static int bnxt_request_irq(struct bnxt *bp) { + const int numa_node = dev_to_node(&bp->pdev->dev); struct cpu_rmap *rmap = NULL; int i, j, rc = 0; unsigned long flags = 0; @@ -11932,6 +11933,7 @@ static int bnxt_request_irq(struct bnxt *bp) for (i = 0, j = 0; i < bp->cp_nr_rings; i++) { int map_idx = bnxt_cp_num_to_irq_num(bp, i); struct bnxt_irq *irq = &bp->irq_tbl[map_idx]; + u16 tag; if (IS_ENABLED(CONFIG_RFS_ACCEL) && rmap && bp->bnapi[i]->rx_ring) { @@ -11950,33 +11952,31 @@ static int bnxt_request_irq(struct bnxt *bp) netif_napi_set_irq_locked(&bp->bnapi[i]->napi, irq->vector); irq->requested = 1; - if (zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) { - int numa_node = dev_to_node(&bp->pdev->dev); - u16 tag; + if (!zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) + continue; - irq->have_cpumask = 1; - irq->msix_nr = map_idx; - irq->ring_nr = i; - cpumask_set_cpu(cpumask_local_spread(i, numa_node), - irq->cpu_mask); - rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask); - if (rc) { - netdev_warn(bp->dev, - "Update affinity hint failed, IRQ = %d\n", - irq->vector); - break; - } + irq->have_cpumask = 1; + irq->msix_nr = map_idx; + irq->ring_nr = i; + cpumask_set_cpu(cpumask_local_spread(i, numa_node), + irq->cpu_mask); + rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask); + if (rc) { + netdev_warn(bp->dev, + "Update affinity hint failed, IRQ = %d\n", + irq->vector); + break; + } - bnxt_register_irq_notifier(bp, irq); + bnxt_register_irq_notifier(bp, irq); - /* Init ST table entry */ - if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM, - cpumask_first(irq->cpu_mask), - &tag)) - continue; + /* Init ST table entry */ + if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM, + cpumask_first(irq->cpu_mask), + &tag)) + continue; - pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag); - } + pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag); } return rc; } -- cgit v1.2.3 From 51e96fa31f7e7eac2cba8f854e24d36600cc040b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 3 Aug 2026 12:31:35 -0700 Subject: eth: bnxt: avoid deadlock when canceling IRQ affinity notifier Unregistering IRQ affinity notifiers waits for the callback synchronously. bnxt takes the netdev instance lock in the notifier (to restart the queue) and cancels the work under the same lock. This may obviously deadlock. Move the restart to the async service task. The queue restart isn't super time sensitive. Store the new TPH tag, schedule the task. Safely canceling the service task is already ironed out. In bnxt_request_irq() the order of registering notifier, affinity and initial TPH programming has to be inverted. I think it was racy previously since user may trigger an update as soon as notifier is installed. There's a small known gap - if pcie_tph_get_cpu_st() fails at init and the target tag is 0 we may miss programming the entry. This does not seem worth fixing, the code has skip-on-failure all over the place, anyway. Fixes: c214410c47d6 ("bnxt_en: Add TPH support in BNXT driver") Tested-by: Vishvambar Panth S Link: https://patch.msgid.link/20260803193135.2030368-5-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 86 ++++++++++++++++++++++--------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 3 ++ 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index b06cd8fd19dd..bc7b37cb74a7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11789,12 +11789,13 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, { struct bnxt_irq *irq; u16 tag; - int err; irq = container_of(notify, struct bnxt_irq, affinity_notify); #ifdef CONFIG_RFS_ACCEL if (irq->bp->dev->rx_cpu_rmap && irq->ring_nr < irq->bp->rx_nr_rings) { + int err; + err = cpu_rmap_update(irq->bp->dev->rx_cpu_rmap, irq->ring_nr, mask); if (err) @@ -11812,20 +11813,11 @@ static void bnxt_irq_affinity_notify(struct irq_affinity_notify *notify, return; if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM, - cpumask_first(irq->cpu_mask), &tag)) + cpumask_first(mask), &tag)) return; - if (pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag)) - return; - - netdev_lock(irq->bp->dev); - if (netif_running(irq->bp->dev)) { - err = netdev_rx_queue_restart(irq->bp->dev, irq->ring_nr); - if (err) - netdev_err(irq->bp->dev, - "RX queue restart failed: err=%d\n", err); - } - netdev_unlock(irq->bp->dev); + WRITE_ONCE(irq->new_tag, tag); + bnxt_queue_sp_work(irq->bp, BNXT_TPH_UPDATE_SP_EVENT); } static void bnxt_irq_affinity_release(struct kref *ref) @@ -11896,6 +11888,8 @@ static void bnxt_free_irq(struct bnxt *bp) } irq->requested = 0; + irq->tag = 0; + irq->new_tag = 0; } /* Disable TPH support */ @@ -11933,6 +11927,7 @@ static int bnxt_request_irq(struct bnxt *bp) for (i = 0, j = 0; i < bp->cp_nr_rings; i++) { int map_idx = bnxt_cp_num_to_irq_num(bp, i); struct bnxt_irq *irq = &bp->irq_tbl[map_idx]; + unsigned int cpu_num; u16 tag; if (IS_ENABLED(CONFIG_RFS_ACCEL) && @@ -11958,8 +11953,19 @@ static int bnxt_request_irq(struct bnxt *bp) irq->have_cpumask = 1; irq->msix_nr = map_idx; irq->ring_nr = i; - cpumask_set_cpu(cpumask_local_spread(i, numa_node), - irq->cpu_mask); + cpu_num = cpumask_local_spread(i, numa_node); + cpumask_set_cpu(cpu_num, irq->cpu_mask); + + /* Init ST table entry if we can get the mapping */ + if (!pcie_tph_get_cpu_st(bp->pdev, TPH_MEM_TYPE_VM, + cpu_num, &tag)) { + pcie_tph_set_st_entry(bp->pdev, irq->msix_nr, tag); + irq->tag = tag; + irq->new_tag = tag; + } + + bnxt_register_irq_notifier(bp, irq); + rc = irq_update_affinity_hint(irq->vector, irq->cpu_mask); if (rc) { netdev_warn(bp->dev, @@ -11967,16 +11973,6 @@ static int bnxt_request_irq(struct bnxt *bp) irq->vector); break; } - - bnxt_register_irq_notifier(bp, irq); - - /* Init ST table entry */ - if (pcie_tph_get_cpu_st(irq->bp->pdev, TPH_MEM_TYPE_VM, - cpumask_first(irq->cpu_mask), - &tag)) - continue; - - pcie_tph_set_st_entry(irq->bp->pdev, irq->msix_nr, tag); } return rc; } @@ -14479,6 +14475,43 @@ static void bnxt_rtnl_unlock_sp(struct bnxt *bp) rtnl_unlock(); } +static void bnxt_tph_update(struct bnxt *bp) +{ + struct net_device *dev = bp->dev; + int i; + + bnxt_lock_sp(bp); + if (!test_bit(BNXT_STATE_OPEN, &bp->state)) + goto unlock; + + for (i = 0; i < bp->rx_nr_rings; i++) { + struct bnxt_irq *irq; + int map_idx, err; + u16 tag; + + map_idx = bnxt_cp_num_to_irq_num(bp, i); + irq = &bp->irq_tbl[map_idx]; + tag = READ_ONCE(irq->new_tag); + if (irq->tag == tag) + continue; + + if (pcie_tph_set_st_entry(bp->pdev, irq->msix_nr, tag)) + continue; + + err = netdev_rx_queue_restart(dev, irq->ring_nr); + if (err) { + netdev_err(dev, "RX queue restart failed: err=%d\n", + err); + continue; + } + + irq->tag = tag; + } + +unlock: + bnxt_unlock_sp(bp); +} + /* Only called from bnxt_sp_task() */ static void bnxt_reset(struct bnxt *bp, bool silent) { @@ -14903,6 +14936,9 @@ static void bnxt_sp_task(struct work_struct *work) bnxt_devlink_health_fw_report(bp); } + if (test_and_clear_bit(BNXT_TPH_UPDATE_SP_EVENT, &bp->sp_event)) + bnxt_tph_update(bp); + smp_mb__before_atomic(); clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 44ea8fc7b766..dc8ec5e5733e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1268,6 +1268,8 @@ struct bnxt_irq { struct bnxt *bp; int msix_nr; int ring_nr; + u16 tag; + u16 new_tag; struct irq_affinity_notify affinity_notify; }; @@ -2642,6 +2644,7 @@ struct bnxt { #define BNXT_RING_COAL_NOW_SP_EVENT 17 #define BNXT_FW_RESET_NOTIFY_SP_EVENT 18 #define BNXT_FW_EXCEPTION_SP_EVENT 19 +#define BNXT_TPH_UPDATE_SP_EVENT 20 #define BNXT_LINK_CFG_CHANGE_SP_EVENT 21 #define BNXT_THERMAL_THRESHOLD_SP_EVENT 22 #define BNXT_FW_ECHO_REQUEST_SP_EVENT 23 -- cgit v1.2.3 From e16e960d55a40d36bd7c2494cc005e757dc9a1ef Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 6 Aug 2026 10:38:57 +0000 Subject: ipvlan: inherit needed_headroom and needed_tailroom from phy_dev ipvlan devices inherit hard_header_len from phy_dev during ipvlan_init(), but leave needed_headroom and needed_tailroom set to 0. When the underlying phy_dev (or stacked lower device) requires extra headroom or tailroom for headers/trailers (e.g. macsec, ipsec, wireguard, tunnels, or veth with rx headroom), upper layers calculating packet headroom and tailroom fail to reserve sufficient space. This can result in reallocation overhead, skb headroom underflows, or KASAN slab-use-after-free crashes when dev_hard_header() / ipvlan_hard_header() prepends header data or when lower devices append tailroom. Fix this by: 1. Inheriting needed_headroom and needed_tailroom from phy_dev in ipvlan_init(). 2. Propagating needed_headroom and needed_tailroom updates to attached ipvlans in ipvlan_device_event() when receiving NETDEV_FEAT_CHANGE events. Fixes: 2ad7bf363841 ("ipvlan: Initial check-in of the IPVLAN driver.") Reported-by: syzbot+1f9fd0f4b601cf88d6e6@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a720a21.40259c87.584f4.04bb.GAE@google.com/T/#u Reported-by: Tangxin Xie Closes: https://lore.kernel.org/netdev/CANn89i+1EW-sFNK8xoq98gMbPCeLS7e=+rs9gHfLg5Wj+4x0sw@mail.gmail.com/T/#mcc6307f115e500df23ea2980d5669fe95f20b6b4 Signed-off-by: Eric Dumazet Reviewed-by: Hangbin Liu Link: https://patch.msgid.link/20260806103857.115541-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ipvlan/ipvlan_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index ed46439a9f4e..92d3d340281f 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -146,6 +146,8 @@ static int ipvlan_init(struct net_device *dev) dev->lltx = true; netif_inherit_tso_max(dev, phy_dev); dev->hard_header_len = phy_dev->hard_header_len; + dev->needed_headroom = phy_dev->needed_headroom; + dev->needed_tailroom = phy_dev->needed_tailroom; netdev_lockdep_set_classes(dev); @@ -773,6 +775,8 @@ static int ipvlan_device_event(struct notifier_block *unused, case NETDEV_FEAT_CHANGE: list_for_each_entry(ipvlan, &port->ipvlans, pnode) { netif_inherit_tso_max(ipvlan->dev, dev); + ipvlan->dev->needed_headroom = dev->needed_headroom; + ipvlan->dev->needed_tailroom = dev->needed_tailroom; netdev_update_features(ipvlan->dev); } break; -- cgit v1.2.3 From cef51860becd9700217c81732ca1eb1ea6ed6fe1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 6 Aug 2026 14:19:38 +0000 Subject: macvlan: inherit needed_headroom and needed_tailroom from lowerdev macvlan devices inherit hard_header_len from lowerdev during macvlan_init(), but leave needed_headroom and needed_tailroom set to 0. When the underlying lowerdev requires extra headroom or tailroom for headers/trailers (e.g. macsec, ipsec, wireguard, tunnels, or veth with rx headroom), upper layers calculating packet headroom and tailroom fail to reserve sufficient space. This can result in reallocation overhead, skb headroom underflows, or KASAN slab-use-after-free crashes when dev_hard_header() / macvlan_hard_header() prepends header data or when lower devices append tailroom. Fix this by: 1. Inheriting needed_headroom and needed_tailroom from lowerdev in macvlan_init(). 2. Propagating needed_headroom and needed_tailroom updates to attached macvlans in macvlan_device_event() when receiving NETDEV_FEAT_CHANGE events. Fixes: b863ceb7ddce ("[NET]: Add macvlan driver") Reported-by: Tangxin Xie Closes: https://lore.kernel.org/netdev/CANn89i+1EW-sFNK8xoq98gMbPCeLS7e=+rs9gHfLg5Wj+4x0sw@mail.gmail.com/T/#m16adf0ff972cbfd8066c3a8e656e75eaeb12d021 Signed-off-by: Eric Dumazet Reviewed-by: Hangbin Liu Link: https://patch.msgid.link/20260806141938.287660-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/macvlan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index c40fa331836b..ff2f4bb651b7 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -950,6 +950,8 @@ static int macvlan_init(struct net_device *dev) dev->lltx = true; netif_inherit_tso_max(dev, lowerdev); dev->hard_header_len = lowerdev->hard_header_len; + dev->needed_headroom = lowerdev->needed_headroom; + dev->needed_tailroom = lowerdev->needed_tailroom; macvlan_set_lockdep_class(dev); vlan->pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats); @@ -1824,6 +1826,8 @@ static int macvlan_device_event(struct notifier_block *unused, case NETDEV_FEAT_CHANGE: list_for_each_entry(vlan, &port->vlans, list) { netif_inherit_tso_max(vlan->dev, dev); + vlan->dev->needed_headroom = dev->needed_headroom; + vlan->dev->needed_tailroom = dev->needed_tailroom; netdev_update_features(vlan->dev); } break; -- cgit v1.2.3 From 92465f46f3c3a92e4758af20d2363b7adc43e1f5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 6 Aug 2026 15:56:26 -0700 Subject: net: expect instance lock in netdev_queue_get_dma_dev() netdev_queue_get_dma_dev() uses "compat" locking assert which wants either the rtnl_lock or netdev instance lock. This is not right, the callers are taking the instance lock unconditionally. All entry points for queue config are purely instance locked. In other words the callers use netdev_get_by_index_lock(), not netdev_get_by_index_lock_ops_compat(). All the state we will access is effectively instance lock protected (it's const for devices which are not ops-locked). Update the assert to avoid false positive warnings. Cc: stable@vger.kernel.org Fixes: b6c5f9454ef34 ("io_uring/zcrx: call netdev_queue_get_dma_dev() under instance lock") Reported-by: syzbot+a78926bdac2adb52dc0e@syzkaller.appspotmail.com Reviewed-by: Simon Horman Acked-by: Daniel Borkmann Link: https://patch.msgid.link/20260806225627.3998672-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/core/netdev_queues.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/netdev_queues.c b/net/core/netdev_queues.c index 4d6864bb4f6b..f5558b12877c 100644 --- a/net/core/netdev_queues.c +++ b/net/core/netdev_queues.c @@ -40,7 +40,7 @@ struct device *netdev_queue_get_dma_dev(struct net_device *dev, struct netdev_rx_queue *hw_rxq; struct device *dma_dev; - netdev_assert_locked_ops_compat(dev); + netdev_assert_locked(dev); /* Only RX side supports queue leasing today. */ if (type != NETDEV_QUEUE_TYPE_RX || !netif_rxq_is_leased(dev, idx)) -- cgit v1.2.3 From 60db47f02bfa2aa688938aa199117ec4f8e31d23 Mon Sep 17 00:00:00 2001 From: Jonas Köppeler Date: Thu, 6 Aug 2026 13:43:49 +0200 Subject: veth: fix queue index used to wake the peer txq in veth_poll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit veth_poll() derives the index of the peer TX queue to wake from rq->xdp_rxq.queue_index. That field is only initialized by xdp_rxq_info_reg() in veth_enable_xdp_range(), which runs only when an XDP program is attached. On the plain GRO/NAPI path (veth_napi_enable_range()) xdp_rxq_info_reg() is never called, so queue_index stays 0 for every queue, as priv->rq is zero-allocated. So in a multi-queue setup with GRO enabled and no XDP program attached, every NAPI instance looks at the peer's TX queue 0. If veth_xmit() stops peer TX queue 1 because the ptr_ring is full (NETDEV_TX_BUSY), nothing ever wakes it again: the poller draining queue 1 wakes queue 0 instead. veth implements no ndo_tx_timeout, so the netdev watchdog does not kick in either, and the queue stays stopped indefinitely. Derive the index from the position of the rq within priv->rq instead, which is correct regardless of whether XDP was ever enabled. Scripts to reproduce the stall are available at https://github.com/netoptimizer/veth-backpressure-performance-testing Fixes: dc82a33297fc ("veth: apply qdisc backpressure on full ptr_ring to reduce TX drops") Signed-off-by: Jonas Köppeler Tested-by: Jesper Dangaard Brouer Acked-by: Jesper Dangaard Brouer Link: https://patch.msgid.link/20260806-veth-fix-poll-queue-idx-v1-1-c5357fb7573d@tu-berlin.de Signed-off-by: Jakub Kicinski --- drivers/net/veth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index f80f6584a6ff..6ab84c837a33 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -967,7 +967,7 @@ static int veth_poll(struct napi_struct *napi, int budget) struct veth_rq *rq = container_of(napi, struct veth_rq, xdp_napi); struct veth_priv *priv = netdev_priv(rq->dev); - int queue_idx = rq->xdp_rxq.queue_index; + int queue_idx = rq - priv->rq; struct netdev_queue *peer_txq; struct veth_stats stats = {}; struct net_device *peer_dev; -- cgit v1.2.3 From 883b56ae58fe657d8497806c7059646e9ba6dbd0 Mon Sep 17 00:00:00 2001 From: Hyunjung Ko Date: Thu, 6 Aug 2026 19:12:52 +0900 Subject: net/sched: act_gact, act_police: range check the fallback control action tcf_action_check_ctrlact() range checks the primary control action: if (!opcode) ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0; TC_ACT_VALUE_MAX is TC_ACT_TRAP, so kernel-internal verdicts above it cannot be set that way. But act_gact and act_police each carry a second, independent control action supplied by user space that never reaches that helper - TCA_GACT_PROB.paction and TCA_POLICE_RESULT. Both only reject TC_ACT_GOTO_CHAIN, so any other value is stored verbatim and returned verbatim from the action. In particular user space can store TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is deliberately not part of the UAPI value range. That verdict tells every caller the action took ownership of the skb, so nobody frees it: sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for it. The result is one leaked sk_buff plus its data buffer per packet traversing the filter, unbounded, for all traffic on the chain including kernel-generated packets. Both are trivially deterministic. act_gact clamps tcfg_pval to >= 1, so with pval = 1 gact_determ() returns the fallback for every packet. act_police has no mandatory rate, so rate = 0 leaves tcfp_mtu = ~0 and tcf_police_mtu_check() always passes. TC_ACT_CONSUMED was added by commit 720f22fed81b ("net: sched: refactor reinsert action"), after both goto-chain guards were written: commit 9469f375ab09 ("net/sched: act_gact: disallow 'goto chain' on fallback control action") and commit c08f5ed5d625 ("net/sched: act_police: disallow 'goto chain' on fallback control action"). Neither guard was widened when the new verdict appeared. Factor the existing range test out of tcf_action_check_ctrlact() as tcf_action_valid() and apply it to both fallbacks. The helper cannot call tcf_action_check_ctrlact() directly because that also allocates a goto_chain, which is exactly what these two sites must not do. Reproduced on v7.2-rc6: kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet. With this patch both configurations are rejected with -EINVAL and kmemleak reports none. Fixes: 720f22fed81b ("net: sched: refactor reinsert action") Cc: stable@vger.kernel.org # v5.3+ Signed-off-by: Hyunjung Ko Acked-by: Jamal Hadi Salim Tested-by: Victor Nogueira Link: https://patch.msgid.link/20260806101252.809593-1-hj351016@gmail.com Signed-off-by: Jakub Kicinski --- include/net/act_api.h | 19 +++++++++++++++++++ net/sched/act_gact.c | 5 +++++ net/sched/act_police.c | 6 ++++++ 3 files changed, 30 insertions(+) diff --git a/include/net/act_api.h b/include/net/act_api.h index 20d9e55f8564..fd03f6319e88 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -270,6 +270,25 @@ int tcf_action_check_ctrlact(int action, struct tcf_proto *tp, struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action, struct tcf_chain *newchain); +/* Range check for a control action supplied by user space. + * + * This is the same test tcf_action_check_ctrlact() applies to the primary + * control action, factored out for the *fallback* control actions + * (act_gact's TCA_GACT_PROB.paction and act_police's TCA_POLICE_RESULT), + * which must not reach tcf_action_check_ctrlact() because they have no + * goto_chain to allocate. Without it, user space can store kernel-internal + * verdicts such as TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is + * deliberately not part of the UAPI value range. + */ +static inline bool tcf_action_valid(int action) +{ + int opcode = TC_ACT_EXT_OPCODE(action); + + if (!opcode) + return action <= TC_ACT_VALUE_MAX; + return opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC; +} + #ifdef CONFIG_INET DECLARE_STATIC_KEY_FALSE(tcf_frag_xmit_count); #endif diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index e949280eb800..565860cccba6 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -89,6 +89,11 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, p_parm = nla_data(tb[TCA_GACT_PROB]); if (p_parm->ptype >= MAX_RAND) return -EINVAL; + if (!tcf_action_valid(p_parm->paction)) { + NL_SET_ERR_MSG(extack, + "invalid fallback control action"); + return -EINVAL; + } if (TC_ACT_EXT_CMP(p_parm->paction, TC_ACT_GOTO_CHAIN)) { NL_SET_ERR_MSG(extack, "goto chain not allowed on fallback"); diff --git a/net/sched/act_police.c b/net/sched/act_police.c index b16468a98c55..ce08f6840ef7 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -128,6 +128,12 @@ static int tcf_police_init(struct net *net, struct nlattr *nla, if (tb[TCA_POLICE_RESULT]) { tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]); + if (!tcf_action_valid(tcfp_result)) { + NL_SET_ERR_MSG(extack, + "invalid fallback control action"); + err = -EINVAL; + goto failure; + } if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) { NL_SET_ERR_MSG(extack, "goto chain not allowed on fallback"); -- cgit v1.2.3 From 60837e4b840a9c3f7ec826e3584df0bc6542a2c2 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Fri, 7 Aug 2026 09:44:36 +0800 Subject: tcp: fix icsk_ack.ato bitfield overflow On cross-region connections we observed delayed ACKs suddenly turning into immediate ACKs plus a TCP_MAX_QUICKACKS burst, as if the connection had just received its first data segment. Commit 95b9a87c6a6b ("tcp: record last received ipv6 flowlabel") squeezed icsk_ack.ato into 8 bits, sized for TCP_DELACK_MAX. But both writers still bound ato by icsk_rto, which can be well above 255 jiffies, so the bitfield assignment silently wraps mod 256: repeated delack timer misses double ato up to icsk_rto, storing 320 as 64 and 256 as 0, and ato == 0 is the "first data packet" sentinel in tcp_event_data_recv(). Clamp both writers to TCP_DELACK_MAX, which the static_assert already guarantees to fit and tcp_send_delayed_ack() effectively caps ato at anyway. Fixes: 95b9a87c6a6b ("tcp: record last received ipv6 flowlabel") Signed-off-by: Jiayuan Chen Reviewed-by: Neal Cardwell Link: https://patch.msgid.link/20260807014437.36687-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_input.c | 6 +++--- net/ipv4/tcp_timer.c | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5b6378b94701..0f60a1dbf927 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1039,9 +1039,9 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb) /* The fastest case is the first. */ icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2; } else if (m < icsk->icsk_ack.ato) { - icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m; - if (icsk->icsk_ack.ato > icsk->icsk_rto) - icsk->icsk_ack.ato = icsk->icsk_rto; + icsk->icsk_ack.ato = min3((icsk->icsk_ack.ato >> 1) + (u32)m, + icsk->icsk_rto, + (u32)TCP_DELACK_MAX); } else if (m > icsk->icsk_rto) { /* Too long gap. Apparently sender failed to * restart window, so that we send ACKs quickly. diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index bf171b5e1eb3..4df1c9745c5d 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -334,7 +334,9 @@ void tcp_delack_timer_handler(struct sock *sk) if (inet_csk_ack_scheduled(sk)) { if (!inet_csk_in_pingpong_mode(sk)) { /* Delayed ACK missed: inflate ATO. */ - icsk->icsk_ack.ato = min_t(u32, icsk->icsk_ack.ato << 1, icsk->icsk_rto); + icsk->icsk_ack.ato = min3((u32)icsk->icsk_ack.ato << 1, + icsk->icsk_rto, + (u32)TCP_DELACK_MAX); } else { /* Delayed ACK missed: leave pingpong mode and * deflate ATO. -- cgit v1.2.3 From 202fef9bbbf5784487eec27581389c6fb97c350d Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 6 Aug 2026 15:47:16 +0200 Subject: net: phy: realtek: fix EEE advertisement write on the internal PHY MMD path In rtlgen_write_mmd(), the MDIO_AN_EEE_ADV case swaps the arguments to rtlgen_write_vend2(): it passes the MMD register number as the OCP address and the OCP address constant as the value. The caller's value is discarded and the write lands on the wrong register, so the EEE advertisement cannot be configured on the affected PHYs. Mirror rtlgen_read_mmd() and write the value to RTL_MDIO_AN_EEE_ADV. Fixes: da681ed73fb9 ("net: phy: realtek: improve mmd register access for internal PHY's") Signed-off-by: Oleksij Rempel Reviewed-by: Andrew Lunn Reviewed-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260806134716.3511821-1-o.rempel@pengutronix.de Signed-off-by: Jakub Kicinski --- drivers/net/phy/realtek/realtek_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index b65d0f5fa1a0..0d2321bd18c8 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -1370,7 +1370,7 @@ static int rtlgen_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, if (devnum == MDIO_MMD_VEND2) ret = rtlgen_write_vend2(phydev, regnum, val); else if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) - ret = rtlgen_write_vend2(phydev, regnum, RTL_MDIO_AN_EEE_ADV); + ret = rtlgen_write_vend2(phydev, RTL_MDIO_AN_EEE_ADV, val); else ret = -EOPNOTSUPP; -- cgit v1.2.3 From 8a7ed561671aa6a911a2de99e59ef670a4d0b1df Mon Sep 17 00:00:00 2001 From: Hyunjung Ko Date: Thu, 6 Aug 2026 19:12:34 +0900 Subject: net/sched: act_ct: fix sk_buff leak when the header checks reject a packet tcf_ct_handle_fragments() runs its header sanity checks before handing anything to the defragmentation engine: if (family == NFPROTO_IPV4) err = tcf_ct_ipv4_is_fragment(skb, &frag); else err = tcf_ct_ipv6_is_fragment(skb, &frag); if (err || !frag) return err; tcf_ct_ipv4_is_fragment() returns -EINVAL or -ENOMEM; tcf_ct_ipv6_is_fragment() adds -EPROTO when ipv6_find_hdr() fails. None of them frees or queues the skb, so on that path the caller still owns it. tcf_ct_act() however funnels every non-zero return into the ownership-transfer exit: err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag); if (err) goto out_frag; ... out_frag: if (err != -EINPROGRESS) tcf_action_inc_drop_qstats(&c->common); return TC_ACT_CONSUMED; TC_ACT_CONSUMED means the action took ownership of the skb, so no caller frees it - sch_handle_ingress(), sch_handle_egress() and tcf_qevent_handle() all deliberately skip the free for that verdict. The skb is therefore orphaned: one sk_buff plus its data buffer is leaked per malformed packet, unbounded. Note the drop counter is already incremented for these errors, so the statistics claim a drop that never happens. Three different ownership states reach out_frag: today - the skb may be queued by the defrag engine (-EINPROGRESS), already freed by nf_ct_handle_fragments(), or still owned by us. Tell the caller which of those it is, and free the packet ourselves in the last case, which restores the TC_ACT_SHOT behaviour that predated the Fixes: commit. Reproduced on v7.2-rc6 with a 54-byte frame carrying a 40-byte IPv6 header with nexthdr = 0 (hop-by-hop) and nothing after it, on a clsact ingress chain with "action ct". kmemleak reports one leaked 232-byte skbuff_head_cache object plus its 704-byte data buffer per packet; with this patch it reports none. Fixes: 3f14b377d01d ("net/sched: act_ct: fix skb leak and crash on ooo frags") Cc: stable@vger.kernel.org # v6.8+ Signed-off-by: Hyunjung Ko Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260806101235.809370-1-hj351016@gmail.com Signed-off-by: Jakub Kicinski --- net/sched/act_ct.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index be535a261fa0..e250969c84ac 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -840,8 +840,15 @@ static int tcf_ct_ipv6_is_fragment(struct sk_buff *skb, bool *frag) return 0; } +/* On error, tells the caller whether it still owns @skb and must free it + * itself. @skb is ours only when the header checks below reject the packet + * before it is handed to the defragmentation engine; once nf_ct_handle_ + * fragments() has been called the skb is either queued (-EINPROGRESS) or has + * already been freed by it. + */ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, - u8 family, u16 zone, bool *defrag) + u8 family, u16 zone, bool *defrag, + bool *skb_is_ours) { enum ip_conntrack_info ctinfo; struct tc_skb_cb cb; @@ -859,8 +866,12 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb, err = tcf_ct_ipv4_is_fragment(skb, &frag); else err = tcf_ct_ipv6_is_fragment(skb, &frag); - if (err || !frag) + if (err) { + *skb_is_ours = true; return err; + } + if (!frag) + return 0; cb = *tc_skb_cb(skb); err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &cb.mru); @@ -977,6 +988,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, int nh_ofs, err, retval; struct tcf_ct_params *p; bool add_helper = false; + bool skb_is_ours = false; bool skip_add = false; bool defrag = false; struct nf_conn *ct; @@ -1012,9 +1024,18 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, */ nh_ofs = skb_network_offset(skb); skb_pull_rcsum(skb, nh_ofs); - err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag); - if (err) + err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag, + &skb_is_ours); + if (err) { + /* The skb is still ours only when the header checks rejected + * it; returning TC_ACT_CONSUMED for such a packet would leak + * it, since no caller frees an skb it was told it no longer + * owns. + */ + if (skb_is_ours) + goto drop; goto out_frag; + } err = nf_ct_skb_network_trim(skb, family); if (err) -- cgit v1.2.3 From f1b3416ceaf7ca4cb5cbd986ee8fe3ffaeda2d48 Mon Sep 17 00:00:00 2001 From: Hyunjung Ko Date: Thu, 6 Aug 2026 19:12:35 +0900 Subject: selftests: tc-testing: add act_ct test for malformed header handling Add a tdc case covering the leak fixed by the previous patch. The test attaches "action ct" to a clsact ingress chain and injects ten IPv6 frames whose nexthdr says hop-by-hop but which carry nothing after the 40-byte header, so ipv6_find_hdr() fails and tcf_ct_ipv6_is_fragment() returns -EPROTO. Before the fix act_ct returned TC_ACT_CONSUMED for these packets, so tc_run() never reached its TC_ACT_SHOT arm and the clsact drop counter stayed at zero while the skbs leaked. After the fix the packets are dropped properly and the counter reflects them, which is what the test matches on: before: Sent 476 bytes 11 pkt (dropped 0, overlimits 0 requeues 0) after: Sent 400 bytes 10 pkt (dropped 10, overlimits 0 requeues 0) Signed-off-by: Hyunjung Ko Reviewed-by: Victor Nogueira Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260806101235.809370-2-hj351016@gmail.com Signed-off-by: Jakub Kicinski --- .../selftests/tc-testing/tc-tests/actions/ct.json | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json index da65f838bd52..8ab48def89b6 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json @@ -702,5 +702,45 @@ "$TC qdisc del dev $DUMMY clsact", "$TC qdisc del dev $DUMMY root handle 1:" ] + }, + { + "id": "c7a3", + "name": "Verify act_ct drops a packet whose header checks fail", + "category": [ + "actions", + "ct", + "scapy" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + [ + "$TC qdisc del dev $DEV1 clsact", + 0, + 1, + 2, + 255 + ], + "$TC qdisc add dev $DEV1 clsact" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol all prio 1 matchall action ct", + "scapy": [ + { + "iface": "$DEV0", + "count": 10, + "packet": "Ether(type=0x86dd)/IPv6(nh=0, plen=0, src='::1', dst='::2')" + } + ], + "expExitCode": "0", + "verifyCmd": "$TC -s qdisc show dev $DEV1 clsact", + "matchPattern": "dropped 10", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 clsact" + ] } ] -- cgit v1.2.3 From d2121faf133ac3bf9531b53a7e21273649a08517 Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Thu, 6 Aug 2026 12:25:37 +0900 Subject: NTB: ntb_netdev: Preserve RX queue depth on allocation failure ntb_netdev_rx_handler() hands the received skb to the network stack before allocating its replacement. If the allocation fails, nothing is reposted. Every failure therefore takes one buffer out of the RX queue while the interface remains up, and enough failures eventually stall reception. A retry path could refill the queue later, but ntb_netdev has none. Allocate the replacement first instead. If that fails, drop the packet and repost the same skb. This keeps the queue full and lets packet delivery resume as soon as memory is available again. Fixes: 548c237c0a99 ("net: Add support for NTB virtual ethernet device") Cc: stable@vger.kernel.org Signed-off-by: Koichiro Den Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260806032537.3526498-1-den@valinux.co.jp Signed-off-by: Paolo Abeni --- drivers/net/ntb_netdev.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index c3a6ba96fc8a..029a4a532a10 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -127,8 +127,8 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, { struct ntb_netdev_queue *q = qp_data; struct ntb_netdev *dev = q->ntdev; + struct sk_buff *skb, *new_skb; struct net_device *ndev; - struct sk_buff *skb; int rc; ndev = dev->ndev; @@ -144,6 +144,12 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, goto enqueue_again; } + new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); + if (!new_skb) { + ndev->stats.rx_dropped++; + goto enqueue_again; + } + skb_put(skb, len); skb->protocol = eth_type_trans(skb, ndev); skb->ip_summed = CHECKSUM_NONE; @@ -157,12 +163,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, ndev->stats.rx_bytes += len; } - skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); - if (!skb) { - ndev->stats.rx_errors++; - ndev->stats.rx_frame_errors++; - return; - } + skb = new_skb; enqueue_again: rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN); -- cgit v1.2.3 From bc5bde9ce3cc36502839dfe98e068f7303a50982 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Sat, 8 Aug 2026 02:17:10 +0800 Subject: ipv4: fix use-after-free in fib_nhc_update_mtu() fib_nhc_update_mtu() walks the nexthop exception table under RTNL, but RTNL does not serialize this walk with PMTU exception updates. The walk uses rcu_dereference_protected() with a constant true condition without holding fnhe_lock. The following interleaving can therefore occur: CPU 0 CPU 1 fib_nhc_update_mtu() update_or_create_fnhe() load fnhe spin_lock_bh(&fnhe_lock) fnhe_remove_oldest() unlink fnhe kfree_rcu(fnhe, rcu) access fnhe after grace period KASAN reported: BUG: KASAN: slab-use-after-free in fib_nhc_update_mtu+0x3df/0x410 Read of size 8 at addr ffff888107d49000 by task poc/90 Call Trace: fib_nhc_update_mtu+0x3df/0x410 fib_sync_mtu+0x7a/0xd0 fib_netdev_event+0x229/0x3f0 netif_set_mtu_ext+0x33a/0x570 dev_set_mtu+0x88/0x120 The same walk updates fnhe_pmtu and fnhe_mtu_locked. These fields form a pair and other writers serialize them with fnhe_lock. RCU alone prevents reclamation, but would still allow concurrent writers to leave a mixed pair. Walk the table under RCU and acquire fnhe_lock only while updating each exception. RCU keeps the current entry alive while the short critical section serializes its paired PMTU fields. This avoids holding the global lock while scanning all 2048 buckets for every nexthop. Fixes: af7d6cce5369 ("net: ipv4: update fnhe_pmtu when first hop's MTU changes") Cc: stable@vger.kernel.org Suggested-by: Ido Schimmel Signed-off-by: Chengfeng Ye Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260807181710.1178747-1-nicoyip.dev@gmail.com Signed-off-by: Paolo Abeni --- include/net/route.h | 2 ++ net/ipv4/fib_semantics.c | 34 +++++++++++----------------------- net/ipv4/route.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/include/net/route.h b/include/net/route.h index f90106f383c5..45290177a33c 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -276,6 +276,8 @@ int fib_dump_info_fnhe(struct sk_buff *skb, struct netlink_callback *cb, u32 table_id, struct fib_info *fi, int *fa_index, int fa_start, unsigned int flags); +void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig); + static inline void ip_rt_put(struct rtable *rt) { /* dst_release() accepts a NULL parameter. diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 78f84ae3ee12..0483519b7fb0 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1895,42 +1895,30 @@ static int call_fib_nh_notifiers(struct fib_nh *nh, return NOTIFY_DONE; } -/* Update the PMTU of exceptions when: - * - the new MTU of the first hop becomes smaller than the PMTU - * - the old MTU was the same as the PMTU, and it limited discovery of - * larger MTUs on the path. With that limit raised, we can now - * discover larger MTUs - * A special case is locked exceptions, for which the PMTU is smaller - * than the minimal accepted PMTU: - * - if the new MTU is greater than the PMTU, don't make any change - * - otherwise, unlock and set PMTU +/* Walk the exceptions of a nexthop after its first hop MTU changed. The + * chain is RCU protected here, while fnhe_update_pmtu() takes fnhe_lock + * for the update of each entry. */ void fib_nhc_update_mtu(struct fib_nh_common *nhc, u32 new, u32 orig) { struct fnhe_hash_bucket *bucket; int i; - bucket = rcu_dereference_protected(nhc->nhc_exceptions, 1); + rcu_read_lock(); + bucket = rcu_dereference(nhc->nhc_exceptions); if (!bucket) - return; + goto out; for (i = 0; i < FNHE_HASH_SIZE; i++) { struct fib_nh_exception *fnhe; - for (fnhe = rcu_dereference_protected(bucket[i].chain, 1); + for (fnhe = rcu_dereference(bucket[i].chain); fnhe; - fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) { - if (fnhe->fnhe_mtu_locked) { - if (new <= fnhe->fnhe_pmtu) { - fnhe->fnhe_pmtu = new; - fnhe->fnhe_mtu_locked = false; - } - } else if (new < fnhe->fnhe_pmtu || - orig == fnhe->fnhe_pmtu) { - fnhe->fnhe_pmtu = new; - } - } + fnhe = rcu_dereference(fnhe->fnhe_next)) + fnhe_update_pmtu(fnhe, new, orig); } +out: + rcu_read_unlock(); } void fib_sync_mtu(struct net_device *dev, u32 orig_mtu) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 152d8cb28f65..b82401a6baed 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -741,6 +741,35 @@ out_unlock: spin_unlock_bh(&fnhe_lock); } +/* Update the PMTU of an exception when: + * - the new MTU of the first hop becomes smaller than the PMTU + * - the old MTU was the same as the PMTU, and it limited discovery of + * larger MTUs on the path. With that limit raised, we can now + * discover larger MTUs + * A special case is locked exceptions, for which the PMTU is smaller + * than the minimal accepted PMTU: + * - if the new MTU is greater than the PMTU, don't make any change + * - otherwise, unlock and set PMTU + * + * fnhe_lock keeps fnhe_pmtu and fnhe_mtu_locked consistent against + * update_or_create_fnhe(), which sets both under the same lock. + */ +void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig) +{ + spin_lock_bh(&fnhe_lock); + + if (fnhe->fnhe_mtu_locked) { + if (new <= fnhe->fnhe_pmtu) { + fnhe->fnhe_pmtu = new; + fnhe->fnhe_mtu_locked = false; + } + } else if (new < fnhe->fnhe_pmtu || orig == fnhe->fnhe_pmtu) { + fnhe->fnhe_pmtu = new; + } + + spin_unlock_bh(&fnhe_lock); +} + static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4, bool kill_route) { -- cgit v1.2.3 From b37971686ec59fb027fa4910ba16805e68fddb97 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 9 Aug 2026 20:18:29 +0900 Subject: vxlan: do not arm the ageing timer on a device that is down vxlan_changelink() arms vxlan->age_timer whenever the requested ageing interval differs from the configured one: if (conf.age_interval != vxlan->cfg.age_interval) mod_timer(&vxlan->age_timer, jiffies); There is no netif_running() test, so the timer is armed even on a device that was never brought up. The only synchronous cancel in the driver is the timer_delete_sync() in vxlan_stop(), which is .ndo_stop. netif_close_many() drops devices without IFF_UP before __dev_close_many() runs, so that cancel is skipped for such a device. vxlan_setup() sets dev->needs_free_netdev = true and age_timer is a member of struct vxlan_dev, so free_netdev() releases the allocation the timer lives in while it is still queued on a timer_base. expire_timers() unlinks the entry before it loads timer->function, so the timer core writes through the freed object's list pointers: BUG: KASAN: slab-use-after-free in __run_timers+0x208/0x654 Write of size 8 at addr ffff00001adace68 by task true/192 __asan_store8+0x84/0xac __run_timers+0x208/0x654 run_timer_softirq+0x154/0x18c Allocated by task 189: alloc_netdev_mqs+0x64/0x720 rtnl_create_link+0x4ac/0x520 rtnl_newlink+0x758/0xd00 Freed by task 191: netdev_release+0x40/0x58 netdev_run_todo+0x4a4/0x8c0 rtnl_dellink+0x200/0x4e8 The rtnl operations involved are netns-scoped, so an unprivileged user can perform them in a new user and network namespace. Arming the timer on a down device never had an effect: vxlan_cleanup() returns early on !netif_running(), and vxlan_open() arms the timer for any non-zero interval once the device is brought up. Add the missing test. Discovered by XBOW, triaged by Baul Lee Fixes: 40051c4dcad5 ("vxlan: Allow changing ageing time") Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260809111829.78834-1-baul.lee@xbow.com Signed-off-by: Paolo Abeni --- drivers/net/vxlan/vxlan_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 1ded27768a97..824144bb7774 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4500,7 +4500,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[], if (change_igmp && vxlan_addr_multicast(&dst->remote_ip)) err = vxlan_multicast_leave(vxlan); - if (conf.age_interval != vxlan->cfg.age_interval) + if (netif_running(dev) && conf.age_interval != vxlan->cfg.age_interval) mod_timer(&vxlan->age_timer, jiffies); netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev); -- cgit v1.2.3 From b48a0a0a76ccecec60f0568e2af4d89994b08bec Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 10 Aug 2026 14:10:38 +0300 Subject: pmdomain: qcom: rpmhpd: Add missing MXC and MMCX power domains for Eliza MXC and MMCX were initially omitted as they were believed to have no consumers, and they were not present downstream either. The Iris video codec requires these power domains, so add MXC and MMCX along with the MXC_AO and MMCX_AO. Fixes: f387ecdd492f ("pmdomain: qcom: rpmhpd: Add Eliza RPMh Power Domains") Signed-off-by: Abel Vesa Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/qcom/rpmhpd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 63120e703923..ffc46de8d07a 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -245,9 +245,13 @@ static struct rpmhpd *eliza_rpmhpds[] = { [RPMHPD_GFX] = &gfx, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx, + [RPMHPD_MMCX_AO] = &mmcx_ao, [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_NSP] = &nsp, }; -- cgit v1.2.3 From 01fdecc0480d916c799dbee584833a4a37e94d06 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 7 Aug 2026 14:34:04 +0800 Subject: net: packet: fix wrong transport_header when sending VLAN-tagged frame In packet_parse_headers(), when processing a VLAN-tagged frame, skb_set_network_header() is called to advance network_header past the VLAN tag to the inner protocol header. skb_probe_transport_header() is then called with skb->protocol still set to the outer VLAN EtherType (e.g. ETH_P_8021Q), while nhoff (derived from skb_network_offset()) already points past the VLAN tag to the inner protocol header. In __skb_flow_dissect(), proto is initialized to ETH_P_8021Q and nhoff points past the VLAN tag. When the dissector hits case ETH_P_8021Q, it reads a struct vlan_hdr at nhoff via __skb_header_pointer(), but that offset contains the inner protocol header (e.g. an IP header). The bytes are misinterpreted as a VLAN header, yielding a garbage encapsulated EtherType that matches no known protocol. The dissector returns false, so skb_probe_transport_header() never calls skb_set_transport_header(), leaving transport_header at its uninitialized sentinel value (~0U). Move skb_probe_transport_header() to before skb_set_network_header(). At the time skb_probe_transport_header() is called, network_header still points to the VLAN header, so nhoff correctly points to the VLAN header. The flow dissector can then parse the VLAN header, extract the inner EtherType, and advance nhoff to the inner protocol header, allowing transport_header to be set correctly. Fixes: dfed913e8b55 ("net/af_packet: add VLAN support for AF_PACKET SOCK_RAW GSO") Assisted-by: WChat:claude-opus-4-8 Signed-off-by: Wei Fang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260807063405.688780-2-wei.fang@oss.nxp.com Signed-off-by: Paolo Abeni --- net/packet/af_packet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 435756877aba..213a8a3b7ee5 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1943,13 +1943,13 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock) sock->type == SOCK_RAW) skb->protocol = dev_parse_header_protocol(skb); + skb_probe_transport_header(skb); + /* Move network header to the right position for VLAN tagged packets */ if (likely(skb->dev->type == ARPHRD_ETHER) && eth_type_vlan(skb->protocol) && vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - - skb_probe_transport_header(skb); } /* -- cgit v1.2.3 From cbb35cbe8db268fefe34c23df15348cf99025298 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Fri, 7 Aug 2026 14:34:05 +0800 Subject: net: tap: fix wrong transport_header when sending VLAN-tagged frame In tap_get_user_xdp(), when processing a VLAN-tagged frame (e.g. ETH_P_8021Q), skb_set_network_header() is called first to advance network_header past the VLAN tag to the inner protocol header. skb_probe_transport_header() is then called with skb->protocol still set to ETH_P_8021Q, while nhoff (derived from skb_network_offset()) already points past the VLAN tag to the inner protocol header. In __skb_flow_dissect(), proto is initialized to ETH_P_8021Q and nhoff points past the VLAN tag. When the dissector hits case ETH_P_8021Q, it reads a struct vlan_hdr at the current nhoff via __skb_header_pointer(), but that offset contains the inner protocol header (e.g. an IP header). The bytes are misinterpreted as a VLAN header, yielding a garbage encapsulated EtherType that matches no known protocol. The dissector returns false, so skb_probe_transport_header() never calls skb_set_transport_header(), leaving transport_header at its uninitialized sentinel value (~0U). Move skb_set_network_header() to after skb_probe_transport_header(). At the time skb_probe_transport_header() is called, network_header still points to the VLAN header (offset ETH_HLEN), so nhoff is correct and the flow dissector can parse the VLAN header, extract the inner EtherType, and advance nhoff to the inner protocol header, allowing transport_header to be set correctly. Fixes: 8c76e77f9069 ("tap: call skb_probe_transport_header after setting skb->dev") Assisted-by: WChat:claude-opus-4-8 Signed-off-by: Wei Fang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260807063405.688780-3-wei.fang@oss.nxp.com Signed-off-by: Paolo Abeni --- drivers/net/tap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 5d2d34d24ce8..9b5af08cdd47 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1091,12 +1091,13 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) } } + skb_probe_transport_header(skb); + /* Move network header to the right position for VLAN tagged packets */ if (eth_type_vlan(skb->protocol) && vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - skb_probe_transport_header(skb); dev_queue_xmit(skb); rcu_read_unlock(); -- cgit v1.2.3 From 5f3a13e0bb5ebcc1ca2dfda42ea40b9f3c2be6ea Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Fri, 7 Aug 2026 14:22:14 +0800 Subject: net: ngbe: fix NULL pointer dereference in non-MSI-X interrupt enabling In non-MSI-X mode (such as legacy INTx or single MSI), wx->msix_entry is not allocated or initialized. Calling NGBE_INTR_MISC(wx) dereferences wx->msix_entry->entry, leading to a NULL pointer dereference crash. This issue was introduced by fixing the IRQ vector when the number of VFs is 7. Fix the issue by explicitly checking `pdev->msix_enabled` to determine the correct vector index. Additionally, as a side fix, set the interrupt mask to BIT(0) for the non-MSI-X fallback. In MSI/INTx mode, the MISC and queue interrupts share vector 0, and the WX_PX_MISC_IVAR register is only valid in the MSI-X case. Thus, BIT(0) is the correct mask for the miscellaneous cause when MSI-X is disabled. Fixes: 4174c0c331a2 ("net: ngbe: specify IRQ vector when the number of VFs is 7") Signed-off-by: Jiawen Wu Reviewed-by: Breno Leitao Reviewed-by: Simon Horman Link: https://patch.msgid.link/B2693E9A8BFAD110+20260807062214.410838-1-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index a16221995909..cb653861aad8 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -180,8 +180,10 @@ static void ngbe_irq_enable(struct wx *wx, bool queues) /* mask interrupt */ if (queues) wx_intr_enable(wx, NGBE_INTR_ALL); - else + else if (wx->pdev->msix_enabled) wx_intr_enable(wx, NGBE_INTR_MISC(wx)); + else + wx_intr_enable(wx, BIT(0)); } /** -- cgit v1.2.3 From 976df67f463db1fddaf2a32fb04f57ad2891a23d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 6 Aug 2026 20:44:07 -0400 Subject: net/tls: Fail tls_sw_splice_read() after a failed async decrypt When an async decrypt fails, tls_decrypt_done() records the error in ctx->async_wait.err and calls tls_err_abort(), which stores it in sk_err. tls_sw_recvmsg() and tls_sw_read_sock() each read async_wait.err once they hold the reader lock and fail the call: a record that did not authenticate breaks the connection. tls_sw_splice_read() has no such check, and sk_err does not stand in for one. tls_rx_rec_wait() tests sk_err only inside the loop it skips whenever a record is already parsed, and the first reader to reach sock_error() clears it, while async_wait.err persists. A splice therefore keeps delivering records on a connection that recvmsg() and read_sock() refuse to read. Read async_wait.err in tls_sw_splice_read() as the other two readers do. Fixes: f314bfee81b1 ("tls: rx: return the already-copied data on crypto error") Reviewed-by: Jakub Kicinski Signed-off-by: Chuck Lever Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260806-tls-splice-crypto-fix-v1-1-a2624005a286@kernel.org Signed-off-by: Paolo Abeni --- net/tls/tls_sw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 62d46736e24b..d1ad31986cf2 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2014,6 +2014,11 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, if (err < 0) return err; + /* If crypto failed the connection is broken */ + err = ctx->async_wait.err; + if (err) + goto splice_read_end; + if (!skb_queue_empty(&ctx->rx_list)) { skb = __skb_dequeue(&ctx->rx_list); } else { -- cgit v1.2.3 From 8a422297391328b8128e5f6b7e1c49b0240ffa82 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 6 Aug 2026 20:44:08 -0400 Subject: selftests: tls: cover splice after a failed decrypt Nothing in this file splices a socket whose last decrypt failed, so the check that fails tls_sw_splice_read() on a broken connection can be removed without a test noticing. Such a splice hands the application plaintext that recvmsg() and read_sock() already refuse to return. Extend the bad_auth pattern. Corrupt an authenticated record, confirm recvmsg() reports EBADMSG, then splice the same socket and require EBADMSG again. A synchronous decrypt fails again on the still-queued record, so only an async decrypt reaches EBADMSG through the recorded-failure check alone. bad_auth builds the same corrupted record, so its construction moves into a helper the two tests share. Signed-off-by: Chuck Lever Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260806-tls-splice-crypto-fix-v1-2-a2624005a286@kernel.org Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/tls.c | 75 +++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index a0e72e402748..9d3cd4fff062 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -24,6 +24,7 @@ #include "kselftest_harness.h" #define TLS_PAYLOAD_MAX_LEN 16384 +#define TLS_HDR_LEN 5 #define SOL_TLS 282 static int fips_enabled; @@ -2732,28 +2733,83 @@ TEST_F(tls_err, bad_rec) EXPECT_EQ(errno, EAGAIN); } +/* cfd carries a byte stream, so one recv() can return part of a + * record. Take the fragment length from the record header and wait + * for the remainder. + */ +static void tls_send_bad_auth(struct __test_metadata *_metadata, + int fd, int cfd, int fd2) +{ + char buf[128]; + int len; + + memrnd(buf, sizeof(buf) / 2); + ASSERT_EQ(send(fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2); + + ASSERT_EQ(recv(cfd, buf, TLS_HDR_LEN, MSG_WAITALL), TLS_HDR_LEN); + + len = ((unsigned char)buf[3] << 8) | (unsigned char)buf[4]; + ASSERT_GT(len, 0); + ASSERT_LE(len, (int)sizeof(buf) - TLS_HDR_LEN); + + ASSERT_EQ(recv(cfd, buf + TLS_HDR_LEN, len, MSG_WAITALL), len); + + buf[TLS_HDR_LEN + len - 1]++; + + ASSERT_EQ(send(fd2, buf, TLS_HDR_LEN + len, 0), TLS_HDR_LEN + len); +} + TEST_F(tls_err, bad_auth) { char buf[128]; - int n; if (self->notls) SKIP(return, "no TLS support"); - memrnd(buf, sizeof(buf) / 2); - EXPECT_EQ(send(self->fd, buf, sizeof(buf) / 2, 0), sizeof(buf) / 2); - n = recv(self->cfd, buf, sizeof(buf), 0); - EXPECT_GT(n, sizeof(buf) / 2); + tls_send_bad_auth(_metadata, self->fd, self->cfd, self->fd2); - buf[n - 1]++; - - EXPECT_EQ(send(self->fd2, buf, n, 0), n); EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1); EXPECT_EQ(errno, EBADMSG); EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1); EXPECT_EQ(errno, EBADMSG); } +/* A record that did not authenticate breaks the connection for every + * reader, splice included. + * + * The two decrypt paths reach that result differently. A synchronous + * decrypt leaves the record parsed, so the splice re-runs the decrypt + * and fails on the record itself; the ctx->async_wait.err check in + * tls_sw_splice_read() is not what stops it. Only an asynchronous + * decrypt, which needs a TLS 1.2 socket and an AEAD advertising + * CRYPTO_ALG_ASYNC, consumes the record before the failure is + * recorded, leaving that check the sole reason the splice fails. + */ +TEST_F(tls_err, bad_auth_splice) +{ + char buf[128]; + ssize_t ret; + int p[2]; + + if (self->notls) + SKIP(return, "no TLS support"); + + tls_send_bad_auth(_metadata, self->fd, self->cfd, self->fd2); + + EXPECT_EQ(recv(self->cfd2, buf, sizeof(buf), 0), -1); + EXPECT_EQ(errno, EBADMSG); + + ASSERT_GE(pipe(p), 0); + + ret = splice(self->cfd2, NULL, p[1], NULL, sizeof(buf), + SPLICE_F_NONBLOCK); + EXPECT_EQ(ret, -1); + EXPECT_EQ(errno, EBADMSG); + + close(p[0]); + close(p[1]); +} + TEST_F(tls_err, bad_in_large_read) { char txt[3][64]; @@ -3009,7 +3065,6 @@ static size_t parse_tls_records(struct __test_metadata *_metadata, { const __u8 *rec = rx_buf; size_t total_plaintext_rx = 0; - const __u8 rec_header_len = 5; while (rec < rx_buf + rx_len) { __u16 record_payload_len; @@ -3029,7 +3084,7 @@ static size_t parse_tls_records(struct __test_metadata *_metadata, /* Plaintext must not exceed the specified limit */ ASSERT_LE(plaintext_len, max_payload_len); - rec += rec_header_len + record_payload_len; + rec += TLS_HDR_LEN + record_payload_len; } return total_plaintext_rx; -- cgit v1.2.3 From 78983d82dc4c677c5cd2941ef828b1903ca51c9e Mon Sep 17 00:00:00 2001 From: Niranjan H Y Date: Fri, 7 Aug 2026 20:46:23 +0530 Subject: ASoC: tac5xx2-sdw: select REGMAP_SOUNDWIRE_MBQ "select REGMAP_SOUNDWIRE_MBQ" to fix build error: ERROR: modpost: "__devm_regmap_init_sdw_mbq" [sound/soc/codecs/snd-soc-tac5xx2-sdw.ko] undefined! Signed-off-by: Niranjan H Y Link: https://patch.msgid.link/20260807151623.3005-1-niranjan.hy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d43475a63ac2..e4d139108d72 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2157,6 +2157,7 @@ config SND_SOC_TAC5XX2_SDW tristate "Texas Instruments TAC5XX2 SoundWire Smart Amplifier" depends on SOUNDWIRE depends on SND_SOC_SDCA + select REGMAP_SOUNDWIRE_MBQ help This option enables support for Texas Instruments TAC5XX2 family of SoundWire Smart Amplifiers. This includes TAC5572, TAC5672, -- cgit v1.2.3 From cba9ccb47e9fa4cc77692fb896cc5ab57a667882 Mon Sep 17 00:00:00 2001 From: Jun Yang Date: Mon, 10 Aug 2026 18:21:38 +0800 Subject: tipc: read le->link under the node lock in tipc_node_link_down() tipc_node_link_down() caches the link pointer before taking n->lock: struct tipc_link *l = le->link; /* unlocked */ if (!l) return; tipc_node_write_lock(n); if (!tipc_link_is_establishing(l)) { /* deref l */ ... tipc_link_reset(l); /* write into l */ if (delete) { kfree(l); le->link = NULL; The delete=true caller frees that very object under n->lock, so the lock does not protect the cached pointer against it: - CPU A, delete=false: tipc_rcv() on TIPC_LINK_DOWN_EVT, or the link supervision timer via tipc_node_timeout(), reads l unlocked and then dereferences it under n->lock; - CPU B, delete=true: netlink TIPC_NL_BEARER_DISABLE -> bearer_disable() -> tipc_node_delete_links() -> tipc_node_link_down(n, bearer_id, true) -> kfree(l). The link is freed with plain kfree(), not kfree_rcu(), and for UDP bearers disable_media() only schedules the asynchronous cleanup_bearer() work, so its synchronize_net() runs after the links are already gone. An in-flight CPU A that has read l therefore dereferences freed memory once B frees it: a use-after-free read in tipc_link_is_establishing(), and a use-after-free write via tipc_link_reset() on the establishing branch. The following trace was captured on 7.2.0-rc5-00284-gaf39eb111ce6: BUG: KASAN: slab-use-after-free in tipc_link_is_establishing (net/tipc/link.c:285) Read of size 4 at addr ffff88802e2aa068 by task swapper/2/0 tipc_link_is_establishing (net/tipc/link.c:285) tipc_node_link_down (net/tipc/node.c:1076) tipc_node_timeout (net/tipc/node.c:843) Allocated by task 9549: tipc_link_create (net/tipc/link.c:490) tipc_node_check_dest (net/tipc/node.c:1279) tipc_disc_rcv (net/tipc/discover.c:252) tipc_udp_recv (net/tipc/udp_media.c:389) Freed by task 9549: tipc_node_link_down (net/tipc/node.c:1084) tipc_node_delete_links (net/tipc/node.c:1320) bearer_disable (net/tipc/bearer.c:414) __tipc_nl_bearer_disable (net/tipc/bearer.c:992) Move the le->link read inside tipc_node_write_lock(), so it is serialised against the kfree() in the delete path. A racing teardown now either has not run yet, and we see a valid link, or has already run, and we see NULL. Fixes: 73f646cec354 ("tipc: delay ESTABLISH state event when link is established") Cc: stable@kernel.org Reported-by: TencentOS Corvus AI Assisted-by: tencentos-corvus-ai:kimi-k3 Signed-off-by: Jun Yang Reviewed-by: Tung Nguyen Link: https://patch.msgid.link/20260810102147.48191-1-juny24602@gmail.com Signed-off-by: Paolo Abeni --- net/tipc/node.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/net/tipc/node.c b/net/tipc/node.c index 8e4ef2630ae4..683a136e53ef 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1061,18 +1061,23 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete) { - struct tipc_link_entry *le = &n->links[bearer_id]; struct tipc_media_addr *maddr = NULL; - struct tipc_link *l = le->link; int old_bearer_id = bearer_id; + struct tipc_link_entry *le; struct sk_buff_head xmitq; - - if (!l) - return; + struct tipc_link *l; __skb_queue_head_init(&xmitq); + /* Synchronize the link lookup with bearer teardown. */ tipc_node_write_lock(n); + le = &n->links[bearer_id]; + l = le->link; + if (!l) { + tipc_node_write_unlock_fast(n); + return; + } + if (!tipc_link_is_establishing(l)) { __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr); } else { -- cgit v1.2.3 From 133c71b2c0bc976a4751f9e05ef7cdea67f964e5 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 3 Aug 2026 21:59:25 +0800 Subject: spi: dw: fix wrong RX_SAMPLE_DLY setting after resume On platforms which need a non-zero rx sample delay, the RX_SAMPLE_DLY reg setting is lost after resume. The reason is that the reg may be reset to 0 after resuming, but dws->cur_rx_sample_dly doesn't know this fact. Fix this issue by clearing dws->cur_rx_sample_dly in dw_spi_shutdown_chip(). Signed-off-by: Jisheng Zhang Suggested-by: Mark Brown Link: https://patch.msgid.link/20260803135925.12622-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-dw.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 2f2debc64e73..a9d055f9c5dd 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -283,6 +283,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws) dw_spi_enable_chip(dws, 0); dw_spi_set_clk(dws, 0); dws->current_freq = 0; + dws->cur_rx_sample_dly = 0; } extern void dw_spi_set_cs(struct spi_device *spi, bool enable); -- cgit v1.2.3 From f12afefb7b01f94d6d66d397f323a9914edbf70e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 6 Aug 2026 16:32:31 -0700 Subject: ASoC: xilinx: formatter_pcm: pass aud_drv_data to irq handlers The irq handlers take a struct device pointer and call dev_get_drvdata() to obtain the driver data. However, the driver data is only set at the end of probe, after devm_request_irq(), so an interrupt taken in between causes the handlers to pass a NULL pointer to readl() and crash. Pass the private data directly as the devm_request_irq() argument instead of the device pointer, matching what the handlers expect. Fixes: 6f6c3c36f091 ("ASoC: xlnx: add pcm formatter platform driver") Assisted-by: opencode:deepseek-v4-flash-free Signed-off-by: Rosen Penev Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260806233231.30631-1-rosenp@gmail.com Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_formatter_pcm.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 8f7a76758535..f15336197ed3 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -281,8 +281,7 @@ static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg) { u32 val; void __iomem *reg; - struct device *dev = arg; - struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); + struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio + XLNX_MM2S_OFFSET + XLNX_AUD_STS; val = readl(reg); @@ -300,8 +299,7 @@ static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) { u32 val; void __iomem *reg; - struct device *dev = arg; - struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); + struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio + XLNX_S2MM_OFFSET + XLNX_AUD_STS; val = readl(reg); @@ -637,7 +635,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) } ret = devm_request_irq(dev, aud_drv_data->mm2s_irq, xlnx_mm2s_irq_handler, 0, - "xlnx_formatter_pcm_mm2s_irq", dev); + "xlnx_formatter_pcm_mm2s_irq", aud_drv_data); if (ret) { dev_err(dev, "xlnx audio mm2s irq request failed\n"); goto clk_err; @@ -664,7 +662,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) ret = devm_request_irq(dev, aud_drv_data->s2mm_irq, xlnx_s2mm_irq_handler, 0, "xlnx_formatter_pcm_s2mm_irq", - dev); + aud_drv_data); if (ret) { dev_err(dev, "xlnx audio s2mm irq request failed\n"); goto clk_err; -- cgit v1.2.3 From ae7fd6ff4c6713270d2efe6db87a4a58ccb7cc61 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 11 Aug 2026 14:18:16 +0100 Subject: regmap: sdw-mbq: Fix swap of timeout and retry times When polling Function Busy using read_poll_timeout() the total timeout and retry delay arguments are swapped. This leads to only a single retry being processed, it seems the existing users typically do succeed before the first retry. Swap the arguments over to ensure the correct polling time. Reported-by: Ville Saarinen Link: https://lore.kernel.org/linux-sound/ansTPGgVNoDJlA5r@opensource.cirrus.com/T/#m680731a2f307f1f5176b27ed5aa560ddc94e5d62 Fixes: 5bc493bf0c37 ("regmap: sdw-mbq: Add support for SDCA deferred controls") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260811131816.332082-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-sdw-mbq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c index 2585933d4946..6d7c760cbef2 100644 --- a/drivers/base/regmap/regmap-sdw-mbq.c +++ b/drivers/base/regmap/regmap-sdw-mbq.c @@ -59,7 +59,7 @@ static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, if (ctx->readable_reg(dev, reg)) { ret = read_poll_timeout(sdw_read_no_pm, val, val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY), - ctx->cfg.timeout_us, ctx->cfg.retry_us, + ctx->cfg.retry_us, ctx->cfg.timeout_us, false, slave, reg); if (val < 0) return val; -- cgit v1.2.3 From 079e27f52b929b554b90514b081ea40b3d632a25 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Tue, 11 Aug 2026 08:53:54 +0800 Subject: ASoC: Intel: NVL: Add entry for HDMI-In capture support to non-I2S codec boards. Adding HDMI-In capture support for the NVL products which doesn't have onboard I2S codec. But need to support HDMI-In capture via I2S and audio playback through HDMI/DP monitor. Signed-off-by: Balamurugan C Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260811005354.2884137-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_ssp_amp.c | 6 ++++++ sound/soc/intel/common/soc-acpi-intel-nvl-match.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c index 729c0cd7c19c..b80d24966cbd 100644 --- a/sound/soc/intel/boards/sof_ssp_amp.c +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -222,6 +222,12 @@ static const struct platform_device_id board_ids[] = { /* SSP 0 and SSP 2 are used for HDMI IN */ SOF_HDMI_PLAYBACK_PRESENT), }, + { + .name = "nvl_lt6911_hdmi_ssp", + .driver_data = (kernel_ulong_t)(SOF_SSP_MASK_HDMI_CAPTURE(0x5) | + /* SSP 0 and SSP 2 are used for HDMI IN */ + SOF_HDMI_PLAYBACK_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index 4a6208f821eb..8f6e987c791e 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -56,6 +56,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | SND_SOC_ACPI_TPLG_INTEL_SSP_MSB, }, + /* place amp/hdmi-in only boards in the end of table */ + { + .id = "INTC10B0", + .drv_name = "nvl_lt6911_hdmi_ssp", + .sof_tplg_filename = "sof-nvl-hdmi-ssp02.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_machines); -- cgit v1.2.3 From 6853da8cdb6676d8b173fac15094490dff419ac5 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:20 +0100 Subject: Revert "drm/sched: Remove drm_sched_init_args->num_rqs" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d09339388b778f04dd9e638befa2594c9cb4290b. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-2-tvrtko.ursulin@igalia.com --- include/drm/gpu_scheduler.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index d61c19e78182..f1e15a80c844 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -608,6 +608,8 @@ struct drm_gpu_scheduler { * @ops: backend operations provided by the driver * @submit_wq: workqueue to use for submission. If NULL, an ordered wq is * allocated and used. + * @num_rqs: Number of run-queues. This may be at most DRM_SCHED_PRIORITY_COUNT, + * as there's usually one run-queue per priority, but may be less. * @credit_limit: the number of credits this scheduler can hold from all jobs * @hang_limit: number of times to allow a job to hang before dropping it. * This mechanism is DEPRECATED. Set it to 0. @@ -621,6 +623,7 @@ struct drm_sched_init_args { const struct drm_sched_backend_ops *ops; struct workqueue_struct *submit_wq; struct workqueue_struct *timeout_wq; + u32 num_rqs; u32 credit_limit; unsigned int hang_limit; long timeout; -- cgit v1.2.3 From 482025714dba7c0d51356aa12e37246682b54375 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:21 +0100 Subject: Revert "drm/xe: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4ca491d6ccf2daea813e67ed4b42e7b272f0687d. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-3-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/xe/xe_dep_scheduler.c | 1 + drivers/gpu/drm/xe/xe_execlist.c | 1 + drivers/gpu/drm/xe/xe_gpu_scheduler.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_dep_scheduler.c b/drivers/gpu/drm/xe/xe_dep_scheduler.c index 004aac8b89e6..51d99fee9aa5 100644 --- a/drivers/gpu/drm/xe/xe_dep_scheduler.c +++ b/drivers/gpu/drm/xe/xe_dep_scheduler.c @@ -78,6 +78,7 @@ xe_dep_scheduler_create(struct xe_device *xe, const struct drm_sched_init_args args = { .ops = &sched_ops, .submit_wq = submit_wq, + .num_rqs = 1, .credit_limit = job_limit, .timeout = MAX_SCHEDULE_TIMEOUT, .name = name, diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c index 9fb99c038ea8..0fe4fb226ef4 100644 --- a/drivers/gpu/drm/xe/xe_execlist.c +++ b/drivers/gpu/drm/xe/xe_execlist.c @@ -328,6 +328,7 @@ static int execlist_exec_queue_init(struct xe_exec_queue *q) struct drm_gpu_scheduler *sched; const struct drm_sched_init_args args = { .ops = &drm_sched_ops, + .num_rqs = 1, .credit_limit = xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, .hang_limit = XE_SCHED_HANG_LIMIT, .timeout = XE_SCHED_JOB_TIMEOUT, diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.c b/drivers/gpu/drm/xe/xe_gpu_scheduler.c index 67d8ce368486..9c8004d5dd91 100644 --- a/drivers/gpu/drm/xe/xe_gpu_scheduler.c +++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.c @@ -66,6 +66,7 @@ int xe_sched_init(struct xe_gpu_scheduler *sched, const struct drm_sched_init_args args = { .ops = ops, .submit_wq = submit_wq, + .num_rqs = 1, .credit_limit = hw_submission, .hang_limit = hang_limit, .timeout = timeout, -- cgit v1.2.3 From 6ef022fbe26d59378e229c4eb296411bccbb7ae2 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:22 +0100 Subject: Revert "drm/v3d: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a1bf9381fc62f3c4e26a2caedb8317046383a559. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-4-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/v3d/v3d_sched.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 63dd95b828c8..bea46298b69e 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -835,6 +835,7 @@ v3d_queue_sched_init(struct v3d_dev *v3d, const struct drm_sched_backend_ops *op enum v3d_queue queue, const char *name) { struct drm_sched_init_args args = { + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = 1, .timeout = msecs_to_jiffies(500), .timeout_wq = v3d->reset_wq, -- cgit v1.2.3 From c41282f9ca22e240b1d01f9e06c328a3ac7ef30a Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:23 +0100 Subject: Revert "drm/sched: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2833a0512b4cd55d9fea7ec18be85ef82e69ad3b. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-5-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/scheduler/tests/mock_scheduler.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c index 14403a762335..8e9ae7d980eb 100644 --- a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c +++ b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c @@ -290,6 +290,7 @@ struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test, long timeout) { struct drm_sched_init_args args = { .ops = &drm_mock_scheduler_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = U32_MAX, .hang_limit = 1, .timeout = timeout, -- cgit v1.2.3 From 09e6a5d433ef9b5da1267c81f2f394ece64a7c56 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:24 +0100 Subject: Revert "drm/panthor: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 30c4a19cf71f040462254dcb8b2d3c3e7232b99b. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-6-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/panthor/panthor_mmu.c | 1 + drivers/gpu/drm/panthor/panthor_sched.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index e592a8ebb478..e10dbd18d8cf 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -2732,6 +2732,7 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu, const struct drm_sched_init_args sched_args = { .ops = &panthor_vm_bind_ops, .submit_wq = ptdev->mmu->vm.wq, + .num_rqs = 1, .credit_limit = 1, /* Bind operations are synchronous for now, no timeout needed. */ .timeout = MAX_SCHEDULE_TIMEOUT, diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 298b046c95ed..369ee06434bb 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -3502,6 +3502,7 @@ group_create_queue(struct panthor_group *group, struct drm_sched_init_args sched_args = { .ops = &panthor_queue_sched_ops, .submit_wq = group->ptdev->scheduler->wq, + .num_rqs = 1, /* * The credit limit argument tells us the total number of * instructions across all CS slots in the ringbuffer, with -- cgit v1.2.3 From c9126bf8fcb53462a66ec10183cad038fa26f5e9 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:25 +0100 Subject: Revert "drm/panfrost: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 285eab7f55ae3d961bfa4e759c3d2d0033e72294. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-7-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/panfrost/panfrost_job.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index 2d12b83e900a..d59b4863b8ad 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -850,6 +850,7 @@ int panfrost_jm_init(struct panfrost_device *pfdev) { struct drm_sched_init_args args = { .ops = &panfrost_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = 2, .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS), .dev = pfdev->base.dev, -- cgit v1.2.3 From 5b07bcb10343ab2e6f25943d19a2ab511dff1982 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:26 +0100 Subject: Revert "drm/nouveau: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 79005e34bdcbb4a0b7f512bc32981fb60041767b. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-8-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/nouveau/nouveau_sched.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c index 2cbae003d6de..8b9f935afe09 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sched.c +++ b/drivers/gpu/drm/nouveau/nouveau_sched.c @@ -405,6 +405,7 @@ nouveau_sched_init(struct nouveau_sched *sched, struct nouveau_drm *drm, struct drm_sched_entity *entity = &sched->entity; struct drm_sched_init_args args = { .ops = &nouveau_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = credit_limit, .timeout = msecs_to_jiffies(NOUVEAU_SCHED_JOB_TIMEOUT_MS), .name = "nouveau_sched", -- cgit v1.2.3 From 2683a0e7c4ccd5fcbf8926573045f4f586ec5fd5 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:27 +0100 Subject: Revert "drm/msm: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 9c44ff055965f2f75eee2ac95a7692600cf026a8. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-9-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/msm/msm_gem_vma.c | 1 + drivers/gpu/drm/msm/msm_ringbuffer.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 3ed05ab0eeef..c4cfe036066b 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -841,6 +841,7 @@ msm_gem_vm_create(struct drm_device *drm, struct msm_mmu *mmu, const char *name, if (!managed) { struct drm_sched_init_args args = { .ops = &msm_vm_bind_ops, + .num_rqs = 1, .credit_limit = 1, .timeout = MAX_SCHEDULE_TIMEOUT, .name = "msm-vm-bind", diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c index 0d14c31bd4e4..2d6b930b766e 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.c +++ b/drivers/gpu/drm/msm/msm_ringbuffer.c @@ -67,6 +67,7 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id, { struct drm_sched_init_args args = { .ops = &msm_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = num_hw_submissions, .timeout = MAX_SCHEDULE_TIMEOUT, .dev = gpu->dev->dev, -- cgit v1.2.3 From b9f3f3f00bc8c4c53d46d420bf8646fe741622ab Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:28 +0100 Subject: Revert "drm/lima: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 098fe077ec029a1c8ded65af3c2b2a4190d93e9d. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-10-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/lima/lima_sched.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index 0a01213c4878..9a1e6b9ecbe5 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c @@ -521,6 +521,7 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name) lima_sched_timeout_ms : 10000; const struct drm_sched_init_args args = { .ops = &lima_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = 1, .hang_limit = lima_job_hang_limit, .timeout = msecs_to_jiffies(timeout), -- cgit v1.2.3 From 6c7b7e512058d54a461bbd38dc3f0db897503a9c Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:29 +0100 Subject: Revert "drm/imagination: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f84d73d2a08498174d950ba5935930dd94df7d3c. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-11-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/imagination/pvr_queue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index d13726401d92..2d52ee321367 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -1285,6 +1285,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx, const struct drm_sched_init_args sched_args = { .ops = &pvr_queue_sched_ops, .submit_wq = pvr_dev->sched_wq, + .num_rqs = 1, .credit_limit = 64 * 1024, .hang_limit = 1, .timeout = msecs_to_jiffies(500), -- cgit v1.2.3 From 4ce955c5339187a218f343e12e7d7e5dd46b105d Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:30 +0100 Subject: Revert "drm/etnaviv: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit adfb5deba567045d74bfd75482b8d4f89d073004. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-12-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/etnaviv/etnaviv_sched.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c index 139e6e38784b..3cc50d697c89 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c @@ -144,6 +144,7 @@ int etnaviv_sched_init(struct etnaviv_gpu *gpu) { const struct drm_sched_init_args args = { .ops = &etnaviv_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = etnaviv_hw_jobs_limit, .hang_limit = etnaviv_job_hang_limit, .timeout = msecs_to_jiffies(500), -- cgit v1.2.3 From 4788c25fcd6bb1731d3d7a81424eb92c43db5f19 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:31 +0100 Subject: Revert "drm/amdgpu: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2462a0ce23b0ba1c2195beccf39bc8608cdbd84e. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-13-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index bff3e06a756c..e43024ad4feb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -2250,6 +2250,7 @@ static int amdgpu_device_init_schedulers(struct amdgpu_device *adev) { struct drm_sched_init_args args = { .ops = &amdgpu_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .timeout_wq = adev->reset_domain->wq, .dev = adev->dev, }; -- cgit v1.2.3 From d5b15e57045562bb6dd4f69116b14f96cb195cd4 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:32 +0100 Subject: Revert "accel/ethosu: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 06879a9ad55bc4a7aa2e1bb7ee9fa658cdddee79. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-14-tvrtko.ursulin@igalia.com --- drivers/accel/ethosu/ethosu_job.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c index b76924645aaa..1e2465279aae 100644 --- a/drivers/accel/ethosu/ethosu_job.c +++ b/drivers/accel/ethosu/ethosu_job.c @@ -296,6 +296,7 @@ int ethosu_job_init(struct ethosu_device *edev) struct device *dev = edev->base.dev; struct drm_sched_init_args args = { .ops = ðosu_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = 1, .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS), .name = dev_name(dev), -- cgit v1.2.3 From 05463d54efd770e4146dd7558e5227fe42ec8170 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:33 +0100 Subject: Revert "accel/rocket: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4f335bba019958e59c2a02c4d71b72a8457cc595. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-15-tvrtko.ursulin@igalia.com --- drivers/accel/rocket/rocket_job.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c index 2f1861f960cc..ac51bff39833 100644 --- a/drivers/accel/rocket/rocket_job.c +++ b/drivers/accel/rocket/rocket_job.c @@ -437,6 +437,7 @@ int rocket_job_init(struct rocket_core *core) { struct drm_sched_init_args args = { .ops = &rocket_sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = 1, .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS), .name = dev_name(core->dev), -- cgit v1.2.3 From aa82a25302570ec1a4ce331c43967addc4e091eb Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:34 +0100 Subject: Revert "accel/amdxdna: Remove drm_sched_init_args->num_rqs usage" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ac58121339db0178186d256a956bb65feb8b6e45. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-16-tvrtko.ursulin@igalia.com --- drivers/accel/amdxdna/aie2_ctx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 4b3a62aa8798..2b97d9707129 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -665,6 +665,7 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) struct amdxdna_dev *xdna = client->xdna; const struct drm_sched_init_args args = { .ops = &sched_ops, + .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = HWCTX_MAX_CMDS, .timeout = tdr_timeout_ms ? msecs_to_jiffies(tdr_timeout_ms) : -- cgit v1.2.3 From 67cf83ac8316ab6a866cbafc449069409b11f923 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:35 +0100 Subject: Revert "drm/sched: Embed run queue singleton into the scheduler" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 16e7698bc04d3dd19d95a688e4b0297a0e28a93b. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-17-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 6 ++---- drivers/gpu/drm/amd/amdgpu/amdgpu_job.c | 6 ++---- drivers/gpu/drm/amd/amdgpu/amdgpu_job.h | 5 +---- drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h | 8 ++------ drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c | 8 ++++---- drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c | 8 ++++---- drivers/gpu/drm/scheduler/sched_entity.c | 29 ++++++++++++++++++----------- drivers/gpu/drm/scheduler/sched_fence.c | 2 +- drivers/gpu/drm/scheduler/sched_internal.h | 3 ++- drivers/gpu/drm/scheduler/sched_main.c | 27 ++++++++++++++++++++++++--- drivers/gpu/drm/scheduler/sched_rq.c | 16 ++++++++-------- include/drm/gpu_scheduler.h | 5 ++++- 12 files changed, 72 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index f8bf0f6b5097..5e23bf11f046 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1123,8 +1123,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) if (p->gang_size > 1 && !adev->vm_manager.concurrent_flush) { for (i = 0; i < p->gang_size; ++i) { struct drm_sched_entity *entity = p->entities[i]; - struct drm_gpu_scheduler *sched = - container_of(entity->rq, typeof(*sched), rq); + struct drm_gpu_scheduler *sched = entity->rq->sched; struct amdgpu_ring *ring = to_amdgpu_ring(sched); if (amdgpu_vmid_uses_reserved(vm, ring->vm_hub)) @@ -1241,8 +1240,7 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) return r; } - sched = container_of(p->gang_leader->base.entity->rq, typeof(*sched), - rq); + sched = p->gang_leader->base.entity->rq->sched; while ((fence = amdgpu_sync_get_fence(&p->sync))) { struct drm_sched_fence *s_fence = to_drm_sched_fence(fence); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index 9ecc6387c1eb..152352ac34fa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -388,9 +388,7 @@ static struct dma_fence * amdgpu_job_prepare_job(struct drm_sched_job *sched_job, struct drm_sched_entity *s_entity) { - struct drm_gpu_scheduler *sched = - container_of(s_entity->rq, typeof(*sched), rq); - struct amdgpu_ring *ring = to_amdgpu_ring(sched); + struct amdgpu_ring *ring = to_amdgpu_ring(s_entity->rq->sched); struct amdgpu_job *job = to_amdgpu_job(sched_job); struct dma_fence *fence; int r; @@ -483,7 +481,7 @@ drm_sched_entity_queue_pop(struct drm_sched_entity *entity) void amdgpu_job_stop_all_jobs_on_sched(struct drm_gpu_scheduler *sched) { - struct drm_sched_rq *rq = &sched->rq; + struct drm_sched_rq *rq = sched->rq; struct drm_sched_entity *s_entity; struct drm_sched_job *s_job; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h index e70a1117b812..56a88e14a044 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h @@ -107,10 +107,7 @@ struct amdgpu_job { static inline struct amdgpu_ring *amdgpu_job_ring(struct amdgpu_job *job) { - struct drm_gpu_scheduler *sched = - container_of(job->base.entity->rq, typeof(*sched), rq); - - return to_amdgpu_ring(sched); + return to_amdgpu_ring(job->base.entity->rq->sched); } int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h index 85724ec6aaf8..d13e64a69e25 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h @@ -145,7 +145,6 @@ TRACE_EVENT(amdgpu_cs, struct amdgpu_ib *ib), TP_ARGS(p, job, ib), TP_STRUCT__entry( - __field(struct drm_gpu_scheduler *, sched) __field(struct amdgpu_bo_list *, bo_list) __field(u32, ring) __field(u32, dw) @@ -153,14 +152,11 @@ TRACE_EVENT(amdgpu_cs, ), TP_fast_assign( - __entry->sched = container_of(job->base.entity->rq, - typeof(*__entry->sched), - rq); __entry->bo_list = p->bo_list; - __entry->ring = to_amdgpu_ring(__entry->sched)->idx; + __entry->ring = to_amdgpu_ring(job->base.entity->rq->sched)->idx; __entry->dw = ib->length_dw; __entry->fences = amdgpu_fence_count_emitted( - to_amdgpu_ring(__entry->sched)); + to_amdgpu_ring(job->base.entity->rq->sched)); ), TP_printk("bo_list=%p, ring=%u, dw=%u, fences=%u", __entry->bo_list, __entry->ring, __entry->dw, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c index fd09a2b5a147..2eb64df6daa9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c @@ -106,13 +106,13 @@ static int amdgpu_vm_sdma_prepare(struct amdgpu_vm_update_params *p, static int amdgpu_vm_sdma_commit(struct amdgpu_vm_update_params *p, struct dma_fence **fence) { - struct drm_gpu_scheduler *sched = - container_of(p->vm->delayed.rq, typeof(*sched), rq); - struct amdgpu_ring *ring = - container_of(sched, struct amdgpu_ring, sched); struct amdgpu_ib *ib = p->job->ibs; + struct amdgpu_ring *ring; struct dma_fence *f; + ring = container_of(p->vm->delayed.rq->sched, struct amdgpu_ring, + sched); + WARN_ON(ib->length_dw == 0); amdgpu_ring_pad_ib(ring, ib); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c index 409e103ffe8c..42be8ee155dd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c @@ -466,15 +466,15 @@ int amdgpu_xcp_open_device(struct amdgpu_device *adev, void amdgpu_xcp_release_sched(struct amdgpu_device *adev, struct amdgpu_ctx_entity *entity) { - struct drm_gpu_scheduler *sched = - container_of(entity->entity.rq, typeof(*sched), rq); + struct drm_gpu_scheduler *sched; + struct amdgpu_ring *ring; if (!adev->xcp_mgr) return; + sched = entity->entity.rq->sched; if (drm_sched_wqueue_ready(sched)) { - struct amdgpu_ring *ring = to_amdgpu_ring(sched); - + ring = to_amdgpu_ring(entity->entity.rq->sched); atomic_dec(&adev->xcp_mgr->xcp[ring->xcp_id].ref_cnt); } } diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index 4ebb513255ed..c61c12078629 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -135,9 +135,19 @@ int drm_sched_entity_init(struct drm_sched_entity *entity, entity->last_user = current->group_leader; entity->num_sched_list = num_sched_list; entity->sched_list = num_sched_list > 1 ? sched_list : NULL; - entity->rq = &sched_list[0]->rq; RCU_INIT_POINTER(entity->last_scheduled, NULL); RB_CLEAR_NODE(&entity->rb_tree_node); + + if (!sched_list[0]->rq) { + /* Since every entry covered by num_sched_list + * should be non-NULL and therefore we warn drivers + * not to do this and to fix their DRM calling order. + */ + pr_warn("%s: called with uninitialized scheduler\n", __func__); + } else { + entity->rq = sched_list[0]->rq; + } + init_completion(&entity->entity_idle); /* We start in an idle state. */ @@ -336,7 +346,7 @@ long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout) if (!entity->rq) return 0; - sched = container_of(entity->rq, typeof(*sched), rq); + sched = entity->rq->sched; /* * The client will not queue more jobs during this fini - consume * existing queued ones, or discard them on SIGKILL. @@ -417,12 +427,10 @@ static void drm_sched_entity_wakeup(struct dma_fence *f, { struct drm_sched_entity *entity = container_of(cb, struct drm_sched_entity, cb); - struct drm_gpu_scheduler *sched = - container_of(entity->rq, typeof(*sched), rq); entity->dependency = NULL; dma_fence_put(f); - drm_sched_wakeup(sched); + drm_sched_wakeup(entity->rq->sched); } /** @@ -449,8 +457,7 @@ EXPORT_SYMBOL(drm_sched_entity_set_priority); static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity, struct drm_sched_job *sched_job) { - struct drm_gpu_scheduler *sched = - container_of(entity->rq, typeof(*sched), rq); + struct drm_gpu_scheduler *sched = entity->rq->sched; struct dma_fence *fence = entity->dependency; struct drm_sched_fence *s_fence; @@ -584,7 +591,7 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity) spin_lock(&entity->lock); sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list); - rq = sched ? &sched->rq : NULL; + rq = sched ? sched->rq : NULL; if (rq != entity->rq) { drm_sched_rq_remove_entity(entity->rq, entity); entity->rq = rq; @@ -608,8 +615,6 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity) void drm_sched_entity_push_job(struct drm_sched_job *sched_job) { struct drm_sched_entity *entity = sched_job->entity; - struct drm_gpu_scheduler *sched = - container_of(entity->rq, typeof(*sched), rq); bool first; trace_drm_sched_job_queue(sched_job, entity); @@ -621,7 +626,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) xa_for_each(&sched_job->dependencies, index, entry) trace_drm_sched_job_add_dep(sched_job, entry); } - atomic_inc(sched->score); + atomic_inc(entity->rq->sched->score); WRITE_ONCE(entity->last_user, current->group_leader); /* @@ -632,6 +637,8 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) /* first job wakes up scheduler */ if (first) { + struct drm_gpu_scheduler *sched; + sched = drm_sched_rq_add_entity(entity); if (sched) drm_sched_wakeup(sched); diff --git a/drivers/gpu/drm/scheduler/sched_fence.c b/drivers/gpu/drm/scheduler/sched_fence.c index 85ee3d694dc9..096fe28aa9c9 100644 --- a/drivers/gpu/drm/scheduler/sched_fence.c +++ b/drivers/gpu/drm/scheduler/sched_fence.c @@ -227,7 +227,7 @@ void drm_sched_fence_init(struct drm_sched_fence *fence, { unsigned seq; - fence->sched = container_of(entity->rq, typeof(*fence->sched), rq); + fence->sched = entity->rq->sched; seq = atomic_inc_return(&entity->fence_seq); dma_fence_init(&fence->scheduled, &drm_sched_fence_ops_scheduled, &fence->lock, entity->fence_context, seq); diff --git a/drivers/gpu/drm/scheduler/sched_internal.h b/drivers/gpu/drm/scheduler/sched_internal.h index 13ecb771d7a2..28c3f3df4f3a 100644 --- a/drivers/gpu/drm/scheduler/sched_internal.h +++ b/drivers/gpu/drm/scheduler/sched_internal.h @@ -35,7 +35,8 @@ bool drm_sched_can_queue(struct drm_gpu_scheduler *sched, struct drm_sched_entity *entity); void drm_sched_wakeup(struct drm_gpu_scheduler *sched); -void drm_sched_rq_init(struct drm_sched_rq *rq); +void drm_sched_rq_init(struct drm_gpu_scheduler *sched, + struct drm_sched_rq *rq); struct drm_gpu_scheduler * drm_sched_rq_add_entity(struct drm_sched_entity *entity); diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 818d3d4434b5..79e44ccefdc9 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -648,7 +648,7 @@ void drm_sched_job_arm(struct drm_sched_job *job) BUG_ON(!entity); drm_sched_entity_select_rq(entity); - sched = container_of(entity->rq, typeof(*sched), rq); + sched = entity->rq->sched; job->sched = sched; job->s_priority = entity->priority; @@ -1081,6 +1081,15 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_ sched->score = args->score ? args->score : &sched->_score; sched->dev = args->dev; + if (sched->rq) { + /* Not an error, but warn anyway so drivers can + * fine-tune their DRM calling order, and return all + * is good. + */ + dev_warn(sched->dev, "%s: scheduler already initialized!\n", __func__); + return 0; + } + if (args->submit_wq) { sched->submit_wq = args->submit_wq; sched->own_submit_wq = false; @@ -1092,7 +1101,11 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_ sched->own_submit_wq = true; } - drm_sched_rq_init(&sched->rq); + sched->rq = kzalloc_obj(*sched->rq); + if (!sched->rq) + goto Out_check_own; + + drm_sched_rq_init(sched, sched->rq); init_waitqueue_head(&sched->job_scheduled); INIT_LIST_HEAD(&sched->pending_list); @@ -1108,6 +1121,12 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_ sched->ready = true; return 0; + +Out_check_own: + if (sched->own_submit_wq) + destroy_workqueue(sched->submit_wq); + dev_err(sched->dev, "%s: Failed to setup GPU scheduler--out of memory\n", __func__); + return -ENOMEM; } EXPORT_SYMBOL(drm_sched_init); @@ -1153,6 +1172,8 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched) if (sched->own_submit_wq) destroy_workqueue(sched->submit_wq); sched->ready = false; + kfree(sched->rq); + sched->rq = NULL; if (!list_empty(&sched->pending_list)) dev_warn(sched->dev, "Tearing down scheduler while jobs are pending!\n"); @@ -1172,7 +1193,7 @@ void drm_sched_increase_karma(struct drm_sched_job *bad) { struct drm_gpu_scheduler *sched = bad->sched; struct drm_sched_entity *entity, *tmp; - struct drm_sched_rq *rq = &sched->rq; + struct drm_sched_rq *rq = sched->rq; /* don't change @bad's karma if it's from KERNEL RQ, * because sometimes GPU hang would cause kernel jobs (like VM updating jobs) diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c index 044546bcb5f8..aeeed6f3901d 100644 --- a/drivers/gpu/drm/scheduler/sched_rq.c +++ b/drivers/gpu/drm/scheduler/sched_rq.c @@ -85,15 +85,18 @@ static void drm_sched_rq_update_tree_locked(struct drm_sched_entity *entity, /** * drm_sched_rq_init - initialize a given run queue struct + * @sched: scheduler instance to associate with this run queue * @rq: scheduler run queue * * Initializes a scheduler runqueue. */ -void drm_sched_rq_init(struct drm_sched_rq *rq) +void drm_sched_rq_init(struct drm_gpu_scheduler *sched, + struct drm_sched_rq *rq) { spin_lock_init(&rq->lock); INIT_LIST_HEAD(&rq->entities); rq->rb_tree_root = RB_ROOT_CACHED; + rq->sched = sched; rq->head_prio = DRM_SCHED_PRIORITY_INVALID; } @@ -162,8 +165,7 @@ drm_sched_entity_restore_vruntime(struct drm_sched_entity *entity, enum drm_sched_priority rq_prio) { struct drm_sched_entity_stats *stats = entity->stats; - struct drm_gpu_scheduler *sched = - container_of(entity->rq, typeof(*sched), rq); + struct drm_gpu_scheduler *sched = entity->rq->sched; enum drm_sched_priority prio = entity->priority; unsigned long avg_us, sched_avg_us; ktime_t vruntime; @@ -263,8 +265,8 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity) } rq = entity->rq; - sched = container_of(rq, typeof(*sched), rq); spin_lock(&rq->lock); + sched = rq->sched; if (list_empty(&entity->list)) { atomic_inc(sched->score); @@ -291,8 +293,6 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity) void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, struct drm_sched_entity *entity) { - struct drm_gpu_scheduler *sched = container_of(rq, typeof(*sched), rq); - lockdep_assert_held(&entity->lock); if (list_empty(&entity->list)) @@ -300,7 +300,7 @@ void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, spin_lock(&rq->lock); - atomic_dec(sched->score); + atomic_dec(rq->sched->score); list_del_init(&entity->list); drm_sched_rq_remove_tree_locked(entity, rq); @@ -356,7 +356,7 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) struct drm_sched_entity * drm_sched_select_entity(struct drm_gpu_scheduler *sched) { - struct drm_sched_rq *rq = &sched->rq; + struct drm_sched_rq *rq = sched->rq; struct rb_node *rb; spin_lock(&rq->lock); diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index f1e15a80c844..80e5ae2d61ec 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -249,6 +249,7 @@ struct drm_sched_entity { /** * struct drm_sched_rq - queue of entities to be scheduled. * + * @sched: the scheduler to which this rq belongs to. * @lock: protects @entities, @rb_tree_root and @head_prio. * @entities: list of the entities to be scheduled. * @rb_tree_root: root of time based priority queue of entities for FIFO scheduling @@ -259,6 +260,8 @@ struct drm_sched_entity { * the next entity to emit commands from. */ struct drm_sched_rq { + struct drm_gpu_scheduler *sched; + spinlock_t lock; /* Following members are protected by the @lock: */ struct list_head entities; @@ -581,7 +584,7 @@ struct drm_gpu_scheduler { atomic_t credit_count; long timeout; const char *name; - struct drm_sched_rq rq; + struct drm_sched_rq *rq; wait_queue_head_t job_scheduled; atomic64_t job_id_count; struct workqueue_struct *submit_wq; -- cgit v1.2.3 From 9e9da8625427a80e1246f2df0f3ac18374045892 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:36 +0100 Subject: Revert "drm/sched: Remove FIFO and RR and simplify to a single run queue" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 77a6809f1dc39376116f8d769a0d2630dc95ad79. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-18-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_job.c | 25 ++++--- drivers/gpu/drm/scheduler/sched_entity.c | 29 ++++++-- drivers/gpu/drm/scheduler/sched_internal.h | 12 +++- drivers/gpu/drm/scheduler/sched_main.c | 112 +++++++++++++++++++++++------ drivers/gpu/drm/scheduler/sched_rq.c | 73 ++++++++++++++----- include/drm/gpu_scheduler.h | 36 ++++++++-- 6 files changed, 227 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index 152352ac34fa..07771721af9d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -481,22 +481,25 @@ drm_sched_entity_queue_pop(struct drm_sched_entity *entity) void amdgpu_job_stop_all_jobs_on_sched(struct drm_gpu_scheduler *sched) { - struct drm_sched_rq *rq = sched->rq; - struct drm_sched_entity *s_entity; struct drm_sched_job *s_job; + struct drm_sched_entity *s_entity = NULL; + int i; /* Signal all jobs not yet scheduled */ - spin_lock(&rq->lock); - list_for_each_entry(s_entity, &rq->entities, list) { - while ((s_job = drm_sched_entity_queue_pop(s_entity))) { - struct drm_sched_fence *s_fence = s_job->s_fence; - - dma_fence_signal(&s_fence->scheduled); - dma_fence_set_error(&s_fence->finished, -EHWPOISON); - dma_fence_signal(&s_fence->finished); + for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) { + struct drm_sched_rq *rq = sched->sched_rq[i]; + spin_lock(&rq->lock); + list_for_each_entry(s_entity, &rq->entities, list) { + while ((s_job = drm_sched_entity_queue_pop(s_entity))) { + struct drm_sched_fence *s_fence = s_job->s_fence; + + dma_fence_signal(&s_fence->scheduled); + dma_fence_set_error(&s_fence->finished, -EHWPOISON); + dma_fence_signal(&s_fence->finished); + } } + spin_unlock(&rq->lock); } - spin_unlock(&rq->lock); /* Signal all jobs already scheduled to HW */ list_for_each_entry(s_job, &sched->pending_list, list) { diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index c61c12078629..672b5c57ed8e 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -133,19 +133,37 @@ int drm_sched_entity_init(struct drm_sched_entity *entity, entity->guilty = guilty; entity->priority = priority; entity->last_user = current->group_leader; + entity->rq_priority = drm_sched_policy == DRM_SCHED_POLICY_FAIR ? + DRM_SCHED_PRIORITY_KERNEL : priority; entity->num_sched_list = num_sched_list; entity->sched_list = num_sched_list > 1 ? sched_list : NULL; RCU_INIT_POINTER(entity->last_scheduled, NULL); RB_CLEAR_NODE(&entity->rb_tree_node); - if (!sched_list[0]->rq) { + if (!sched_list[0]->sched_rq) { /* Since every entry covered by num_sched_list * should be non-NULL and therefore we warn drivers * not to do this and to fix their DRM calling order. */ pr_warn("%s: called with uninitialized scheduler\n", __func__); } else { - entity->rq = sched_list[0]->rq; + enum drm_sched_priority p = entity->priority; + + /* + * The "priority" of an entity cannot exceed the number of + * run-queues of a scheduler. Protect against num_rqs being 0, + * by converting to signed. Choose the lowest priority + * available. + */ + if (p >= sched_list[0]->num_user_rqs) { + dev_err(sched_list[0]->dev, "entity with out-of-bounds priority:%u num_user_rqs:%u\n", + p, sched_list[0]->num_user_rqs); + p = max_t(s32, + (s32)sched_list[0]->num_user_rqs - 1, + (s32)DRM_SCHED_PRIORITY_KERNEL); + entity->priority = p; + } + entity->rq = sched_list[0]->sched_rq[entity->rq_priority]; } init_completion(&entity->entity_idle); @@ -591,7 +609,7 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity) spin_lock(&entity->lock); sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list); - rq = sched ? sched->rq : NULL; + rq = sched ? sched->sched_rq[entity->rq_priority] : NULL; if (rq != entity->rq) { drm_sched_rq_remove_entity(entity->rq, entity); entity->rq = rq; @@ -616,6 +634,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) { struct drm_sched_entity *entity = sched_job->entity; bool first; + ktime_t submit_ts; trace_drm_sched_job_queue(sched_job, entity); @@ -632,14 +651,16 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) /* * After the sched_job is pushed into the entity queue, it may be * completed and freed up at any time. We can no longer access it. + * Make sure to set the submit_ts first, to avoid a race. */ + sched_job->submit_ts = submit_ts = ktime_get(); first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node); /* first job wakes up scheduler */ if (first) { struct drm_gpu_scheduler *sched; - sched = drm_sched_rq_add_entity(entity); + sched = drm_sched_rq_add_entity(entity, submit_ts); if (sched) drm_sched_wakeup(sched); } diff --git a/drivers/gpu/drm/scheduler/sched_internal.h b/drivers/gpu/drm/scheduler/sched_internal.h index 28c3f3df4f3a..a901801fce85 100644 --- a/drivers/gpu/drm/scheduler/sched_internal.h +++ b/drivers/gpu/drm/scheduler/sched_internal.h @@ -31,6 +31,13 @@ struct drm_sched_entity_stats { struct ewma_drm_sched_avgtime avg_job_us; }; +/* Used to choose between FIFO and RR job-scheduling */ +extern int drm_sched_policy; + +#define DRM_SCHED_POLICY_RR 0 +#define DRM_SCHED_POLICY_FIFO 1 +#define DRM_SCHED_POLICY_FAIR 2 + bool drm_sched_can_queue(struct drm_gpu_scheduler *sched, struct drm_sched_entity *entity); void drm_sched_wakeup(struct drm_gpu_scheduler *sched); @@ -39,13 +46,14 @@ void drm_sched_rq_init(struct drm_gpu_scheduler *sched, struct drm_sched_rq *rq); struct drm_gpu_scheduler * -drm_sched_rq_add_entity(struct drm_sched_entity *entity); +drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts); void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, struct drm_sched_entity *entity); void drm_sched_rq_pop_entity(struct drm_sched_entity *entity); struct drm_sched_entity * -drm_sched_select_entity(struct drm_gpu_scheduler *sched); +drm_sched_rq_select_entity(struct drm_gpu_scheduler *sched, + struct drm_sched_rq *rq); void drm_sched_entity_select_rq(struct drm_sched_entity *entity); struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity); diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 79e44ccefdc9..8127c93a1718 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -84,6 +84,15 @@ #define CREATE_TRACE_POINTS #include "gpu_scheduler_trace.h" +int drm_sched_policy = DRM_SCHED_POLICY_FAIR; + +/** + * DOC: sched_policy (int) + * Used to override default entities scheduling policy in a run queue. + */ +MODULE_PARM_DESC(sched_policy, "Specify the scheduling policy for entities on a run-queue, " __stringify(DRM_SCHED_POLICY_RR) " = Round Robin, " __stringify(DRM_SCHED_POLICY_FIFO) " = FIFO, " __stringify(DRM_SCHED_POLICY_FAIR) " = Fair (default)."); +module_param_named(sched_policy, drm_sched_policy, int, 0444); + static u32 drm_sched_available_credits(struct drm_gpu_scheduler *sched) { u32 credits; @@ -871,6 +880,34 @@ void drm_sched_wakeup(struct drm_gpu_scheduler *sched) drm_sched_run_job_queue(sched); } +/** + * drm_sched_select_entity - Select next entity to process + * + * @sched: scheduler instance + * + * Return an entity to process or NULL if none are found. + * + * Note, that we break out of the for-loop when "entity" is non-null, which can + * also be an error-pointer--this assures we don't process lower priority + * run-queues. See comments in the respectively called functions. + */ +static struct drm_sched_entity * +drm_sched_select_entity(struct drm_gpu_scheduler *sched) +{ + struct drm_sched_entity *entity = NULL; + int i; + + /* Start with the highest priority. + */ + for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) { + entity = drm_sched_rq_select_entity(sched, sched->sched_rq[i]); + if (entity) + break; + } + + return IS_ERR(entity) ? NULL : entity; +} + /** * drm_sched_get_finished_job - fetch the next finished job to be destroyed * @@ -996,7 +1033,7 @@ static void drm_sched_run_job_work(struct work_struct *w) /* Find entity with a ready job */ entity = drm_sched_select_entity(sched); - if (IS_ERR_OR_NULL(entity)) { + if (!entity) { /* * Either no more work to do, or the next ready job needs more * credits than the scheduler has currently available. @@ -1072,6 +1109,8 @@ static struct workqueue_struct *drm_sched_alloc_wq(const char *name) */ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_args *args) { + int i; + sched->ops = args->ops; sched->credit_limit = args->credit_limit; sched->name = args->name; @@ -1081,7 +1120,13 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_ sched->score = args->score ? args->score : &sched->_score; sched->dev = args->dev; - if (sched->rq) { + if (args->num_rqs > DRM_SCHED_PRIORITY_COUNT) { + /* This is a gross violation--tell drivers what the problem is. + */ + dev_err(sched->dev, "%s: num_rqs cannot be greater than DRM_SCHED_PRIORITY_COUNT\n", + __func__); + return -EINVAL; + } else if (sched->sched_rq) { /* Not an error, but warn anyway so drivers can * fine-tune their DRM calling order, and return all * is good. @@ -1101,11 +1146,19 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_ sched->own_submit_wq = true; } - sched->rq = kzalloc_obj(*sched->rq); - if (!sched->rq) + sched->num_user_rqs = args->num_rqs; + sched->num_rqs = drm_sched_policy != DRM_SCHED_POLICY_FAIR ? + args->num_rqs : 1; + sched->sched_rq = kzalloc_objs(*sched->sched_rq, args->num_rqs); + if (!sched->sched_rq) goto Out_check_own; - drm_sched_rq_init(sched, sched->rq); + for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) { + sched->sched_rq[i] = kzalloc_obj(*sched->sched_rq[i]); + if (!sched->sched_rq[i]) + goto Out_unroll; + drm_sched_rq_init(sched, sched->sched_rq[i]); + } init_waitqueue_head(&sched->job_scheduled); INIT_LIST_HEAD(&sched->pending_list); @@ -1121,7 +1174,12 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_ sched->ready = true; return 0; +Out_unroll: + for (--i ; i >= DRM_SCHED_PRIORITY_KERNEL; i--) + kfree(sched->sched_rq[i]); + kfree(sched->sched_rq); + sched->sched_rq = NULL; Out_check_own: if (sched->own_submit_wq) destroy_workqueue(sched->submit_wq); @@ -1157,8 +1215,13 @@ static void drm_sched_cancel_remaining_jobs(struct drm_gpu_scheduler *sched) */ void drm_sched_fini(struct drm_gpu_scheduler *sched) { + int i; + drm_sched_wqueue_stop(sched); + for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) + kfree(sched->sched_rq[i]); + /* Wakeup everyone stuck in drm_sched_entity_flush for this scheduler */ wake_up_all(&sched->job_scheduled); @@ -1172,8 +1235,8 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched) if (sched->own_submit_wq) destroy_workqueue(sched->submit_wq); sched->ready = false; - kfree(sched->rq); - sched->rq = NULL; + kfree(sched->sched_rq); + sched->sched_rq = NULL; if (!list_empty(&sched->pending_list)) dev_warn(sched->dev, "Tearing down scheduler while jobs are pending!\n"); @@ -1191,28 +1254,35 @@ EXPORT_SYMBOL(drm_sched_fini); */ void drm_sched_increase_karma(struct drm_sched_job *bad) { + int i; + struct drm_sched_entity *tmp; + struct drm_sched_entity *entity; struct drm_gpu_scheduler *sched = bad->sched; - struct drm_sched_entity *entity, *tmp; - struct drm_sched_rq *rq = sched->rq; /* don't change @bad's karma if it's from KERNEL RQ, * because sometimes GPU hang would cause kernel jobs (like VM updating jobs) * corrupt but keep in mind that kernel jobs always considered good. */ - if (bad->s_priority == DRM_SCHED_PRIORITY_KERNEL) - return; - - atomic_inc(&bad->karma); - - spin_lock(&rq->lock); - list_for_each_entry_safe(entity, tmp, &rq->entities, list) { - if (bad->s_fence->scheduled.context == entity->fence_context) { - if (entity->guilty) - atomic_set(entity->guilty, 1); - break; + if (bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) { + atomic_inc(&bad->karma); + + for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) { + struct drm_sched_rq *rq = sched->sched_rq[i]; + + spin_lock(&rq->lock); + list_for_each_entry_safe(entity, tmp, &rq->entities, list) { + if (bad->s_fence->scheduled.context == + entity->fence_context) { + if (entity->guilty) + atomic_set(entity->guilty, 1); + break; + } + } + spin_unlock(&rq->lock); + if (&entity->list != &rq->entities) + break; } } - spin_unlock(&rq->lock); } EXPORT_SYMBOL(drm_sched_increase_karma); diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c index aeeed6f3901d..0464d324d98d 100644 --- a/drivers/gpu/drm/scheduler/sched_rq.c +++ b/drivers/gpu/drm/scheduler/sched_rq.c @@ -49,7 +49,7 @@ static void drm_sched_rq_update_prio(struct drm_sched_rq *rq) rq->head_prio = prio; } -static void drm_sched_rq_remove_tree_locked(struct drm_sched_entity *entity, +static void drm_sched_rq_remove_fifo_locked(struct drm_sched_entity *entity, struct drm_sched_rq *rq) { lockdep_assert_held(&entity->lock); @@ -62,7 +62,7 @@ static void drm_sched_rq_remove_tree_locked(struct drm_sched_entity *entity, } } -static void drm_sched_rq_update_tree_locked(struct drm_sched_entity *entity, +static void drm_sched_rq_update_fifo_locked(struct drm_sched_entity *entity, struct drm_sched_rq *rq, ktime_t ts) { @@ -74,7 +74,7 @@ static void drm_sched_rq_update_tree_locked(struct drm_sched_entity *entity, lockdep_assert_held(&entity->lock); lockdep_assert_held(&rq->lock); - drm_sched_rq_remove_tree_locked(entity, rq); + drm_sched_rq_remove_fifo_locked(entity, rq); entity->oldest_job_waiting = ts; @@ -239,9 +239,15 @@ static ktime_t drm_sched_entity_update_vruntime(struct drm_sched_entity *entity) return runtime; } +static ktime_t drm_sched_entity_get_job_ts(struct drm_sched_entity *entity) +{ + return drm_sched_entity_update_vruntime(entity); +} + /** * drm_sched_rq_add_entity - add an entity * @entity: scheduler entity + * @ts: submission timestamp * * Adds a scheduler entity to the run queue. * @@ -249,11 +255,10 @@ static ktime_t drm_sched_entity_update_vruntime(struct drm_sched_entity *entity) * been stopped and cannot be submitted to. */ struct drm_gpu_scheduler * -drm_sched_rq_add_entity(struct drm_sched_entity *entity) +drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts) { struct drm_gpu_scheduler *sched; struct drm_sched_rq *rq; - ktime_t ts; /* Add the entity to the run queue */ spin_lock(&entity->lock); @@ -273,9 +278,15 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity) list_add_tail(&entity->list, &rq->entities); } - ts = drm_sched_rq_get_min_vruntime(rq); - ts = drm_sched_entity_restore_vruntime(entity, ts, rq->head_prio); - drm_sched_rq_update_tree_locked(entity, rq, ts); + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { + ts = drm_sched_rq_get_min_vruntime(rq); + ts = drm_sched_entity_restore_vruntime(entity, ts, + rq->head_prio); + } else if (drm_sched_policy == DRM_SCHED_POLICY_RR) { + ts = entity->rr_ts; + } + + drm_sched_rq_update_fifo_locked(entity, rq, ts); spin_unlock(&rq->lock); spin_unlock(&entity->lock); @@ -303,11 +314,27 @@ void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, atomic_dec(rq->sched->score); list_del_init(&entity->list); - drm_sched_rq_remove_tree_locked(entity, rq); + drm_sched_rq_remove_fifo_locked(entity, rq); spin_unlock(&rq->lock); } +static ktime_t +drm_sched_rq_next_rr_ts(struct drm_sched_rq *rq, + struct drm_sched_entity *entity) +{ + ktime_t ts; + + lockdep_assert_held(&entity->lock); + lockdep_assert_held(&rq->lock); + + ts = ktime_add_ns(rq->rr_ts, 1); + entity->rr_ts = ts; + rq->rr_ts = ts; + + return ts; +} + /** * drm_sched_rq_pop_entity - pops an entity * @entity: scheduler entity @@ -330,22 +357,32 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) if (next_job) { ktime_t ts; - ts = drm_sched_entity_update_vruntime(entity); - drm_sched_rq_update_tree_locked(entity, rq, ts); + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) + ts = drm_sched_entity_get_job_ts(entity); + else if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) + ts = next_job->submit_ts; + else + ts = drm_sched_rq_next_rr_ts(rq, entity); + + drm_sched_rq_update_fifo_locked(entity, rq, ts); } else { - ktime_t min_vruntime; + drm_sched_rq_remove_fifo_locked(entity, rq); - drm_sched_rq_remove_tree_locked(entity, rq); - min_vruntime = drm_sched_rq_get_min_vruntime(rq); - drm_sched_entity_save_vruntime(entity, min_vruntime); + if (drm_sched_policy == DRM_SCHED_POLICY_FAIR) { + ktime_t min_vruntime; + + min_vruntime = drm_sched_rq_get_min_vruntime(rq); + drm_sched_entity_save_vruntime(entity, min_vruntime); + } } spin_unlock(&rq->lock); spin_unlock(&entity->lock); } /** - * drm_sched_select_entity - Select an entity which provides a job to run + * drm_sched_rq_select_entity - Select an entity which provides a job to run * @sched: the gpu scheduler + * @rq: scheduler run queue to check. * * Find oldest waiting ready entity. * @@ -354,9 +391,9 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity) * its job; return NULL, if no ready entity was found. */ struct drm_sched_entity * -drm_sched_select_entity(struct drm_gpu_scheduler *sched) +drm_sched_rq_select_entity(struct drm_gpu_scheduler *sched, + struct drm_sched_rq *rq) { - struct drm_sched_rq *rq = sched->rq; struct rb_node *rb; spin_lock(&rq->lock); diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 80e5ae2d61ec..790f7ecb6b85 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -100,7 +100,8 @@ struct drm_sched_entity { * @lock: * * Lock protecting the run-queue (@rq) to which this entity belongs, - * @priority and the list of schedulers (@sched_list, @num_sched_list). + * @priority, the list of schedulers (@sched_list, @num_sched_list) and + * the @rr_ts field. */ spinlock_t lock; @@ -153,6 +154,18 @@ struct drm_sched_entity { */ enum drm_sched_priority priority; + /** + * @rq_priority: Run-queue priority + */ + enum drm_sched_priority rq_priority; + + /** + * @rr_ts: + * + * Fake timestamp of the last popped job from the entity. + */ + ktime_t rr_ts; + /** * @job_queue: the list of jobs of this entity. */ @@ -250,7 +263,8 @@ struct drm_sched_entity { * struct drm_sched_rq - queue of entities to be scheduled. * * @sched: the scheduler to which this rq belongs to. - * @lock: protects @entities, @rb_tree_root and @head_prio. + * @lock: protects @entities, @rb_tree_root, @rr_ts and @head_prio. + * @rr_ts: monotonically incrementing fake timestamp for RR mode. * @entities: list of the entities to be scheduled. * @rb_tree_root: root of time based priority queue of entities for FIFO scheduling * @head_prio: priority of the top tree element. @@ -264,6 +278,7 @@ struct drm_sched_rq { spinlock_t lock; /* Following members are protected by the @lock: */ + ktime_t rr_ts; struct list_head entities; struct rb_root_cached rb_tree_root; enum drm_sched_priority head_prio; @@ -349,6 +364,13 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * to schedule the job. */ struct drm_sched_job { + /** + * @submit_ts: + * + * When the job was pushed into the entity queue. + */ + ktime_t submit_ts; + /** * @sched: * @@ -552,7 +574,11 @@ struct drm_sched_backend_ops { * @credit_count: the current credit count of this scheduler * @timeout: the time after which a job is removed from the scheduler. * @name: name of the ring for which this scheduler is being used. - * @rq: Scheduler run queue. + * @num_user_rqs: Number of run-queues. This is at most + * DRM_SCHED_PRIORITY_COUNT, as there's usually one run-queue per + * priority, but could be less. + * @num_rqs: Equal to @num_user_rqs for FIFO and RR and 1 for the FAIR policy. + * @sched_rq: An allocated array of run-queues of size @num_rqs; * @job_scheduled: once drm_sched_entity_flush() is called the scheduler * waits on this wait queue until all the scheduled jobs are * finished. @@ -584,7 +610,9 @@ struct drm_gpu_scheduler { atomic_t credit_count; long timeout; const char *name; - struct drm_sched_rq *rq; + u32 num_rqs; + u32 num_user_rqs; + struct drm_sched_rq **sched_rq; wait_queue_head_t job_scheduled; atomic64_t job_id_count; struct workqueue_struct *submit_wq; -- cgit v1.2.3 From 2bbea6b819be0a9e8c450375f39a542088f510eb Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:37 +0100 Subject: Revert "drm/sched: Switch default policy to fair" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 45c211ddf92a1f9b4214ffadaf70d9037f53aaf6. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-19-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/scheduler/sched_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 8127c93a1718..337db7b1e688 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -84,13 +84,13 @@ #define CREATE_TRACE_POINTS #include "gpu_scheduler_trace.h" -int drm_sched_policy = DRM_SCHED_POLICY_FAIR; +int drm_sched_policy = DRM_SCHED_POLICY_FIFO; /** * DOC: sched_policy (int) * Used to override default entities scheduling policy in a run queue. */ -MODULE_PARM_DESC(sched_policy, "Specify the scheduling policy for entities on a run-queue, " __stringify(DRM_SCHED_POLICY_RR) " = Round Robin, " __stringify(DRM_SCHED_POLICY_FIFO) " = FIFO, " __stringify(DRM_SCHED_POLICY_FAIR) " = Fair (default)."); +MODULE_PARM_DESC(sched_policy, "Specify the scheduling policy for entities on a run-queue, " __stringify(DRM_SCHED_POLICY_RR) " = Round Robin, " __stringify(DRM_SCHED_POLICY_FIFO) " = FIFO (default), " __stringify(DRM_SCHED_POLICY_FAIR) " = Fair."); module_param_named(sched_policy, drm_sched_policy, int, 0444); static u32 drm_sched_available_credits(struct drm_gpu_scheduler *sched) -- cgit v1.2.3 From 9a11db68872055e6ead919bad04d6330851c522d Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 11 Aug 2026 17:31:38 +0100 Subject: drm/sched: Mark fair policy as experimental MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark the fair policy as experimental until reported regressions are addressed. Signed-off-by: Tvrtko Ursulin Cc: Luke.Wildhardt@proton.me Cc: Matthew Brost Cc: Danilo Krummrich Cc: Philipp Stanner Cc: Christian König Signed-off-by: Tvrtko Ursulin Link: https://lore.kernel.org/r/20260811163139.99746-20-tvrtko.ursulin@igalia.com --- drivers/gpu/drm/scheduler/sched_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 337db7b1e688..214ed02cb496 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -90,7 +90,7 @@ int drm_sched_policy = DRM_SCHED_POLICY_FIFO; * DOC: sched_policy (int) * Used to override default entities scheduling policy in a run queue. */ -MODULE_PARM_DESC(sched_policy, "Specify the scheduling policy for entities on a run-queue, " __stringify(DRM_SCHED_POLICY_RR) " = Round Robin, " __stringify(DRM_SCHED_POLICY_FIFO) " = FIFO (default), " __stringify(DRM_SCHED_POLICY_FAIR) " = Fair."); +MODULE_PARM_DESC(sched_policy, "Specify the scheduling policy for entities on a run-queue, " __stringify(DRM_SCHED_POLICY_RR) " = Round Robin, " __stringify(DRM_SCHED_POLICY_FIFO) " = FIFO (default), " __stringify(DRM_SCHED_POLICY_FAIR) " = Fair (experimental)."); module_param_named(sched_policy, drm_sched_policy, int, 0444); static u32 drm_sched_available_credits(struct drm_gpu_scheduler *sched) -- cgit v1.2.3 From 6bcd76c134c55c697148acb5c0194e9666abdf84 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 10 Aug 2026 15:04:47 +0000 Subject: af_packet: Don't send zero-byte data in tpacket_snd(). syzbot reported a WARNING in __dev_queue_xmit() triggered via tpacket_snd(): skb_assert_len WARNING: at include/linux/skbuff.h:2753 skb_assert_len WARNING: at __dev_queue_xmit+0x21bc/0x4970 net/core/dev.c:4781 Call Trace: dev_queue_xmit include/linux/netdevice.h:3448 [inline] packet_xmit+0x243/0x310 net/packet/af_packet.c:276 tpacket_snd net/packet/af_packet.c:2907 [inline] packet_sendmsg+0x28d6/0x4eb0 net/packet/af_packet.c:3134 When sending 0-byte packets via TPACKET ring buffer on devices with no hard header (e.g. dev->hard_header_len == 0), tpacket_fill_skb() populates an skb with skb->len == 0 and returns 0. tpacket_snd() then forwards this empty skb to packet_xmit(), causing __dev_queue_xmit() to hit skb_assert_len(skb). Similar checks exist in packet_snd() via commit dc633700f00f ("net/af_packet: check len when min_header_len equals to 0") and in packet_sendmsg_spkt() via commit 6a341729fb31 ("af_packet: Don't send zero-byte data in packet_sendmsg_spkt()."). Return -EINVAL in tpacket_fill_skb() when skb->len is zero to reject zero-length packets in tpacket_snd(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot+30b93b6845b19cc38581@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a79e807.01d0871a.3a0d52.00ac.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Willem de Bruijn Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260810150447.1220864-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/packet/af_packet.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 213a8a3b7ee5..1168bd6b09cd 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2660,6 +2660,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, len = ((to_write > len_max) ? len_max : to_write); } + if (unlikely(!skb->len)) + return -EINVAL; + packet_parse_headers(skb, sock); return tp_len; -- cgit v1.2.3 From f60b396ee174206fe08ebf997d16cd3801b77b22 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 9 Aug 2026 05:09:28 -0400 Subject: net/sched: act_api: fix TOCTOU NULL deref on a->goto_chain tcf_action_exec() handles TC_ACT_GOTO_CHAIN by first checking rcu_access_pointer(a->goto_chain) and then calling tcf_action_goto_chain_exec(), which does a second, independent rcu_dereference_bh(a->goto_chain) read and immediately dereferences chain->filter_chain. A concurrent tcf_action_set_ctrlact() (e.g. the gact replace path) can clear a->goto_chain between the two reads, so the second read returns NULL and tcf_action_goto_chain_exec() dereferences NULL. Fix the race by doing a single rcu_dereference_bh() read of a->goto_chain in tcf_action_exec(), checking it once for NULL, and passing the resulting chain pointer into tcf_action_goto_chain_exec(). This turns the split check/use into a single check/use on one value. Fixes: ee3bbfe806cd ("net/sched: let actions use RCU to access 'goto_chain'") Reported-by: vega@nebusec.ai Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Reviewed-by: Davide Caratti Link: https://patch.msgid.link/20260809090928.868186-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski --- net/sched/act_api.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f141634df214..600b7804befd 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -41,11 +41,9 @@ int tcf_dev_queue_xmit(struct sk_buff *skb, int (*xmit)(struct sk_buff *skb)) } EXPORT_SYMBOL_GPL(tcf_dev_queue_xmit); -static void tcf_action_goto_chain_exec(const struct tc_action *a, +static void tcf_action_goto_chain_exec(const struct tcf_chain *chain, struct tcf_result *res) { - const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain); - res->goto_tp = rcu_dereference_bh(chain->filter_chain); } @@ -1170,12 +1168,14 @@ repeat: return TC_ACT_OK; } } else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) { - if (unlikely(!rcu_access_pointer(a->goto_chain))) { + struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain); + + if (unlikely(!chain)) { tcf_set_drop_reason(skb, SKB_DROP_REASON_TC_CHAIN_NOTFOUND); return TC_ACT_SHOT; } - tcf_action_goto_chain_exec(a, res); + tcf_action_goto_chain_exec(chain, res); } if (ret != TC_ACT_PIPE) -- cgit v1.2.3 From 6bf14575c65569dcded90ef78afb8a6d57323f04 Mon Sep 17 00:00:00 2001 From: Jordan Rhee Date: Fri, 7 Aug 2026 22:43:14 +0000 Subject: gve: fix zero-length skb frag with header-split When header split is enabled and a header-only packet is received such as a pure TCP ACK, GVE will indicate an RX SKB with a zero-length fragment. If this SKB is then hairpinned and sent back out, the GVE TX path will emit a zero-length descriptor. Hardware considers this an illegal descriptor and stops the queue, causing a TX timeout and interface reset. Fix it by not adding the zero-length skb frag. Cc: stable@vger.kernel.org Fixes: 5e37d8254e7f ("gve: Add header split data path") Suggested-by: Praveen Kaligineedi Co-developed-by: Ziwei Xiao Signed-off-by: Ziwei Xiao Signed-off-by: Jordan Rhee Signed-off-by: Harshitha Ramamurthy Link: https://patch.msgid.link/20260807224315.234152-2-hramamurthy@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/google/gve/gve_rx_dqo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index 8271f731a91f..0ece2f6fdffb 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -886,6 +886,11 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx, rx->rx_hsplit_unsplit_pkt += unsplit; rx->rx_hsplit_bytes += hdr_len; u64_stats_update_end(&rx->statss); + + if (!buf_len) { + gve_free_buffer(rx, buf_state); + return 0; + } } else if (!rx->ctx.skb_head && rx->dqo.page_pool && netmem_is_net_iov(buf_state->page_info.netmem)) { /* when header split is disabled, the header went to the packet -- cgit v1.2.3 From 3992ced109c70b771efad9e51ae68e5c7a04dea3 Mon Sep 17 00:00:00 2001 From: Jordan Rhee Date: Fri, 7 Aug 2026 22:43:15 +0000 Subject: gve: fix NULL dereference due to missing ptp adjfine Fix NULL dereference due to missing implementation of adjfine, which can be triggered from usermode as follows: sudo ./testptp -d /dev/ptp0 -f 0 [ 551.943697] BUG: kernel NULL pointer dereference, address: 0000000000000000 [...] [ 552.061946] Call Trace: [ 552.064487] [ 552.066681] ptp_clock_adjtime+0x1c0/0x2c0 [ 552.070874] ? get_clock_desc+0x6b/0xb0 [ 552.074825] pc_clock_adjtime+0x78/0xc0 [ 552.078755] __do_sys_clock_adjtime+0x85/0x110 [ 552.083293] do_syscall_64+0xea/0x610 Cc: stable@vger.kernel.org Fixes: acd16380523b ("gve: Add initial PTP device support") Signed-off-by: Jordan Rhee Signed-off-by: Harshitha Ramamurthy Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260807224315.234152-3-hramamurthy@google.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/google/gve/gve_ptp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/google/gve/gve_ptp.c b/drivers/net/ethernet/google/gve/gve_ptp.c index 06b1cf4a5efc..1d6c59f4ead3 100644 --- a/drivers/net/ethernet/google/gve/gve_ptp.c +++ b/drivers/net/ethernet/google/gve/gve_ptp.c @@ -26,6 +26,11 @@ int gve_clock_nic_ts_read(struct gve_priv *priv) return 0; } +static int gve_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + return -EOPNOTSUPP; +} + static int gve_ptp_gettimex64(struct ptp_clock_info *info, struct timespec64 *ts, struct ptp_system_timestamp *sts) @@ -60,6 +65,7 @@ out: static const struct ptp_clock_info gve_ptp_caps = { .owner = THIS_MODULE, .name = "gve clock", + .adjfine = gve_ptp_adjfine, .gettimex64 = gve_ptp_gettimex64, .settime64 = gve_ptp_settime64, .do_aux_work = gve_ptp_do_aux_work, -- cgit v1.2.3 From 6d3724e616faf952c3adcf8414fc21a828ef3709 Mon Sep 17 00:00:00 2001 From: Zhang Changzhong Date: Fri, 7 Aug 2026 15:50:38 +0800 Subject: net/sched: cls_u32: skip hash tables in u32_bind_class() u32_walk() enumerates both struct tc_u_hnode and struct tc_u_knode through the walker callback. u32_bind_class() unconditionally casts the passed fh to tc_u_knode and accesses &n->res, so when fh is actually a tc_u_hnode, which has no tcf_result member, this results in a slab-out-of-bounds read of res->classid in tc_cls_bind_class(). The issue can be reproduced with the following commands: tc qdisc add dev lo root handle 1: hfsc tc class add dev lo parent 1: classid 1:1 hfsc sc rate 1000kbit tc filter add dev lo parent 1:1 protocol ip prio 1 u32 match u32 0 0 flowid 1:1 tc class add dev lo parent 1: classid 1:2 hfsc sc rate 2000kbit Fix this by skipping hash tables via the TC_U32_KEY(handle) check. Fixes: 07d79fc7d94e ("net_sched: add reverse binding for tc class") Signed-off-by: Zhang Changzhong Acked-by: Jamal Hadi Salim Link: https://patch.msgid.link/1786089038-36366-1-git-send-email-zhangchangzhong@huawei.com Signed-off-by: Jakub Kicinski --- net/sched/cls_u32.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index ac98b1c2144a..c297d7dbcf91 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1346,6 +1346,9 @@ static void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q, { struct tc_u_knode *n = fh; + if (TC_U32_KEY(n->handle) == 0) + return; + tc_cls_bind_class(classid, cl, q, &n->res, base); } -- cgit v1.2.3 From 090a95dbe13df9965279b588d97eda134831769c Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 8 Aug 2026 17:05:06 +0200 Subject: pmdomain: mediatek: mfg: initialize prev_o in mtk_mfg_attach_dev() mtk_mfg_attach_dev() reads prev_o on the first iteration of its loop, in "if (prev_o && prev_o->freq == o->freq)", before prev_o is assigned at the end of the loop body. On that first iteration, evaluating prev_o reads an indeterminate value. If it is non-NULL, the condition dereferences a stale or invalid pointer, potentially faulting or incorrectly skipping the first OPP. Initialize prev_o to NULL. This matches the intent as well: there is no previous OPP to compare against on the first iteration. Found with Clang's -Wconditional-uninitialized. Fixes: f08e7a4e8d6ac ("pmdomain: mediatek: Add support for MFlexGraphics") Assisted-by: Claude:claude-fable-5 Signed-off-by: Karl Mehltretter Reviewed-by: Nicolas Frattaroli Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c index 53bdab66cf15..9a8a38d98cce 100644 --- a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c +++ b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c @@ -657,7 +657,7 @@ static int mtk_mfg_attach_dev(struct generic_pm_domain *pd, struct device *dev) struct mtk_mfg *mfg = mtk_mfg_from_genpd(pd); struct dev_pm_opp_data *so = mfg->stack_opps; struct dev_pm_opp_data *go = mfg->gpu_opps; - struct dev_pm_opp_data *prev_o; + struct dev_pm_opp_data *prev_o = NULL; struct dev_pm_opp_data *o; int i, ret; -- cgit v1.2.3 From b64a9f67e082e04835ddd69d422a25168d69375b Mon Sep 17 00:00:00 2001 From: Jérémy Jean Date: Tue, 11 Aug 2026 19:10:11 +0000 Subject: pid: reject allocations through dead ancestor pid namespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alloc_pid() checks PIDNS_ADDING only on the leaf pid namespace before making a new struct pid visible in every ancestor namespace. That is insufficient when an unborn descendant pid namespace outlives an ancestor whose init task has already exited. The descendant can still be initialized later through setns(), and the new pid is then published into the dead ancestor as well. Keep the existing ENOMEM behavior, but require PIDNS_ADDING to be set in every namespace that will receive the new pid before publishing any of them. This preserves the invariant that free_pid() never decrements pid_allocated in a namespace whose child_reaper is no longer live. Fixes: a3bdc23ba8ea ("pid_namespace: allow opening pid_for_children before init was created") Signed-off-by: Jérémy Jean Reviewed-by: Pavel Tikhomirov Signed-off-by: Christian Brauner (Amutable) --- kernel/pid.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/pid.c b/kernel/pid.c index f55189a3d07d..d01d0dd7114b 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -324,8 +324,10 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid, * error path may try to wakeup the possibly freed ns->child_reaper. */ retval = -ENOMEM; - if (unlikely(!(ns->pid_allocated & PIDNS_ADDING))) - goto out_free; + for (upid = pid->numbers + ns->level; upid >= pid->numbers; --upid) + if (unlikely(!(upid->ns->pid_allocated & PIDNS_ADDING))) + goto out_free; + for (upid = pid->numbers + ns->level; upid >= pid->numbers; --upid) { /* Make the PID visible to find_pid_ns. */ idr_replace(&upid->ns->idr, pid, upid->nr); -- cgit v1.2.3 From 1fd495ef09eef96169a379a749c24b5e69974bb8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 31 Jul 2026 11:49:49 +0200 Subject: m68k: Define NR_CPUS to 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a Kconfig warning fs/erofs/Kconfig:137:warning: range is invalid which originates from EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS using NR_CPUS which up to now didn't exist for ARCH=m68k. All other architectures define this symbol, so fix the outlier. [geert] This also fixes: - CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS being set to the literal NR_CPUS instead of a number by automatic configs like "make allmodconfig" or "make olddefconfig", - An infinite loop in manual configs like "make oldconfig" when CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS is not present or has an invalid value in your existing .config. Fixes: c9b47e6b2311 ("erofs: cap LZMA stream pool size") Signed-off-by: Uwe Kleine-König Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260731094950.1988084-2-ukleinek@kernel.org Signed-off-by: Geert Uytterhoeven --- arch/m68k/Kconfig.cpu | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/m68k/Kconfig.cpu b/arch/m68k/Kconfig.cpu index 148f8a79d206..5a58cc13229e 100644 --- a/arch/m68k/Kconfig.cpu +++ b/arch/m68k/Kconfig.cpu @@ -549,6 +549,10 @@ config CACHE_COPYBACK endchoice endif # HAVE_CACHE_CB +config NR_CPUS + int + default "1" + # Coldfire cores that do not have a data cache configured can do coherent DMA. config COLDFIRE_COHERENT_DMA bool -- cgit v1.2.3 From 931cd1d1baeae68e8eb2c23bc1f3d8934dca6241 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Thu, 6 Aug 2026 12:45:24 +0800 Subject: drm/amdgpu: disallow multiple FENCE chunks in one submit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_cs_pass1() dispatches on chunk_id once per chunk without rejecting repeated ids. p->uf_bo is a single-slot field, so a submission carrying two AMDGPU_CHUNK_ID_FENCE chunks runs amdgpu_cs_p1_user_fence() twice, and the second run overwrites p->uf_bo with a freshly referenced BO without dropping the reference taken by the first. amdgpu_cs_parser_fini() only unrefs the final p->uf_bo, so every FENCE chunk but the last leaks a BO reference. The leaked BO outlives handle close and process exit. Reject duplicate FENCE chunks the same way commit fec5f8e8c6bc ("drm/amdgpu: disallow multiple BO_HANDLES chunks in one submit") did for p->bo_list. Fixes: d38ceaf99ed0 ("drm/amdgpu: add core driver (v4)") Reported-by: Yuhao Jiang Assisted-by: Claude:claude-opus-5 Cc: stable@vger.kernel.org Reviewed-by: Christian König Signed-off-by: Junrui Luo Signed-off-by: Alex Deucher (cherry picked from commit 665b1fc2a1845206408f9a2c6da67101789edb82) --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index f8bf0f6b5097..6798f6decc37 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -248,6 +248,10 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, if (size < sizeof(struct drm_amdgpu_cs_chunk_fence)) goto free_partial_kdata; + /* Only a single user fence is allowed to simplify handling. */ + if (p->uf_bo) + goto free_partial_kdata; + ret = amdgpu_cs_p1_user_fence(p, p->chunks[i].kdata, &uf_offset); if (ret) -- cgit v1.2.3 From 5e9d136ad74df4edec67e502ce267597064d8f86 Mon Sep 17 00:00:00 2001 From: Candice Li Date: Mon, 27 Jul 2026 11:51:37 +0800 Subject: drm/amdgpu: validate GEM_CREATE domain combinations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AMDGPU_GEM_CREATE checked domain bits against AMDGPU_GEM_DOMAIN_MASK, but did not validate domain combinations. Userspace could combine CPU|GTT|VRAM with DOORBELL, GDS, GWS, or OA, making amdgpu_bo_placement_from_domain() exceed AMDGPU_BO_MAX_PLACEMENTS and hit BUG_ON(). Allow combinations only within CPU/GTT/VRAM, and require non-CPU/GTT/ VRAM domains to be specified one at a time. Return -EINVAL for invalid combinations in amdgpu_gem_create_ioctl(). v2: Rename helper from amdgpu_gem_domain_valid() to amdgpu_gem_are_domains_valid() (Christian) Signed-off-by: Candice Li Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit db39852d0c39843cb02048dfb47e4b8c703e9080) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 6a0699746fbc..f754a4a3a1c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -397,6 +397,25 @@ const struct drm_gem_object_funcs amdgpu_gem_object_funcs = { .vm_ops = &amdgpu_gem_vm_ops, }; +static bool amdgpu_gem_are_domains_valid(u32 domains) +{ + u32 normal = AMDGPU_GEM_DOMAIN_CPU | + AMDGPU_GEM_DOMAIN_GTT | + AMDGPU_GEM_DOMAIN_VRAM; + /* Treat all non CPU/GTT/VRAM domains as special domains. */ + u32 special = AMDGPU_GEM_DOMAIN_MASK & ~normal; + u32 normal_mask = domains & normal; + u32 special_mask = domains & special; + + if (!special_mask) + return true; + + if (normal_mask) + return false; + + return !(special_mask & (special_mask - 1)); +} + /* * GEM ioctls. */ @@ -421,6 +440,8 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, /* reject invalid gem domains */ if (args->in.domains & ~AMDGPU_GEM_DOMAIN_MASK) return -EINVAL; + if (!amdgpu_gem_are_domains_valid(args->in.domains)) + return -EINVAL; if (!amdgpu_is_tmz(adev) && (flags & AMDGPU_GEM_CREATE_ENCRYPTED)) { DRM_NOTE_ONCE("Cannot allocate secure buffer since TMZ is disabled\n"); -- cgit v1.2.3 From f2a1c4c6fe0a6fcde02e59dde546dba28d283635 Mon Sep 17 00:00:00 2001 From: Samuel Pitoiset Date: Fri, 7 Aug 2026 16:58:55 +0200 Subject: drm/amd/display: Fix NULL pointer dereference in amdgpu_dm_crtc_set_vblank() amdgpu_dm_crtc_set_vblank() dereferences acrtc_state->stream when vblank is enabled/queried from DRM_IOCTL_MODE_CRTC_GET_SEQUENCE before a stream is attached to it. BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: amdgpu_dm_crtc_set_vblank+0x6b/0x4d0 [amdgpu] Call Trace: drm_vblank_enable drm_vblank_get drm_crtc_get_sequence_ioctl drm_ioctl_kernel drm_ioctl Reproduced by running VKCTS with WSI tests enabled on RADV. Guard the enable path on acrtc_state->stream being non-NULL, matching the existing checks in this function. Fixes: 34d66bc7ff10 ("drm/amd/display: Fix Xorg desktop unresponsive on Replay panel") Reviewed-by: Melissa Wen Signed-off-by: Samuel Pitoiset Signed-off-by: Alex Deucher (cherry picked from commit 7b1b31bf6942e6f43509b48da23f8e27269aac39) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 06598273d481..56bf907f1f6c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -257,7 +257,7 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) irq_type = amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id); - if (enable) { + if (enable && acrtc_state->stream) { struct dc *dc = adev->dm.dc; struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); struct psr_settings *psr = &acrtc_state->stream->link->psr_settings; -- cgit v1.2.3 From 47cd31185090bd1439d4587b835ac22d7ba6f1e3 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Jun 2026 17:46:19 -0400 Subject: drm/amdgpu: fix missing check in vm_flush() We shouldn't return early if we need to emit spm update. Reviewed-by: David Rosca Signed-off-by: Alex Deucher (cherry picked from commit 54a118f1d7e184fcbb18f83889f48f17a767878a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index dc6a9d7dd0b2..1baad7624f1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -800,6 +800,7 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, mutex_unlock(&id_mgr->lock); gds_switch_needed &= !!ring->funcs->emit_gds_switch; + spm_update_needed &= !!adev->gfx.rlc.funcs->update_spm_vmid; vm_flush_needed &= !!ring->funcs->emit_vm_flush && job->vm_pd_addr != AMDGPU_BO_INVALID_OFFSET; pasid_mapping_needed &= adev->gmc.gmc_funcs->emit_pasid_mapping && @@ -811,7 +812,7 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, &job->base.s_fence->scheduled == isolation->spearhead; if (!vm_flush_needed && !gds_switch_needed && !need_pipe_sync && - !cleaner_shader_needed) + !cleaner_shader_needed && !spm_update_needed) return; amdgpu_ring_ib_begin(ring); -- cgit v1.2.3 From 587be7a17358ef8c0106775fcedae5a7bef50735 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 8 Aug 2026 21:59:42 +0800 Subject: drm/radeon: fix autosuspend cleanup during teardown radeon_driver_load_kms() calls pm_runtime_use_autosuspend() for PX devices, but radeon_driver_unload_kms() does not call the matching pm_runtime_dont_use_autosuspend() during teardown. If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during teardown, this reference is not dropped. The documentation for pm_runtime_use_autosuspend() also notes that it is important to undo it with pm_runtime_dont_use_autosuspend() at driver exit time, unless runtime PM was initially enabled with devm_pm_runtime_enable(). Add the missing pm_runtime_dont_use_autosuspend() call to the driver unload path. This issue was found by manual code inspection. Fixes: 10ebc0bc0934 ("drm/radeon: add runtime PM support (v2)") Signed-off-by: Guangshuo Li Signed-off-by: Alex Deucher (cherry picked from commit 0fdc1ff82ea14844c22795e9e0813c3ca03235e1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/radeon_kms.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index dc43fd790a9c..4a99c09f4164 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -71,6 +71,7 @@ void radeon_driver_unload_kms(struct drm_device *dev) if (radeon_is_px(dev)) { pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); + pm_runtime_dont_use_autosuspend(dev->dev); } radeon_acpi_fini(rdev); -- cgit v1.2.3 From 2a9c5154a5650c09ad44ff5e1dff74754e15a3c6 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 5 Aug 2026 20:39:18 +0800 Subject: drm/amdgpu: check ASPM on the dGPU host link dGPUs with an internal PCIe switch expose graphics functions below the switch downstream port. The automatic ASPM check uses the display endpoint and evaluates the internal link instead of the host link. Use the switch upstream port for the check and report the selected link. Fixes: 0ab5d711ec74 ("drm/amd: Refactor `amdgpu_aspm` to be evaluated per device") Signed-off-by: Yang Wang Reviewed-by: Hawking Zhang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit 4e0d6f2876e704fff707b18c40dbd383aea4a1c9) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 50 +++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index bff3e06a756c..16049bfb627b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1370,6 +1370,31 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) #endif } +/* + * Some dGPUs expose their display endpoint below an internal PCIe switch. + * Use the switch upstream port to query the host-facing link. + */ +static struct pci_dev *amdgpu_device_get_aspm_pdev(struct amdgpu_device *adev) +{ + struct pci_dev *swds, *swus; + + swds = pci_upstream_bridge(adev->pdev); + if (!swds || + (swds->vendor != PCI_VENDOR_ID_ATI && + swds->vendor != PCI_VENDOR_ID_AMD) || + pci_pcie_type(swds) != PCI_EXP_TYPE_DOWNSTREAM) + return adev->pdev; + + swus = pci_upstream_bridge(swds); + if (!swus || + (swus->vendor != PCI_VENDOR_ID_ATI && + swus->vendor != PCI_VENDOR_ID_AMD) || + pci_pcie_type(swus) != PCI_EXP_TYPE_UPSTREAM) + return adev->pdev; + + return swus; +} + /** * amdgpu_device_should_use_aspm - check if the device should program ASPM * @@ -1382,6 +1407,9 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) */ bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev) { + struct pci_dev *aspm_pdev, *parent; + bool enabled; + switch (amdgpu_aspm) { case -1: break; @@ -1396,7 +1424,27 @@ bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev) return false; if (amdgpu_device_aspm_support_quirk(adev)) return false; - return pcie_aspm_enabled(adev->pdev); + + /* + * pcie_aspm_enabled() checks the link between its argument and + * the immediate upstream bridge. Use SWUS for dGPUs with an + * internal switch so that this is the host-facing link. + */ + aspm_pdev = amdgpu_device_get_aspm_pdev(adev); + parent = pci_upstream_bridge(aspm_pdev); + if (!parent) { + dev_dbg(adev->dev, "ASPM: no upstream PCIe link for %s\n", + pci_name(aspm_pdev)); + return false; + } + + enabled = pcie_aspm_enabled(aspm_pdev); + /* Report the exact link used for the automatic ASPM decision. */ + dev_dbg(adev->dev, "ASPM: link %s <-> %s is %s\n", + pci_name(parent), pci_name(aspm_pdev), + enabled ? "enabled" : "disabled"); + + return enabled; } /* if we get transitioned to only one device, take VGA back */ -- cgit v1.2.3 From 8c9aebcdd9f46f7a14b98d6ab18574b7a48fbb08 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 30 Jul 2026 17:37:44 +0200 Subject: drm/amdgpu: Reject UVD message with dimensions above 4096 Fixes potential overflow in DPB size calculations. Signed-off-by: David Rosca Acked-by: Leo Liu Reviewed-by: Ruijing Dong Signed-off-by: Alex Deucher (cherry picked from commit 05e1387d151f71569fbe122d2c89f9db0c21dc10) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 23383ac5323f..08b199518e22 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -655,8 +655,8 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer; unsigned int min_ctx_size = ~0; - /* Reject invalid dimensions to prevent division by zero */ - if (width < 16 || height < 16) { + /* Reject invalid dimensions */ + if (width < 16 || height < 16 || width > 4096 || height > 4096) { dev_WARN_ONCE(adev->dev, 1, "Invalid UVD decoding dimensions (%dx%d)!\n", width, height); -- cgit v1.2.3 From 21a8084cd76223a13493237e04d45f5226d7cee6 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 30 Jul 2026 17:56:17 +0200 Subject: drm/amdgpu: Fix UVD dpb min size calculation for H264 This should use actual number of references from the decode message, instead of maximum derived from level. Signed-off-by: David Rosca Acked-by: Leo Liu Reviewed-by: Ruijing Dong Signed-off-by: Alex Deucher (cherry picked from commit 64b525edb7e7bdfcdc77883c5e413804e2396856) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 62 +++------------------------------ 1 file changed, 4 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 08b199518e22..c29554e75e77 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -646,11 +646,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, unsigned int height = msg[7]; unsigned int dpb_size = msg[9]; unsigned int pitch = msg[28]; - unsigned int level = msg[57]; unsigned int width_in_mb = width / 16; unsigned int height_in_mb = ALIGN(height / 16, 2); - unsigned int fs_in_mb = width_in_mb * height_in_mb; unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer; unsigned int min_ctx_size = ~0; @@ -669,35 +667,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, switch (stream_type) { case 0: /* H264 */ - switch (level) { - case 30: - num_dpb_buffer = 8100 / fs_in_mb; - break; - case 31: - num_dpb_buffer = 18000 / fs_in_mb; - break; - case 32: - num_dpb_buffer = 20480 / fs_in_mb; - break; - case 41: - num_dpb_buffer = 32768 / fs_in_mb; - break; - case 42: - num_dpb_buffer = 34816 / fs_in_mb; - break; - case 50: - num_dpb_buffer = 110400 / fs_in_mb; - break; - case 51: - num_dpb_buffer = 184320 / fs_in_mb; - break; - default: - num_dpb_buffer = 184320 / fs_in_mb; - break; - } - num_dpb_buffer++; + num_dpb_buffer = ((msg[61] >> 16) & 0xff) + 1; if (num_dpb_buffer > 17) - num_dpb_buffer = 17; + return -EINVAL; /* reference picture buffer */ min_dpb_size = image_size * num_dpb_buffer; @@ -747,35 +719,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, break; case 7: /* H264 Perf */ - switch (level) { - case 30: - num_dpb_buffer = 8100 / fs_in_mb; - break; - case 31: - num_dpb_buffer = 18000 / fs_in_mb; - break; - case 32: - num_dpb_buffer = 20480 / fs_in_mb; - break; - case 41: - num_dpb_buffer = 32768 / fs_in_mb; - break; - case 42: - num_dpb_buffer = 34816 / fs_in_mb; - break; - case 50: - num_dpb_buffer = 110400 / fs_in_mb; - break; - case 51: - num_dpb_buffer = 184320 / fs_in_mb; - break; - default: - num_dpb_buffer = 184320 / fs_in_mb; - break; - } - num_dpb_buffer++; + num_dpb_buffer = ((msg[61] >> 16) & 0xff) + 1; if (num_dpb_buffer > 17) - num_dpb_buffer = 17; + return -EINVAL; /* reference picture buffer */ min_dpb_size = image_size * num_dpb_buffer; -- cgit v1.2.3 From b8bb9ba3f101a1b0011f785a577a4a0a38371174 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 30 Jul 2026 18:01:51 +0200 Subject: drm/amdgpu: Fix UVD decode image min size calculation This needs to use pitch instead of width. Also reject pitch over 4096 to avoid overflow. Signed-off-by: David Rosca Acked-by: Leo Liu Reviewed-by: Ruijing Dong Signed-off-by: Alex Deucher (cherry picked from commit b41c8cb12e202b220353332ab87dc01a11f69304) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index c29554e75e77..004a95d63b09 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -759,7 +759,7 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, return -EINVAL; } - if (width > pitch) { + if (width > pitch || pitch > 4096) { DRM_ERROR("Invalid UVD decoding target pitch!\n"); return -EINVAL; } @@ -771,7 +771,7 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, } buf_sizes[0x1] = dpb_size; - buf_sizes[0x2] = image_size; + buf_sizes[0x2] = (pitch * height) * 3 / 2; buf_sizes[0x4] = min_ctx_size; /* store image width to adjust nb memory pstate */ adev->uvd.decode_image_width = width; -- cgit v1.2.3 From 18727670b44753865b81c56a9338c0d7bd102c54 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 30 Jul 2026 18:05:52 +0200 Subject: drm/amdgpu: Fix UVD min buffer sizes Use correct size for message buffer = sizeof(struct ruvd_msg). Add ITSCALING_TABLE_BUFFER size. Signed-off-by: David Rosca Acked-by: Leo Liu Reviewed-by: Ruijing Dong Signed-off-by: Alex Deucher (cherry picked from commit 37519d007e4261febbcf35b3045f8344f3145497) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 004a95d63b09..7560bee89507 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -918,15 +918,16 @@ static int amdgpu_uvd_cs_pass2(struct amdgpu_uvd_cs_ctx *ctx) ctx->buf_sizes[cmd]); return -EINVAL; } + } else if (cmd == 0x204 || cmd == 0x206) { + unsigned int min_size = ctx->buf_sizes[cmd == 0x204 ? 5 : 4]; - } else if (cmd == 0x206) { - if ((end - start) < ctx->buf_sizes[4]) { + if ((end - start) < min_size) { DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, (unsigned int)(end - start), - ctx->buf_sizes[4]); + min_size); return -EINVAL; } - } else if ((cmd != 0x100) && (cmd != 0x204)) { + } else if ((cmd != 0x100)) { DRM_ERROR("invalid UVD command %X!\n", cmd); return -EINVAL; } @@ -1056,11 +1057,12 @@ int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, { struct amdgpu_uvd_cs_ctx ctx = {}; unsigned int buf_sizes[] = { - [0x00000000] = 2048, + [0x00000000] = 3556, [0x00000001] = 0xFFFFFFFF, [0x00000002] = 0xFFFFFFFF, [0x00000003] = 2048, [0x00000004] = 0xFFFFFFFF, + [0x00000005] = 992, }; int r; -- cgit v1.2.3 From d5ab4c6a64efef2d143a96df5357f59703cd703d Mon Sep 17 00:00:00 2001 From: David Rosca Date: Mon, 10 Aug 2026 11:11:35 +0200 Subject: drm/amdgpu: Implement insert_end for VCE 3 After a recent change VCE now hangs when VCE_CMD_END is emitted after a pipeline sync without VM flush. Implement insert_end to correctly insert only one VCE_CMD_END per job. Fixes: bc639a9eadc7 ("drm/amdgpu: always emit the job vm fence") Signed-off-by: David Rosca Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 8897ea8c761b856f02061848a7908040a1fe5e68) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/vce_v3_0.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index c69f7d82060f..a3eae7d4b57e 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -875,6 +875,23 @@ static void vce_v3_0_ring_emit_ib(struct amdgpu_ring *ring, amdgpu_ring_write(ring, ib->length_dw); } +static void vce_v3_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, + u64 seq, unsigned flags) +{ + WARN_ON(flags & AMDGPU_FENCE_FLAG_64BIT); + + amdgpu_ring_write(ring, VCE_CMD_FENCE); + amdgpu_ring_write(ring, addr); + amdgpu_ring_write(ring, upper_32_bits(addr)); + amdgpu_ring_write(ring, seq); + amdgpu_ring_write(ring, VCE_CMD_TRAP); +} + +static void vce_v3_0_ring_insert_end(struct amdgpu_ring *ring) +{ + amdgpu_ring_write(ring, VCE_CMD_END); +} + static void vce_v3_0_emit_vm_flush(struct amdgpu_ring *ring, unsigned int vmid, uint64_t pd_addr) { @@ -884,7 +901,6 @@ static void vce_v3_0_emit_vm_flush(struct amdgpu_ring *ring, amdgpu_ring_write(ring, VCE_CMD_FLUSH_TLB); amdgpu_ring_write(ring, vmid); - amdgpu_ring_write(ring, VCE_CMD_END); } static void vce_v3_0_emit_pipeline_sync(struct amdgpu_ring *ring) @@ -953,17 +969,19 @@ static const struct amdgpu_ring_funcs vce_v3_0_ring_vm_funcs = { .set_wptr = vce_v3_0_ring_set_wptr, .patch_cs_in_place = amdgpu_vce_ring_parse_cs_vm, .emit_frame_size = - 6 + /* vce_v3_0_emit_vm_flush */ + 5 + /* vce_v3_0_emit_vm_flush */ 4 + /* vce_v3_0_emit_pipeline_sync */ - 6 + 6, /* amdgpu_vce_ring_emit_fence x2 vm fence */ + 5 + 5 + /* vce_v3_0_ring_emit_fence x2 vm fence */ + 1, /* vce_v3_0_ring_insert_end */ .emit_ib_size = 5, /* vce_v3_0_ring_emit_ib */ .emit_ib = vce_v3_0_ring_emit_ib, .emit_vm_flush = vce_v3_0_emit_vm_flush, .emit_pipeline_sync = vce_v3_0_emit_pipeline_sync, - .emit_fence = amdgpu_vce_ring_emit_fence, + .emit_fence = vce_v3_0_ring_emit_fence, .test_ring = amdgpu_vce_ring_test_ring, .test_ib = amdgpu_vce_ring_test_ib, .insert_nop = amdgpu_ring_insert_nop, + .insert_end = vce_v3_0_ring_insert_end, .pad_ib = amdgpu_ring_generic_pad_ib, .begin_use = amdgpu_vce_ring_begin_use, .end_use = amdgpu_vce_ring_end_use, -- cgit v1.2.3 From 2f9a5c0f018d4a1586ee892f81f1383219676415 Mon Sep 17 00:00:00 2001 From: Nathan Lucas Date: Sun, 2 Aug 2026 08:35:23 -0600 Subject: drm/amd/display: fix BT.2020 YCbCr limited output CSC matrix COLOR_SPACE_YCBCR2020_TYPE, which is selected for COLOR_SPACE_2020_YCBCR_LIMITED color_space, has coefficients that are incorrect for limited-range output. Its luma and chroma scaling is full-range so output is too bright and colors are incorrect. COLOR_SPACE_YCBCR2020_TYPE is closer to a full-range conversion matrix with incorrect luma offset, so correct the luma offset for full-range and rename it to COLOR_SPACE_YCBCR2020_FULL_TYPE. Add COLOR_SPACE_YCBCR2020_LIMITED_TYPE with correct scaling and range for limited-range output. Fix related functions so COLOR_SPACE_YCBCR2020_LIMITED_TYPE and COLOR_SPACE_YCBCR2020_FULL_TYPE are correctly selected based on dc_color_space. Derivation of both matrices follows ITU-T H.273: Table 4, MatrixCoefficients 9, BT.2020-NCL weights: KR = 0.2627, KB = 0.0593, KG = 1 - KR - KB = 0.6780. Equations 45-47 in matrix form: [ KR KG KB 0 ] M2020_NCL = [ -KR/(2(1-KB)) -KG/(2(1-KB)) 1/2 0 ] [ 1/2 -KG/(2(1-KR)) -KB/(2(1-KR)) 0 ] [ 0 0 0 1 ] Limited and Full transforms based on equations 30-32 and 36-38 with bit depth 10, normalized by 1023: [ 876/1023 0 0 64/1023 ] MLimited = [ 0 896/1023 0 512/1023 ] [ 0 0 896/1023 512/1023 ] [ 0 0 0 1 ] [ 1023/1023 0 0 0 ] MFull = [ 0 1023/1023 0 512/1023 ] [ 0 0 1023/1023 512/1023 ] [ 0 0 0 1 ] M2020_NCL_Limited = MLimited x M2020_NCL M2020_NCL_Full = MFull x M2020_NCL The upper three rows of M2020_NCL_* are stored in CR, Y, CB order. Each M2020_NCL_* value is stored as Round(value * 8192) in its 16-bit two's-complement representation. Fixes: 973a9c810c78 ("drm/amd/display: Fix COLOR_SPACE_YCBCR2020_TYPE matrix") Assisted-by: OpenAI-Codex:GPT-5.6-Sol Tested-by: Igor Paunovic Tested-by: Satyajit Roy Signed-off-by: Nathan Lucas Signed-off-by: Alex Deucher (cherry picked from commit 3b906e1dc7e3c9ff9f7940f6828b367a6a9ec73c) Cc: stable@vger.kernel.org --- .../gpu/drm/amd/display/dc/core/dc_hw_sequencer.c | 31 ++++++++++++++-------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index 88446817a71f..7f362c3926c6 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -58,7 +58,8 @@ enum dc_color_space_type { COLOR_SPACE_RGB_LIMITED_TYPE, COLOR_SPACE_YCBCR601_TYPE, COLOR_SPACE_YCBCR709_TYPE, - COLOR_SPACE_YCBCR2020_TYPE, + COLOR_SPACE_YCBCR2020_LIMITED_TYPE, + COLOR_SPACE_YCBCR2020_FULL_TYPE, COLOR_SPACE_YCBCR601_LIMITED_TYPE, COLOR_SPACE_YCBCR709_LIMITED_TYPE, COLOR_SPACE_YCBCR709_BLACK_TYPE, @@ -110,9 +111,15 @@ static const struct out_csc_color_matrix_type output_csc_matrix[] = { { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, - { COLOR_SPACE_YCBCR2020_TYPE, + /* Corrected. Not included in the TODO above. */ + { COLOR_SPACE_YCBCR2020_LIMITED_TYPE, + { 0x0E04, 0xF31D, 0xFEDF, 0x1004, + 0x0733, 0x1294, 0x01A0, 0x0201, + 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, + /* Corrected. Not included in the TODO above. */ + { COLOR_SPACE_YCBCR2020_FULL_TYPE, { 0x1000, 0xF149, 0xFEB7, 0x1004, - 0x0868, 0x15B2, 0x01E6, 0x201, + 0x0868, 0x15B2, 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} }, { COLOR_SPACE_YCBCR709_BLACK_TYPE, { 0x0000, 0x0000, 0x0000, 0x1000, @@ -179,14 +186,14 @@ static bool is_ycbcr709_type( return ret; } -static bool is_ycbcr2020_type( - enum dc_color_space color_space) +static bool is_ycbcr2020_limited_type(enum dc_color_space color_space) { - bool ret = false; + return color_space == COLOR_SPACE_2020_YCBCR_LIMITED; +} - if (color_space == COLOR_SPACE_2020_YCBCR_LIMITED || color_space == COLOR_SPACE_2020_YCBCR_FULL) - ret = true; - return ret; +static bool is_ycbcr2020_full_type(enum dc_color_space color_space) +{ + return color_space == COLOR_SPACE_2020_YCBCR_FULL; } static bool is_ycbcr709_limited_type( @@ -215,8 +222,10 @@ static enum dc_color_space_type get_color_space_type(enum dc_color_space color_s type = COLOR_SPACE_YCBCR601_LIMITED_TYPE; else if (is_ycbcr709_limited_type(color_space)) type = COLOR_SPACE_YCBCR709_LIMITED_TYPE; - else if (is_ycbcr2020_type(color_space)) - type = COLOR_SPACE_YCBCR2020_TYPE; + else if (is_ycbcr2020_limited_type(color_space)) + type = COLOR_SPACE_YCBCR2020_LIMITED_TYPE; + else if (is_ycbcr2020_full_type(color_space)) + type = COLOR_SPACE_YCBCR2020_FULL_TYPE; else if (color_space == COLOR_SPACE_YCBCR709) type = COLOR_SPACE_YCBCR709_BLACK_TYPE; else if (color_space == COLOR_SPACE_YCBCR709_BLACK) -- cgit v1.2.3 From cd22349e86faf6e15e6c622d70c0efc57d43201e Mon Sep 17 00:00:00 2001 From: Nathan Lucas Date: Sun, 2 Aug 2026 08:35:24 -0600 Subject: drm/amd/display: fix BT.2020 YCbCr output CSC matrices for DCE The commit cited by the Fixes tag added separate limited and full-range BT.2020 YCbCr entries to the DCE output CSC tables, but populated both entries with the same matrix copied from the common DC table. That matrix combined full-range scaling with limited-range luma offset and was incorrect for both limited and full-range output. Replace the coefficients in both entries in the DCE paths with those from the new COLOR_SPACE_YCBCR2020_LIMITED_TYPE and COLOR_SPACE_YCBCR2020_FULL_TYPE entries in the preceding commit ("drm/amd/display: fix BT.2020 YCbCr limited output CSC matrix"). Fixes: 51e6668ab4ba ("drm/amd/display: add missing CSC entries for BT.2020 for DCE IPs") Assisted-by: OpenAI-Codex:GPT-5.6-Sol Tested-by: Igor Paunovic Tested-by: Satyajit Roy Signed-off-by: Nathan Lucas Signed-off-by: Alex Deucher (cherry picked from commit 14c8726b79d19934d6eb6d35c612e3f7204af2c6) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/dce/dce_transform.c | 7 ++++--- drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index 12c85c3afd6a..a51c9b282055 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -115,10 +115,11 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, { COLOR_SPACE_2020_RGB_LIMITEDRANGE, { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, -{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, - 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +/* COLOR_SPACE_2020_YCBCR_* values corrected. Not included in the TODO above. */ +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x0E04, 0xF31D, 0xFEDF, 0x1004, 0x0733, + 0x1294, 0x01A0, 0x201, 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, { COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, - 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } + 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} } }; static bool setup_scaling_configuration( diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c index 1ed018aaa4bb..f5f8cd2d47a5 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -93,10 +93,11 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, { COLOR_SPACE_2020_RGB_LIMITEDRANGE, { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, -{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, - 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +/* COLOR_SPACE_2020_YCBCR_* values corrected. Not included in the TODO above. */ +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x0E04, 0xF31D, 0xFEDF, 0x1004, 0x0733, + 0x1294, 0x01A0, 0x201, 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, { COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, - 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } + 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} } }; enum csc_color_mode { -- cgit v1.2.3 From cd688a3cb342b9f56399aa076157f1c324c15c5a Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 10 Aug 2026 12:48:19 +0800 Subject: drm/amdgpu: fix nbif 6.3.1 l1 low power not functional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCIe L1 low‑power settings for NBIF 6.3.1 were never applied due to unresolved register mapping, which caused the relevant code to be compiled out. As a result, the PCIe link could not enter L1/L23 power‑down states or transition to L0s. Properly configure the link control register to enable L1 and L23 power‑down, and permit L0s link transitions. Keep LTR disabled and let the PCI core enable it only after verifying end‑to‑end root complex support across switches. Fixes: 894c6d3522d1 ("drm/amdgpu: Add nbif v6_3_1 ip block support") Signed-off-by: Yang Wang Signed-off-by: Kenneth Feng Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit c2417f9fd7049d5a8d87eefd82fd6e36ba1ff7b6) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c | 42 +++++++++++++------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c index 000516b5845a..61eb0513dc97 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c @@ -500,7 +500,6 @@ static u32 nbif_v6_3_1_get_rom_offset(struct amdgpu_device *adev) static void nbif_v6_3_1_program_ltr(struct amdgpu_device *adev) { uint32_t def, data; - u16 devctl2; def = RREG32_SOC15(NBIO, 0, regRCC_EP_DEV0_0_EP_PCIE_TX_LTR_CNTL); data = 0x35EB; @@ -514,15 +513,8 @@ static void nbif_v6_3_1_program_ltr(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP2, data); - pcie_capability_read_word(adev->pdev, PCI_EXP_DEVCTL2, &devctl2); - - if (adev->pdev->ltr_path == (devctl2 & PCI_EXP_DEVCTL2_LTR_EN)) - return; - - if (adev->pdev->ltr_path) - pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN); - else - pcie_capability_clear_word(adev->pdev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN); + pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_LTR_EN); } #endif @@ -530,7 +522,7 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) { #ifdef CONFIG_PCIEASPM uint32_t def, data; - u16 devctl2, ltr; + u16 ltr; def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL); data &= ~PCIE_LC_CNTL__LC_L1_INACTIVITY_MASK; @@ -560,11 +552,8 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP5, data); - pcie_capability_read_word(adev->pdev, PCI_EXP_DEVCTL2, &devctl2); - data = def = devctl2; - data &= ~PCI_EXP_DEVCTL2_LTR_EN; - if (def != data) - pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, (u16)data); + pcie_capability_clear_word(adev->pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_LTR_EN); ltr = pci_find_ext_capability(adev->pdev, PCI_EXT_CAP_ID_LTR); @@ -572,15 +561,13 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) pci_write_config_dword(adev->pdev, ltr + PCI_LTR_MAX_SNOOP_LAT, 0x10011001); } -#if 0 - /* regPSWUSP0_PCIE_LC_CNTL2 should be replace by PCIE_LC_CNTL2 or someone else ? */ - def = data = RREG32_SOC15(NBIO, 0, regPSWUSP0_PCIE_LC_CNTL2); - data |= PSWUSP0_PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L1_MASK | - PSWUSP0_PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L23_MASK; - data &= ~PSWUSP0_PCIE_LC_CNTL2__LC_RCV_L0_TO_RCV_L0S_DIS_MASK; + def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL2); + data |= PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L1_MASK | + PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L23_MASK; + data &= ~PCIE_LC_CNTL2__LC_RCV_L0_TO_RCV_L0S_DIS_MASK; if (def != data) - WREG32_SOC15(NBIO, 0, regPSWUSP0_PCIE_LC_CNTL2, data); -#endif + WREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL2, data); + def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL4); data |= PCIE_LC_CNTL4__LC_L1_POWERDOWN_MASK; if (def != data) @@ -591,7 +578,12 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(PCIE, 0, regPCIE_LC_RXRECOVER_RXSTANDBY_CNTL, data); - nbif_v6_3_1_program_ltr(adev); + /* + * Do not enable endpoint LTR unless the Root Complex and every + * upstream switch support it. + */ + if (adev->pdev->ltr_path) + nbif_v6_3_1_program_ltr(adev); def = data = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP3); data |= 0x5DE0 << RCC_STRAP0_RCC_BIF_STRAP3__STRAP_VLINK_ASPM_IDLE_TIMER__SHIFT; -- cgit v1.2.3 From 9fca434208f1f9ab977feac62df8ebb1cc7ce893 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Tue, 11 Aug 2026 11:03:10 +0200 Subject: drm/amdgpu: Reject UVD message with invalid number of h265 refs Same change as for h264, avoids overflow later when calculating min dpb size. Signed-off-by: David Rosca Reviewed-by: Leo Liu Signed-off-by: Alex Deucher (cherry picked from commit a4b0720e4f1601f97f59a2be9c1b4b94fa6527d5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 7560bee89507..de3dbc95e376 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -749,6 +749,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, image_size = ALIGN(image_size, 256); num_dpb_buffer = (le32_to_cpu(msg[59]) & 0xff) + 2; + if (num_dpb_buffer > 17) + return -EINVAL; + min_dpb_size = image_size * num_dpb_buffer; min_ctx_size = ((width + 255) / 16) * ((height + 255) / 16) * 16 * num_dpb_buffer + 52 * 1024; -- cgit v1.2.3 From ac828b94e027d29af82325fcc55556dc8173fd85 Mon Sep 17 00:00:00 2001 From: Lijo Lazar Date: Mon, 13 Jul 2026 16:34:24 +0530 Subject: drm/amdgpu: Prefer default discovery offset If a valid signature is seen at the default offset, use the default size/offset for discovery. Fixes: 01bdc7e219c4 ("drm/amdgpu: New interface to get IP discovery binary v3") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5447 Signed-off-by: Lijo Lazar Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 46a0df99a0b2fa2fa61d864b04b6a5d5fe748779) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 5e83edbd313b..47e0680ed7b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -311,6 +311,19 @@ static int amdgpu_discovery_get_tmr_info(struct amdgpu_device *adev, goto out; } } else { + if (adev->discovery.offset) { + u32 signature; + + /* If VRAM holds a valid discovery signature at the default + * discovery offset, use it as-is. + */ + amdgpu_device_vram_access(adev, adev->discovery.offset, + &signature, sizeof(signature), + false); + if (le32_to_cpu(signature) == BINARY_SIGNATURE) + goto out; + } + tmr_size = RREG32(mmDRIVER_SCRATCH_2); if (tmr_size) { /* It's preferred to transition to PSP mailbox reg interface -- cgit v1.2.3 From 94f39e4c017e66130e476268bdaa0bf61e914fa2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 10 Jul 2026 09:09:58 +0100 Subject: clocksource/drivers/arm_arch_timer: Workaround bcm2712 broken EL2 virtual timer It appears that the bcm2712 SoC found in the relatively popular RPi5 has a broken EL2 virtual timer. Tthe reason why the timer isn't working is unknown (the timer is ticking, but the interrupt never fires), and the SoC vendor doesn't communicate on the reason why this isn't working, leaving users and maintainers in the dark. Paper over the issue by detecting the broken HW, falling back to the physical timer instead, and let the user know about it. Also taint the kernel as the machine is definitely not compliant with the spec, and it's unknown what else is wrong with it. Reported-by: John Reported-by: Daniel Drake Reported-by: Marek Szyprowski Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Gary Guo Acked-by: Florian Fainelli Cc: Daniel Lezcano Link: https://patch.msgid.link/20260710080958.491620-1-maz@kernel.org --- drivers/clocksource/arm_arch_timer.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 4adf756423de..7b4a98df6962 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -1090,6 +1090,27 @@ static int __init arch_timer_common_init(void) return arch_timer_arch_init(); } +static bool __init has_broken_el2_vtimer(void) +{ + /* + * SoCs described here have been found to be broken, though no + * explanation has been volunteered by the vendor. Let the user know + * we're papering over the vendor's lack of communication. + */ + static const char * const broken_el2_vtimer[] __initconst = { + "brcm,bcm2712", + NULL + }; + + if (of_machine_compatible_match(broken_el2_vtimer)) { + add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); + pr_warn_once(HW_ERR "Known broken EL2 virtual timer, ignoring it\n"); + return true; + } + + return false; +} + /** * arch_timer_select_ppi() - Select suitable PPI for the current system. * @@ -1115,7 +1136,8 @@ static int __init arch_timer_common_init(void) static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void) { if (is_kernel_in_hyp_mode()) { - if (arch_timer_ppi[ARCH_TIMER_HYP_VIRT_PPI]) + if (arch_timer_ppi[ARCH_TIMER_HYP_VIRT_PPI] && + !has_broken_el2_vtimer()) return ARCH_TIMER_HYP_VIRT_PPI; pr_warn_once(FW_BUG "VHE-capable CPU without EL2 virtual timer interrupt\n"); -- cgit v1.2.3 From 00268f9452d2a0d660aa9c1bb0ca07a994af6a4f Mon Sep 17 00:00:00 2001 From: Andrey Golovko Date: Tue, 11 Aug 2026 22:14:35 +0300 Subject: regmap: sdw-mbq: don't call an unset readable_reg callback regmap_sdw_mbq_poll_busy() decides whether to poll the Function Busy bit by calling ctx->readable_reg(), which is a straight copy of config->readable_reg. That callback is optional: regmap_readable() treats a NULL ->readable_reg as "every register is readable", and drivers rely on that. es9356 and tac5xx2-sdw both build an MBQ regmap without one. Since commit ca1b11b36d82 ("regmap: sdw-mbq: Allow defers on undeferrable controls") the poll runs on every -ENODATA, not only for Controls the driver marked deferrable, so any of those devices answering COMMAND_IGNORED takes the kernel through a NULL function pointer. Treat a missing callback the way the rest of regmap does and poll. Fixes: 5bc493bf0c37 ("regmap: sdw-mbq: Add support for SDCA deferred controls") Signed-off-by: Andrey Golovko Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260811184500.5312-1-andrey.golovko@gmail.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-sdw-mbq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c index 6d7c760cbef2..b0312f57c980 100644 --- a/drivers/base/regmap/regmap-sdw-mbq.c +++ b/drivers/base/regmap/regmap-sdw-mbq.c @@ -56,7 +56,7 @@ static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(reg), 0, SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0); - if (ctx->readable_reg(dev, reg)) { + if (!ctx->readable_reg || ctx->readable_reg(dev, reg)) { ret = read_poll_timeout(sdw_read_no_pm, val, val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY), ctx->cfg.retry_us, ctx->cfg.timeout_us, -- cgit v1.2.3 From 5a87925539acecfe88229bad76ab81bd75a7e3f5 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Thu, 11 Jun 2026 19:32:51 +0800 Subject: libceph: tolerate addrvecs with multiple entries of the same type ceph_decode_entity_addrvec() rejects any addrvec containing more than one entry that matches the requested msgr type (LEGACY or MSGR2), logging "another match of type N in addrvec" and returning -EINVAL. Some admin tooling (e.g. pveceph mon create from Proxmox VE) generates addrvecs with multiple same-type entries when public_network lists more than one CIDR: it picks one local IP per subnet and emits both a v2 and a v1 entry for each IP. Monmaps shaped this way cause: libceph: mon0 (1)10.10.10.15:6789 session established libceph: another match of type 1 in addrvec libceph: problem decoding monmap, -22 No Ceph code uses the extra entries: since Nautilus, the userspace messenger (AsyncMessenger) unconditionally picks the first address of the requested type and ignores any subsequent matches. Match that behavior: use the first matching entry and silently skip any subsequent ones. This is a compatibility fix for existing deployments and does not enable dual-stack or multi-subnet address selection. [ idryomov: tweak ceph_decode_entity_addrvec() comment ] Cc: stable@vger.kernel.org Fixes: a5cbd5fc22d5 ("libceph, ceph: get and handle cluster maps with addrvecs") Link: https://bugzilla.proxmox.com/show_bug.cgi?id=7518 Signed-off-by: Kefu Chai Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- net/ceph/decode.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/net/ceph/decode.c b/net/ceph/decode.c index bc109a1a4616..2f21af38cd9a 100644 --- a/net/ceph/decode.c +++ b/net/ceph/decode.c @@ -87,8 +87,9 @@ bad: EXPORT_SYMBOL(ceph_decode_entity_addr); /* - * Return addr of desired type (MSGR2 or LEGACY) or error. - * Make sure there is only one match. + * Return addr of desired type (MSGR2 or LEGACY) or error. In case of + * multiple matches, use the first one for compatibility with userspace + * messenger. * * Assume encoding with MSG_ADDR2. */ @@ -121,14 +122,13 @@ int ceph_decode_entity_addrvec(void **p, void *end, bool msgr2, dout("%s i %d addr %s\n", __func__, i, ceph_pr_addr(&tmp_addr)); if (tmp_addr.type == my_type) { - if (found) { - pr_err("another match of type %d in addrvec\n", - le32_to_cpu(my_type)); - return -EINVAL; + if (!found) { + memcpy(addr, &tmp_addr, sizeof(*addr)); + found = true; + } else { + dout("%s skipping extra match of type %d in addrvec\n", + __func__, le32_to_cpu(my_type)); } - - memcpy(addr, &tmp_addr, sizeof(*addr)); - found = true; } } -- cgit v1.2.3 From 437b6551cfcc235eea1d735a874f9d421f555e17 Mon Sep 17 00:00:00 2001 From: Pavitra Jha Date: Tue, 2 Jun 2026 01:02:19 -0400 Subject: libceph: fix multiple unsafe decodes in decode_locker() decode_locker() in cls_lock_client.c contains three unsafe decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads: 1. ceph_decode_copy() at the locker_id_t name field has no preceding bounds check. With p == end after ceph_start_decoding() accepts struct_len=0, this reads sizeof(ceph_entity_name) = 9 bytes past the validated buffer boundary. 2. *p += sizeof(struct ceph_timespec) after the locker_info_t header is an unchecked pointer advance. A malicious OSD can position p past end, causing all subsequent _safe checks to pass against a bogus boundary. 3. len = ceph_decode_32(p) has no preceding bounds check, and the immediately following *p += len is uncapped. A malicious OSD can send len=0xffffffff, advancing p gigabytes past end and escaping the decode window entirely. Fix all three by replacing bare operations with their safe variants: ceph_decode_copy -> ceph_decode_copy_safe *p += sizeof(...) -> ceph_decode_skip_n ceph_decode_32(p) -> ceph_decode_32_safe *p += len -> ceph_decode_skip_n A new label is added to return -EINVAL on any bounds violation. -EINVAL is appropriate here: the data received from the OSD is structurally malformed, which is an invalid argument to the decode contract regardless of whether the caller or the wire is at fault. Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment can trigger this against any kernel client that issues the lock.get_info class method (e.g. during RBD exclusive lock acquisition) without any further privileges beyond OSD session establishment. [ idryomov: use ceph_decode_skip_string() to skip description, trim changelog ] Cc: stable@vger.kernel.org Fixes: d4ed4a530562 ("libceph: support for lock.lock_info") Signed-off-by: Pavitra Jha Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- net/ceph/cls_lock_client.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/ceph/cls_lock_client.c b/net/ceph/cls_lock_client.c index 377336982f7d..2c8fefc1a721 100644 --- a/net/ceph/cls_lock_client.c +++ b/net/ceph/cls_lock_client.c @@ -259,7 +259,8 @@ static int decode_locker(void **p, void *end, struct ceph_locker *locker) if (ret) return ret; - ceph_decode_copy(p, &locker->id.name, sizeof(locker->id.name)); + ceph_decode_copy_safe(p, end, &locker->id.name, + sizeof(locker->id.name), bad); s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO); if (IS_ERR(s)) return PTR_ERR(s); @@ -270,19 +271,23 @@ static int decode_locker(void **p, void *end, struct ceph_locker *locker) if (ret) return ret; - *p += sizeof(struct ceph_timespec); /* skip expiration */ + /* skip expiration */ + ceph_decode_skip_n(p, end, sizeof(struct ceph_timespec), bad); ret = ceph_decode_entity_addr(p, end, &locker->info.addr); if (ret) return ret; - len = ceph_decode_32(p); - *p += len; /* skip description */ + /* skip description */ + ceph_decode_skip_string(p, end, bad); dout("%s %s%llu cookie %s addr %s\n", __func__, ENTITY_NAME(locker->id.name), locker->id.cookie, ceph_pr_addr(&locker->info.addr)); return 0; + +bad: + return -EINVAL; } static int decode_lockers(void **p, void *end, u8 *type, char **tag, -- cgit v1.2.3 From 00ead17c7de137a692edee59f2772e6af687e8eb Mon Sep 17 00:00:00 2001 From: Pavitra Jha Date: Wed, 8 Jul 2026 01:39:41 -0400 Subject: libceph: fix OOB read in decode_watchers() via missing bounds check ceph_start_decoding() validates that struct_len bytes remain in the buffer after the encoding header, but accepts struct_len=0 as valid: ceph_decode_need(p, end, 0, bad) always passes. When a malicious or compromised OSD sends an obj_list_watch_response_t reply with struct_len=0, ceph_start_decoding() returns success with p == end, leaving zero bytes guaranteed for subsequent reads. The immediately following ceph_decode_32(p) in decode_watchers() has no preceding bounds check. With p == end this is a 4-byte read past the validated buffer boundary. The garbage value is then passed directly to kzalloc_objs() as the watcher count. The sibling function decode_watcher() already uses the safe variants (ceph_decode_copy_safe, ceph_decode_64_safe, ceph_decode_skip_32) after its own ceph_start_decoding() call. decode_watchers() is the only site that uses the bare variant, confirming an oversight. Fix by replacing ceph_decode_32(p) with ceph_decode_32_safe(p, end, *num_watchers, bad), consistent with the established pattern. Attacker model: a malicious or compromised OSD in a multi-tenant Ceph deployment (e.g. cloud) can trigger this against any kernel client that calls CEPH_OSD_OP_LIST_WATCHERS, without any further privileges beyond OSD session establishment. [ idryomov: trim changelog ] Cc: stable@vger.kernel.org Fixes: a4ed38d7a180 ("libceph: support for CEPH_OSD_OP_LIST_WATCHERS") Signed-off-by: Pavitra Jha Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- net/ceph/osd_client.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 2ff00070c181..28d76c2f6b3e 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -5030,7 +5030,7 @@ static int decode_watchers(void **p, void *end, if (ret) return ret; - *num_watchers = ceph_decode_32(p); + ceph_decode_32_safe(p, end, *num_watchers, bad); *watchers = kzalloc_objs(**watchers, *num_watchers, GFP_NOIO); if (!*watchers) return -ENOMEM; @@ -5044,6 +5044,9 @@ static int decode_watchers(void **p, void *end, } return 0; + +bad: + return -EINVAL; } /* -- cgit v1.2.3 From 3660b98d1204b419f6a77e9a295f148dcf38d042 Mon Sep 17 00:00:00 2001 From: Raphael Zimmer Date: Tue, 28 Jul 2026 10:43:40 +0200 Subject: libceph: Avoid using invalid osd indices from primary_temp A corrupted osdmap received from a Ceph monitor or OSD may contain osd indices in its pg_temp, primary_temp, pg_upmap, and pg_upmap_items parts that don't exist, i.e., that are greater than max_osd or smaller than CEPH_HOMELESS_OSD (-1). These indices are used to create the up and acting set in ceph_pg_to_up_acting_osds(), called from calc_target(). While most of these osd indices are checked, the one from primary_temp is not. Subsequently, this may lead to calc_target() returning this (potentially invalid) index as target osd for a (linger) request. Because the osd_state, osd_weight, and osd_addr arrays only contain max_osd entries (with indices 0 to max_osd -1), this leads to out-of-bounds accesses when trying to read values from these arrays. This patch fixes the issue by adding a check to get_temp_osds(), so that only valid osd indices from primary_temp are used, and it falls back to using the primary from pg_temp or the up set if it is invalid. [ idryomov: changelog ] Cc: stable@vger.kernel.org Fixes: 5e8d4d36bf23 ("libceph: add support for primary_temp mappings") Signed-off-by: Raphael Zimmer Reviewed-by: Ilya Dryomov Signed-off-by: Ilya Dryomov --- net/ceph/osdmap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index a4b0dd8672ec..d6282f0bcff8 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -2809,9 +2809,10 @@ static void get_temp_osds(struct ceph_osdmap *osdmap, } } - /* primary_temp? */ + /* primary_temp? (shouldn't ever be a nonexistent or down OSD) */ pg = lookup_pg_mapping(&osdmap->primary_temp, pgid); - if (pg) + if (pg && !WARN_ON_ONCE(ceph_osd_is_down(osdmap, + pg->primary_temp.osd))) temp->primary = pg->primary_temp.osd; } -- cgit v1.2.3 From 2c11c4bfdb7bd2808b3b3ac228e1f2d9bcf25457 Mon Sep 17 00:00:00 2001 From: Yiming Zhu Date: Fri, 24 Jul 2026 18:49:20 +0800 Subject: ceph: fix MDS random selection readiness predicate CEPH_MDS_IS_READY() is parsed so that the ternary expression can return true for an MDS entry with state 0 when it is not laggy. This allows the random selector to choose a down/DNE rank. Group the ternary expression under the state check so zero-state ranks are not treated as ready. Cc: stable@vger.kernel.org Fixes: b38c9eb4757d ("ceph: add possible_max_rank and make the code more readable") Link: https://tracker.ceph.com/issues/78648 Signed-off-by: Yiming Zhu Reviewed-by: Viacheslav Dubeyko Signed-off-by: Ilya Dryomov --- fs/ceph/mdsmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index d8e46eb7e5eb..450a4dc9662e 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -15,7 +15,7 @@ #include "super.h" #define CEPH_MDS_IS_READY(i, ignore_laggy) \ - (m->m_info[i].state > 0 && ignore_laggy ? true : !m->m_info[i].laggy) + (m->m_info[i].state > 0 && (ignore_laggy ? true : !m->m_info[i].laggy)) static int __mdsmap_get_random_mds(struct ceph_mdsmap *m, bool ignore_laggy) { -- cgit v1.2.3 From 91880e4a7fac45bc407771bc57bbaf4f37e9b4c3 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Mon, 27 Jul 2026 09:47:44 +0800 Subject: ceph: use the mount idmap for the owner checks in the SET_LAYOUT ioctls ceph_ioctl_set_layout() and ceph_ioctl_set_layout_policy() call inode_owner_or_capable() with &nop_mnt_idmap instead of the idmap of the mount the ioctl was issued on. CephFS supports idmapped mounts (FS_ALLOW_IDMAP), so on such a mount this compares the caller's fsuid against the unmapped on-disk owner rather than the mapped owner: the actual owner can be wrongly denied with -EACCES and an unrelated caller wrongly allowed. Both functions already have the struct file, so use file_mnt_idmap(file) instead. Cc: stable@vger.kernel.org Fixes: cee38bbf5556 ("ceph: add owner/capability checks for CEPH_IOC_SET_LAYOUT*") Signed-off-by: Zhan Xusheng Reviewed-by: Xiubo Li Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov --- fs/ceph/ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index de07f19b0caa..12cb50e0a166 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -72,7 +72,7 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg) struct ceph_ioctl_layout nl; int err; - if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + if (!inode_owner_or_capable(file_mnt_idmap(file), inode)) return -EACCES; if (copy_from_user(&l, arg, sizeof(l))) @@ -145,7 +145,7 @@ static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg) int err; struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; - if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + if (!inode_owner_or_capable(file_mnt_idmap(file), inode)) return -EACCES; /* copy and validate */ -- cgit v1.2.3 From 9e6869be49064915edb6c8776b27c376cfdb0df5 Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Mon, 10 Aug 2026 13:14:32 +0200 Subject: net/dibs: Correct freeing of dmb_clientid_arr A dibs device interrupt handler can be active after dibs_dev_del() and may still access dmb_clientid_arr. (UAF) In case of a failure in dibs_dev_add() being called by dibs_lo_dev_probe() dmb_clientid_arr is freed twice (double free). Free dmb_clientid_arr in dibs_dev_release() after last reference is gone. Note that allocating in dibs_dev_add() instead of dibs_dev_alloc() is ok for now, because no dmbs can be registered before dibs_dev_add(). Fixes: cc21191b584c ("dibs: Move data path to dibs layer") Cc: stable@vger.kernel.org Co-developed-by: Hidayath Khan Signed-off-by: Hidayath Khan Signed-off-by: Alexandra Winter Reviewed-by: Dust Li Link: https://patch.msgid.link/20260810111432.2334900-1-wintera@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/dibs/dibs_loopback.c | 1 - drivers/dibs/dibs_main.c | 14 ++++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c index fd5caf1e19a8..649e4e375be3 100644 --- a/drivers/dibs/dibs_loopback.c +++ b/drivers/dibs/dibs_loopback.c @@ -335,7 +335,6 @@ static int dibs_lo_dev_probe(void) return 0; err_reg: - kfree(dibs->dmb_clientid_arr); /* pairs with dibs_dev_alloc() */ put_device(&dibs->dev); kfree(ldev); diff --git a/drivers/dibs/dibs_main.c b/drivers/dibs/dibs_main.c index 4c26fd06973f..2b53a9d277dc 100644 --- a/drivers/dibs/dibs_main.c +++ b/drivers/dibs/dibs_main.c @@ -128,6 +128,7 @@ static void dibs_dev_release(struct device *dev) dibs = container_of(dev, struct dibs_dev, dev); + kfree(dibs->dmb_clientid_arr); kfree(dibs); } @@ -194,12 +195,13 @@ int dibs_dev_add(struct dibs_dev *dibs) ret = device_add(&dibs->dev); if (ret) - goto free_client_arr; + return ret; ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group); if (ret) { dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n"); - goto err_device_del; + device_del(&dibs->dev); + return ret; } mutex_lock(&dibs_dev_list.mutex); mutex_lock(&clients_lock); @@ -214,13 +216,6 @@ int dibs_dev_add(struct dibs_dev *dibs) mutex_unlock(&dibs_dev_list.mutex); return 0; - -err_device_del: - device_del(&dibs->dev); -free_client_arr: - kfree(dibs->dmb_clientid_arr); - return ret; - } EXPORT_SYMBOL_GPL(dibs_dev_add); @@ -247,7 +242,6 @@ void dibs_dev_del(struct dibs_dev *dibs) mutex_unlock(&dibs_dev_list.mutex); device_del(&dibs->dev); - kfree(dibs->dmb_clientid_arr); } EXPORT_SYMBOL_GPL(dibs_dev_del); -- cgit v1.2.3 From beb33f8ee1ca83acddb2a5ae80f3d22ec550b4c3 Mon Sep 17 00:00:00 2001 From: Qing Ming Date: Tue, 11 Aug 2026 23:28:03 +0800 Subject: sctp: clear new_transport when removing a peer sctp_process_asconf_param() stores a newly added peer transport in asoc->new_transport. After all parameters in the ASCONF chunk have been processed, sctp_sf_do_asconf() uses this pointer to send a HEARTBEAT to the new transport. An authenticated ASCONF from a remote SCTP peer can add a transport and remove it again with a wildcard DEL-IP parameter in the same chunk. The wildcard deletion preserves the transport on which the ASCONF arrived, but removes the newly added transport through sctp_assoc_del_nonprimary_peers(). The removal does not clear asoc->new_transport, leaving it pointing to the removed transport. sctp_sf_do_asconf() then creates a HEARTBEAT whose chunk->transport points to the removed transport without holding a transport reference. During local address replacement, src_out_of_asoc_ok keeps this HEARTBEAT on control_chunk_list. After the transport is freed by RCU, a successful ASCONF_ACK for the replacement address releases the queued HEARTBEAT and sctp_outq_select_transport() reads the freed transport's state. The issue was found during a static audit of SCTP objects. With an authenticated peer, the reproducer triggered the same KASAN report in 2 of 2 unpatched runs on a KASAN-enabled netdev/main kernel: BUG: KASAN: slab-use-after-free in sctp_outq_select_transport Read of size 4 at addr ffff88800b9bd95c by task python3/197 Call Trace: sctp_outq_select_transport+0x549/0x8b0 [sctp] sctp_outq_flush+0x306/0x2c60 [sctp] sctp_transport_immediate_rtx+0xaf/0x260 [sctp] sctp_process_asconf_ack+0xa48/0xf70 [sctp] Allocated by task 197: sctp_transport_new+0x68/0x650 [sctp] sctp_assoc_add_peer+0x258/0x12a0 [sctp] sctp_process_asconf+0x5e9/0x1090 [sctp] Last potentially related work creation: __call_rcu_common.constprop.0+0x77/0xb70 sctp_assoc_del_nonprimary_peers+0x7c/0xd0 [sctp] sctp_process_asconf+0xd9c/0x1090 [sctp] The first invalid access was a four-byte read of transport->state at net/sctp/outqueue.c:833. The same reproducer completed the full authenticated ASCONF and local-address replacement sequence with this change without a KASAN report or oops. Clear new_transport when its peer is removed, before it can be used to create the HEARTBEAT. Fixes: 6af29ccc223b ("sctp: Bundle HEAERTBEAT into ASCONF_ACK") Cc: stable@vger.kernel.org Signed-off-by: Qing Ming Acked-by: Xin Long Link: https://patch.msgid.link/20260811152803.5629-1-a0yami@mailbox.org Signed-off-by: Jakub Kicinski --- net/sctp/associola.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 5b0ae616e1ff..c65c83638cce 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -543,6 +543,9 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, asoc->addip_last_asconf->transport == peer) asoc->addip_last_asconf->transport = NULL; + if (asoc->new_transport == peer) + asoc->new_transport = NULL; + /* If we have something on the transmitted list, we have to * save it off. The best place is the active path. */ -- cgit v1.2.3 From 36a05d2820077bb3955acb8111e1041d39148037 Mon Sep 17 00:00:00 2001 From: Siddharth Vadapalli Date: Fri, 7 Aug 2026 16:47:37 +0530 Subject: net: ethernet: ti: am65-cpsw-nuss: Fix port_id extraction from SRC TAG On the packet reception path, the ID of the MAC Port on which the packet was received, is embedded in the RX DMA Descriptor's metadata. The ID is extracted using the helper function cppi5_desc_get_tags_ids() which fills in the 16-bit Source Tag into the 'port_id' variable. However, it is only the lower 8-bits of the 16-bit Source Tag that represent the MAC Port ID, while the upper 8-bits are Hardware-Reserved and carry an arbitrary value. With the existing logic, sporadic kernel crash is observed due to the subsequent driver code accessing out-of-bound memory because of an invalid port_id. Hence, fix the port_id extraction logic to use only the lower 8-bits of the Source Tag as the MAC Port ID. Fixes: 93a76530316a ("net: ethernet: ti: introduce am65x/j721e gigabit eth subsystem driver") Signed-off-by: Siddharth Vadapalli Reviewed-by: Chintan Vankar Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260807111738.2055900-1-s-vadapalli@ti.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ti/am65-cpsw-nuss.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 434a31080855..6330540979d7 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1310,6 +1310,8 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_rx_flow *flow, k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); pkt_len = cppi5_hdesc_get_pktlen(desc_rx); cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL); + /* Port ID is contained in the lower 8-bits of the 16-bit Source Tag */ + port_id &= 0xFF; dev_dbg(dev, "%s rx port_id:%d\n", __func__, port_id); port = am65_common_get_port(common, port_id); ndev = port->ndev; -- cgit v1.2.3 From 9da976eb649c9e2f588a4499410e4d8af687925f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 15 Jun 2026 22:12:29 -0700 Subject: Input: sur40 - fix input device registration ordering In sur40_probe(), input_register_device() was previously called early before the V4L2 video device and vb2_queue components were fully initialized. If userspace opened the input device immediately upon registration, sur40_open() would trigger and start the sur40_poll() worker thread. This worker thread invokes sur40_process_video() and accesses the uninitialized vb2_queue structure, leading to a data race and potential system crash. Furthermore, if V4L2 or video registration failed after input_register_device() succeeded, the error path fell through to calling input_free_device() on a successfully registered device instead of input_unregister_device(), corrupting input core state. Move input_register_device() to the very end of sur40_probe(). This ensures the V4L2 and video queue structures are fully initialized before polling can start, and naturally resolves the error path bug since input_free_device() is now only called when input registration has not yet occurred. To maintain strict LIFO (Last-In, First-Out) teardown ordering, also move input_unregister_device() to the very beginning of sur40_disconnect(). This guarantees that the input polling worker thread is stopped before V4L2 video components or control handlers are unregistered. Reported-by: sashiko-bot@kernel.org Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260616051235.1549517-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sur40.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index fe63d53d56db..8639ec3ad703 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -725,21 +725,13 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_input; } - /* register the polled input device */ - error = input_register_device(input); - if (error) { - dev_err(&interface->dev, - "Unable to register polled input device."); - goto err_free_buffer; - } - /* register the video master device */ snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG); error = v4l2_device_register(sur40->dev, &sur40->v4l2); if (error) { dev_err(&interface->dev, "Unable to register video master device."); - goto err_unreg_v4l2; + goto err_free_buffer; } /* initialize the lock and subdevice */ @@ -798,6 +790,14 @@ static int sur40_probe(struct usb_interface *interface, goto err_unreg_video; } + /* register the polled input device */ + error = input_register_device(input); + if (error) { + dev_err(&interface->dev, + "Unable to register polled input device."); + goto err_unreg_video; + } + /* we can register the device now, as it is ready */ usb_set_intfdata(interface, sur40); dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC); @@ -823,11 +823,12 @@ static void sur40_disconnect(struct usb_interface *interface) { struct sur40_state *sur40 = usb_get_intfdata(interface); + input_unregister_device(sur40->input); + v4l2_ctrl_handler_free(&sur40->hdl); video_unregister_device(&sur40->vdev); v4l2_device_unregister(&sur40->v4l2); - input_unregister_device(sur40->input); kfree(sur40->bulk_in_buffer); kfree(sur40); -- cgit v1.2.3 From 062dc4693e2c10d77de06f61e6f3faf37c0a8383 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 15 Jun 2026 22:12:30 -0700 Subject: Input: sur40 - fix V4L error path cleanup In sur40_probe(), if video_register_device() fails, the error path jumps to err_unreg_video. This incorrectly attempts to unregister a video device that was never successfully registered, and fails to free the V4L2 control handler (v4l2_ctrl_handler_free) that was initialized immediately prior. Fix this by introducing an err_free_ctrl label to properly free the V4L2 control handler and bypass video_unregister_device() when video device registration fails. Reported-by: sashiko-bot@kernel.org Cc: stable@vger.kernel.org Assisted-by: Antigravity:gemini-3.5-flash Link: https://patch.msgid.link/20260616051235.1549517-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/sur40.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 8639ec3ad703..e9089b0c3e2f 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -787,7 +787,7 @@ static int sur40_probe(struct usb_interface *interface, if (error) { dev_err(&interface->dev, "Unable to register video subdevice."); - goto err_unreg_video; + goto err_free_ctrl; } /* register the polled input device */ @@ -806,6 +806,8 @@ static int sur40_probe(struct usb_interface *interface, err_unreg_video: video_unregister_device(&sur40->vdev); +err_free_ctrl: + v4l2_ctrl_handler_free(&sur40->hdl); err_unreg_v4l2: v4l2_device_unregister(&sur40->v4l2); err_free_buffer: -- cgit v1.2.3 From 8c283e7b56adce00193837f3311b06662466fb21 Mon Sep 17 00:00:00 2001 From: Yuxiang Yang Date: Sun, 9 Aug 2026 12:38:06 +0800 Subject: sctp: fix use-after-free of cached ASCONF chunk addip_last_asconf caches the outstanding outbound ASCONF chunk. The normal ASCONF-ACK completion path releases the chunk and clears the pointer. However, sctp_asconf_queue_teardown() releases the cached chunk without clearing addip_last_asconf. During peer restart handling, sctp_sf_do_dupcook_a() queues SCTP_CMD_PURGE_ASCONF_QUEUE, which invokes sctp_asconf_queue_teardown() while the association remains alive and leaves the pointer dangling. A delayed authenticated ASCONF-ACK can then reach sctp_sf_do_asconf_ack(), which accesses the stale chunk and passes it to sctp_process_asconf_ack(), causing a use-after-free and a second release. Clearing the pointer exposes a race with T4 expiry. Peer restart handling queues the timer stop before the purge, but SCTP_CMD_TIMER_STOP uses timer_delete(), which does not wait for a callback already running on another CPU. Such a callback can reach sctp_sf_t4_timer_expire() after the purge and dereference NULL. Clear addip_last_asconf after releasing the cached chunk, and make sctp_sf_t4_timer_expire() consume a stale T4 expiry if no outstanding ASCONF remains. Fixes: a000c01e60e4 ("sctp: stop pending timers and purge queues when peer restart asoc") Cc: stable@vger.kernel.org Suggested-by: Xin Long Signed-off-by: Yuxiang Yang Acked-by: Xin Long Link: https://patch.msgid.link/20260809043806.2768302-1-yangyx22@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- net/sctp/associola.c | 4 +++- net/sctp/sm_statefuns.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index c65c83638cce..5be0bed2685e 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1716,6 +1716,8 @@ void sctp_asconf_queue_teardown(struct sctp_association *asoc) sctp_assoc_free_asconf_queue(asoc); /* Free any cached ASCONF chunk. */ - if (asoc->addip_last_asconf) + if (asoc->addip_last_asconf) { sctp_chunk_free(asoc->addip_last_asconf); + asoc->addip_last_asconf = NULL; + } } diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 708fa07d5fff..3a8e16b29660 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -6145,8 +6145,12 @@ enum sctp_disposition sctp_sf_t4_timer_expire( struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = asoc->addip_last_asconf; - struct sctp_transport *transport = chunk->transport; + struct sctp_transport *transport; + + if (!chunk) + return SCTP_DISPOSITION_CONSUME; + transport = chunk->transport; SCTP_INC_STATS(net, SCTP_MIB_T4_RTO_EXPIREDS); /* ADDIP 4.1 B1) Increment the error counters and perform path failure -- cgit v1.2.3 From 6ca662cc71df7eb4eaf1b4bcb07cd3f188ad19f2 Mon Sep 17 00:00:00 2001 From: Jérémy Jean Date: Wed, 12 Aug 2026 20:30:42 +0000 Subject: io_uring/rsrc: reject overflowing regvec bvec byte counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit io_import_reg_vec() converts the estimated number of bio_vec entries into iovec-sized storage when struct bio_vec is larger than struct iovec. The conversion still multiplies nr_segs by sizeof(struct bio_vec) in size_t without checking for overflow. On 32-bit kernels, a registered buffer large enough to make io_estimate_bvec_size() return 357913942 segments wraps the byte count from 0x100000008 to 8. io_vec_realloc() then reserves only the input iovecs plus one extra slot while io_vec_fill_bvec() writes the full bio_vec array. Check both the multiplication and the rounding addition before deriving the replacement iovec count. Fixes: b4e41050b212 ("io_uring/rsrc: raise registered buffer 1GB limit") Assisted-by: Codex:gpt-5 Signed-off-by: Jérémy Jean Link: https://patch.msgid.link/20260812203042.720348-1-Jeremy.Jean@oss.cyber.gouv.fr Signed-off-by: Jens Axboe --- io_uring/rsrc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index 8d0f2ee24e0c..98dccefd801b 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -1653,8 +1653,12 @@ int io_import_reg_vec(int ddir, struct iov_iter *iter, if (sizeof(struct bio_vec) > sizeof(struct iovec)) { size_t bvec_bytes; - bvec_bytes = nr_segs * sizeof(struct bio_vec); - nr_segs = (bvec_bytes + sizeof(*iov) - 1) / sizeof(*iov); + if (check_mul_overflow((size_t)nr_segs, sizeof(struct bio_vec), + &bvec_bytes) || + check_add_overflow(bvec_bytes, sizeof(*iov) - 1, + &bvec_bytes)) + return -EOVERFLOW; + nr_segs = bvec_bytes / sizeof(*iov); nr_segs += nr_iovs; } -- cgit v1.2.3 From 6c916e301fa10de9158b922474ade7b43d726cda Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Wed, 12 Aug 2026 13:56:28 -0700 Subject: accel/amdxdna: Skip unmapped range in aie2_populate_range() aie2_populate_range() incorrectly failed jobs for BOs with multiple mmaps: if the unmapped entry appeared first in umap_list, the loop would pick it up, call hmm_range_fault() on a gone VMA, and return -EFAULT without ever trying the remaining valid mapps. Fix it by skipping unmapped entries. After the loop, if the map list is empty or all maps are valid, map_invalid can be cleared normally. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260812205628.810816-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 2b97d9707129..b713a57b3a3b 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -1053,6 +1053,16 @@ again: found = false; down_write(&xdna->notifier_lock); list_for_each_entry(mapp, &abo->mem.umap_list, node) { + /* + * Skip entries that have already been unmapped. + * + * If userspace unmaps the address and later submits I/O using + * it, the IOMMU will reject the access and report a fault. + * Ignore such entries here. + */ + if (mapp->unmapped) + continue; + if (mapp->invalid && kref_get_unless_zero(&mapp->refcnt)) { found = true; break; @@ -1060,6 +1070,12 @@ again: } if (!found) { + /* + * This also covers the case where all mappings have been + * removed. There are no invalid mappings left to process. + * Any subsequent I/O using the unmapped address will be + * rejected by the IOMMU. + */ abo->mem.map_invalid = false; up_write(&xdna->notifier_lock); return 0; -- cgit v1.2.3 From 120977e2c096deea4e866e4273be9220b957c29e Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 9 Aug 2026 05:44:18 -0400 Subject: net/sched: cls_bpf: reject dev-bound programs bound to a different device cls_bpf_prog_from_efd() obtained a SCHED_CLS program via bpf_prog_get_type_dev() but never verified that a device-bound (offloaded) program's bound netdev matches the TC netdev the classifier is being attached to. This let a program loaded with prog_ifindex for device A be attached via cls_bpf + skip_sw to device B; deleting device A then destroyed the program's offload state while it was still attached to device B, triggering a netdevsim WARN (panic with panic_on_warn=1). Mirror the XDP attach path (net/core/dev.c) and reject the attach with -EINVAL when a dev-bound program's bound device does not match the target device. Fixes: 2b3486bc2d23 ("bpf: Introduce device-bound XDP programs") Reported-by: vega@nebusec.ai Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Acked-by: Daniel Borkmann Link: https://patch.msgid.link/20260809094418.901607-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/cls_bpf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 001d8c4ebfed..6d19155becc8 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -374,7 +374,8 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog) } static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, - u32 gen_flags, const struct tcf_proto *tp) + u32 gen_flags, const struct tcf_proto *tp, + struct netlink_ext_ack *extack) { struct bpf_prog *fp; char *name = NULL; @@ -388,6 +389,19 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, if (IS_ERR(fp)) return PTR_ERR(fp); + if (bpf_prog_is_dev_bound(fp->aux)) { + struct tcf_block *block = tp->chain->block; + struct net_device *dev; + + dev = block->q ? qdisc_dev(block->q) : NULL; + if (!dev || !bpf_offload_dev_match(fp, dev)) { + NL_SET_ERR_MSG(extack, + "Program is bound to a different device"); + bpf_prog_put(fp); + return -EINVAL; + } + } + if (tb[TCA_BPF_NAME]) { name = nla_memdup(tb[TCA_BPF_NAME], GFP_KERNEL); if (!name) { @@ -492,7 +506,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, prog->gen_flags = gen_flags; ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) : - cls_bpf_prog_from_efd(tb, prog, gen_flags, tp); + cls_bpf_prog_from_efd(tb, prog, gen_flags, tp, extack); if (ret < 0) goto errout_idr; -- cgit v1.2.3 From 9006c116dd111d457bf5d074990210f70a4ad2c8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 11 Aug 2026 14:46:51 +0000 Subject: l2tp: fix tunnel and session refcount leak on seq_file release In pppol2tp_proc_open() and l2tp_dfs_seq_open(), iteration state (pd->tunnel and pd->session) is kept in seq_file private data to allow iteration across multiple read() system calls. However, if userspace closes /proc/net/pppol2tp or /sys/kernel/debug/l2tp/tunnels before reading to end-of-file (EOF), any tunnel or session reference stored in pd->tunnel / pd->session is left un-dropped when seq_file private data is freed. Fix this by dropping any remaining pd->tunnel and pd->session references in pppol2tp_proc_release() and l2tp_dfs_seq_release() when closing the file. Fixes: 0e0c3fee3a59 ("l2tp: hold reference on tunnels printed in pppol2tp proc file") Fixes: f726214d9b23 ("l2tp: hold reference on tunnels printed in l2tp/tunnels debugfs file") Reported-by: syzbot+d6fa74e3f19d6ee01e3a@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a760f32.01d0871a.3a0d52.004f.GAE@google.com/T/#u Assisted-by: Jetski:Gemini-3.1-Pro Cc: James Chapman Cc: Guillaume Nault Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260811144651.2733424-1-edumazet@google.com Signed-off-by: Paolo Abeni --- net/l2tp/l2tp_debugfs.c | 4 ++++ net/l2tp/l2tp_ppp.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/net/l2tp/l2tp_debugfs.c b/net/l2tp/l2tp_debugfs.c index b26986fda9d6..c12d5e1ee550 100644 --- a/net/l2tp/l2tp_debugfs.c +++ b/net/l2tp/l2tp_debugfs.c @@ -306,6 +306,10 @@ static int l2tp_dfs_seq_release(struct inode *inode, struct file *file) seq = file->private_data; pd = seq->private; + if (pd->session) + l2tp_session_put(pd->session); + if (pd->tunnel) + l2tp_tunnel_put(pd->tunnel); if (pd->net) put_net_track(pd->net, &pd->ns_tracker); kfree(pd); diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index f8881699e1ca..ef8fdfaf051d 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1597,7 +1597,53 @@ static const struct seq_operations pppol2tp_seq_ops = { .stop = pppol2tp_seq_stop, .show = pppol2tp_seq_show, }; -#endif /* CONFIG_PROC_FS */ + +static int pppol2tp_proc_open(struct inode *inode, struct file *file) +{ + struct net *net = pde_data(inode); + struct pppol2tp_seq_data *pd; + + net = maybe_get_net(net); + if (!net) + return -ENXIO; + + pd = __seq_open_private(file, &pppol2tp_seq_ops, sizeof(*pd)); + if (!pd) { + put_net(net); + return -ENOMEM; + } + +#ifdef CONFIG_NET_NS + pd->p.net = net; + netns_tracker_alloc(net, &pd->p.ns_tracker, GFP_KERNEL); +#endif + return 0; +} + +static int pppol2tp_proc_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct pppol2tp_seq_data *pd = seq->private; + + if (pd->session) + l2tp_session_put(pd->session); + if (pd->tunnel) + l2tp_tunnel_put(pd->tunnel); + +#ifdef CONFIG_NET_NS + put_net_track(pd->p.net, &pd->p.ns_tracker); +#else + put_net(&init_net); +#endif + return seq_release_private(inode, file); +} + +static const struct proc_ops pppol2tp_proc_ops = { + .proc_open = pppol2tp_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = pppol2tp_proc_release, +}; /***************************************************************************** * Network namespace @@ -1608,8 +1654,8 @@ static __net_init int pppol2tp_init_net(struct net *net) struct proc_dir_entry *pde; int err = 0; - pde = proc_create_net("pppol2tp", 0444, net->proc_net, - &pppol2tp_seq_ops, sizeof(struct pppol2tp_seq_data)); + pde = proc_create_data("pppol2tp", 0444, net->proc_net, + &pppol2tp_proc_ops, net); if (!pde) { err = -ENOMEM; goto out; @@ -1624,9 +1670,13 @@ static __net_exit void pppol2tp_exit_net(struct net *net) remove_proc_entry("pppol2tp", net->proc_net); } +#endif /* CONFIG_PROC_FS */ + static struct pernet_operations pppol2tp_net_ops = { +#ifdef CONFIG_PROC_FS .init = pppol2tp_init_net, .exit = pppol2tp_exit_net, +#endif }; /***************************************************************************** -- cgit v1.2.3 From 42d217add8d80d6e7d9f58f80d11ea9b07ea113e Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 7 Aug 2026 14:25:26 +0000 Subject: firewire: ohci: fix NULL pointer dereference in ar_context_release During the error handling path of the driver's probe function, a NULL pointer dereference can occur in ar_context_release(). When pci_probe() fails early (e.g., if pcim_enable_device() or MMIO mapping fails), the devres cleanup mechanism invokes release_ohci(). This function unconditionally calls ar_context_release() to clean up the asynchronous receive contexts. However, if ar_context_init() was not yet called, ctx->ohci remains NULL (as the fw_ohci structure is zero-initialized by devres_alloc()). ar_context_release() immediately dereferences ctx->ohci to get the dev pointer before checking if the context was actually initialized, leading to a crash: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:ar_context_release+0x3f/0x380 drivers/firewire/ohci.c:543 Call Trace: release_ohci+0x3f/0x60 drivers/firewire/ohci.c:3567 release_nodes drivers/base/devres.c:546 [inline] devres_release_all+0x1a8/0x260 drivers/base/devres.c:576 device_unbind_cleanup drivers/base/dd.c:597 [inline] really_probe+0x451/0xae0 drivers/base/dd.c:772 To fix this, move the assignment of the dev pointer after the !ctx->buffer check. If ctx->buffer is NULL, it indicates that the context was never successfully initialized and there is nothing to release, safely avoiding the dereference of the uninitialized ctx->ohci pointer. Fixes: 5716e58aecdd ("firewire: ohci: release buffer for AR req/resp contexts when managed resource is released") Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+d30aad27833a559defab@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=d30aad27833a559defab Link: https://syzkaller.appspot.com/ai_job?id=10a18617-7893-42dd-bf1c-cd49e19e95d9 Signed-off-by: Aleksandr Nogikh Link: https://lore.kernel.org/r/90c5db71-dd1f-4d46-b9d3-2f1046cbd5ea@mail.kernel.org Signed-off-by: Takashi Sakamoto --- drivers/firewire/ohci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 8153d62c58f0..e947227e01ac 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -540,11 +540,13 @@ static void ar_context_link_page(struct ar_context *ctx, unsigned int index) static void ar_context_release(struct ar_context *ctx) { - struct device *dev = ctx->ohci->card.device; + struct device *dev; if (!ctx->buffer) return; + dev = ctx->ohci->card.device; + for (int i = 0; i < AR_BUFFERS; ++i) { dma_addr_t dma_addr = ctx->dma_addrs[i]; if (dma_addr) -- cgit v1.2.3 From fc648757908304aedbad74f74bf58192aec383db Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 30 Jun 2026 15:55:20 +0200 Subject: drm/xe: Fix DPT allocation paths. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the fallback for VRAM to system memory, I tested it and that doesn't work at all, only a black screen with pipe fault errors were observed. On systems with media GT, extra latency is added when accessing stolen memory when the GT is in MC6. Since we additionally aren't counting how much memory is used for stolen and we could in theory fill up the entire stolen area with DPT's, avoid using stolen and only use the default memory region. Using stolen may also result in random system hangs under load. Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7513 Fixes: 775d0adc01a5 ("drm/xe/fbdev: Limit the usage of stolen for LNL+") Cc: # v6.12+ Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260630135523.1775379-2-dev@lankhorst.se Signed-off-by: Maarten Lankhorst Acked-by: Matthew Brost #teams (cherry picked from commit a196406a3831291598fe8e73245914f7acffdfe0) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/display/xe_fb_pin.c | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index 5f4a0cd8deca..73469ea5f333 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -164,31 +164,14 @@ static int __xe_pin_fb_vma_dpt(struct drm_gem_object *obj, dpt_size = ALIGN(intel_rotation_info_size(&view->rotated) * 8, XE_PAGE_SIZE); - if (IS_DGFX(xe)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_VRAM0 | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - else - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_STOLEN | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - if (IS_ERR(dpt)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_SYSTEM | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE | - XE_BO_FLAG_FORCE_WC, - pin_params->alignment, false); + dpt = xe_bo_create_pin_map_at_novm(xe, tile0, + dpt_size, ~0ull, + ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile0) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_PAGETABLE | + XE_BO_FLAG_FORCE_WC, + pin_params->alignment, false); if (IS_ERR(dpt)) return PTR_ERR(dpt); -- cgit v1.2.3 From 99b01815957bd7d848420cb697f79ed11f7f215c Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:55 +0000 Subject: drm/xe/guc_ads: allocate UM queues in a separate BO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the UM queues into a dedicated BO (ads->um_queue_bo) and avoid CPU memset operations on it, which eliminates the CPU as a potential cacheline-polluting agent and helps maintain consistency between GAM writes and GuC reads. We also need to ensure the base_dpa for the queue is contiguous on hw where this is used instead of a GGTT address. Another good reason to split this out to a separate BO. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed-by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-2-jia.yao@intel.com (cherry picked from commit 6af05de0dc19bbf3aaeec2020fe48b37c834b811) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_bo.h | 14 ++++++ drivers/gpu/drm/xe/xe_guc.c | 6 +++ drivers/gpu/drm/xe/xe_guc_ads.c | 90 ++++++++++++++++++++++------------- drivers/gpu/drm/xe/xe_guc_ads_types.h | 5 ++ 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index 7ae1d9ac0574..57039cf42ea7 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -6,6 +6,7 @@ #ifndef _XE_BO_H_ #define _XE_BO_H_ +#include #include #include "xe_bo_types.h" @@ -548,6 +549,19 @@ void xe_bo_dev_fini(struct xe_bo_dev *bo_device); struct sg_table *xe_bo_sg(struct xe_bo *bo); +/** + * xe_bo_sg_is_contiguous() - Check if a BO's DMA address space is contiguous. + * @bo: the BO to check (must have a valid sg table, i.e. !xe_bo_is_vram()) + * @len: required contiguous length in bytes + * + * Returns true if the first @len bytes of the BO are mapped to a contiguous + * DMA address range. + */ +static inline bool xe_bo_sg_is_contiguous(struct xe_bo *bo, size_t len) +{ + return drm_prime_get_contiguous_size(xe_bo_sg(bo)) >= len; +} + /* * xe_sg_segment_size() - Provides upper limit for sg segment size. * @dev: device pointer diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 4023700ff2a9..21cf3edef922 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -714,6 +714,12 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc) if (ret) return ret; + if (guc->ads.um_queue_bo) { + ret = xe_managed_bo_reinit_in_vram(xe, tile, &guc->ads.um_queue_bo); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index c98454545a85..a411d6440ba6 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -63,10 +63,14 @@ ads_to_map(struct xe_guc_ads *ads) /* * The Additional Data Struct (ADS) has pointers for different buffers used by - * the GuC. One single gem object contains the ADS struct itself (guc_ads) and - * all the extra buffers indirectly linked via the ADS struct's entries. + * the GuC. One gem object (ads->bo) contains the ADS struct itself (guc_ads) + * and most of the extra buffers linked via the ADS struct's entries. The UM + * fault queues (PAGE_FAULT, PAGE_FAULT_RESPONSE, ACCESS_COUNTER rings) are + * kept in a separate BO (ads->um_queue_bo) so that the full memset of ads->bo + * performed on every GT reset does not discard fault descriptors already + * written into the rings by the GPU. * - * Layout of the ADS blob allocated for the GuC: + * Layout of the ADS blob (ads->bo): * * +---------------------------------------+ <== base * | guc_ads | @@ -98,10 +102,6 @@ ads_to_map(struct xe_guc_ads *ads) * +---------------------------------------+ * | padding | * +---------------------------------------+ <== 4K aligned - * | UM queues | - * +---------------------------------------+ - * | padding | - * +---------------------------------------+ <== 4K aligned * | private data | * +---------------------------------------+ * | padding | @@ -155,16 +155,6 @@ static size_t guc_ads_capture_size(struct xe_guc_ads *ads) return PAGE_ALIGN(ads->capture_size); } -static size_t guc_ads_um_queues_size(struct xe_guc_ads *ads) -{ - struct xe_device *xe = ads_to_xe(ads); - - if (!xe->info.has_usm) - return 0; - - return GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX; -} - static size_t guc_ads_private_data_size(struct xe_guc_ads *ads) { return PAGE_ALIGN(ads_to_guc(ads)->fw.private_data_size); @@ -205,22 +195,12 @@ static size_t guc_ads_capture_offset(struct xe_guc_ads *ads) return PAGE_ALIGN(offset); } -static size_t guc_ads_um_queues_offset(struct xe_guc_ads *ads) -{ - u32 offset; - - offset = guc_ads_capture_offset(ads) + - guc_ads_capture_size(ads); - - return PAGE_ALIGN(offset); -} - static size_t guc_ads_private_data_offset(struct xe_guc_ads *ads) { size_t offset; - offset = guc_ads_um_queues_offset(ads) + - guc_ads_um_queues_size(ads); + offset = guc_ads_capture_offset(ads) + + guc_ads_capture_size(ads); return PAGE_ALIGN(offset); } @@ -409,6 +389,46 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) ads->bo = bo; + if (xe->info.has_usm) { + /* + * Allocate a separate BO for the HW fault ring (UM queues). + * + * Round the size up to the next power of two so that + * (system memory, no IOMMU) the TTM pool issues a single + * alloc_pages(order=N) call, maximising the chance of getting + * a physically contiguous block. GuC requires contiguous DPA. + */ + size_t um_size = roundup_pow_of_two(GUC_UM_QUEUE_SIZE * + GUC_UM_HW_QUEUE_MAX); + + u32 um_flags = XE_BO_FLAG_SYSTEM | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_PINNED_NORESTORE; + + bo = xe_managed_bo_create_pin_map(xe, tile, um_size, um_flags); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + /* + * On pre-Xe3p platforms, GAM (not GuC) accesses the UM queue + * ring via base_dpa, which must be a contiguous DMA address + * range. Verify that the allocated pages are contiguous in + * DMA address space. + */ + if (!xe_bo_is_vram(bo) && + !xe_guc_using_main_gamctrl_queues(ads_to_guc(ads)) && + unlikely(!xe_bo_sg_is_contiguous(bo, + GUC_UM_QUEUE_SIZE * + GUC_UM_HW_QUEUE_MAX))) { + drm_err(&xe->drm, + "UM fault queue memory is not contiguous in DMA address space; GAM requires contiguous DPA\n"); + return -ENOMEM; + } + + ads->um_queue_bo = bo; + } + return 0; } ALLOW_ERROR_INJECTION(xe_guc_ads_init, ERRNO); /* See xe_pci_probe() */ @@ -820,7 +840,7 @@ static void guc_mmio_reg_state_init(struct xe_guc_ads *ads) static void guc_um_init_params(struct xe_guc_ads *ads) { - u32 um_queue_offset = guc_ads_um_queues_offset(ads); + struct xe_bo *um_bo = ads->um_queue_bo; struct xe_guc *guc = ads_to_guc(ads); struct xe_device *xe = ads_to_xe(ads); u64 base_dpa; @@ -830,8 +850,14 @@ static void guc_um_init_params(struct xe_guc_ads *ads) with_dpa = !xe_guc_using_main_gamctrl_queues(guc); - base_ggtt = xe_bo_ggtt_addr(ads->bo) + um_queue_offset; - base_dpa = xe_bo_main_addr(ads->bo, PAGE_SIZE) + um_queue_offset; + if (um_bo) { + /* All USM platforms: UM queues in dedicated um_queue_bo */ + base_ggtt = xe_bo_ggtt_addr(um_bo); + base_dpa = xe_bo_main_addr(um_bo, PAGE_SIZE); + } else { + /* Platform does not support USM: no UM queues, nothing to do */ + return; + } for (i = 0; i < GUC_UM_HW_QUEUE_MAX; ++i) { /* diff --git a/drivers/gpu/drm/xe/xe_guc_ads_types.h b/drivers/gpu/drm/xe/xe_guc_ads_types.h index 48a8e092023f..845c1fbd93a4 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads_types.h +++ b/drivers/gpu/drm/xe/xe_guc_ads_types.h @@ -16,6 +16,11 @@ struct xe_bo; struct xe_guc_ads { /** @bo: Xe BO for GuC ads blob */ struct xe_bo *bo; + /** + * @um_queue_bo: Dedicated BO for the HW fault ring (UM queues). + * NULL if the platform does not support USM. + */ + struct xe_bo *um_queue_bo; /** @golden_lrc_size: golden LRC size */ size_t golden_lrc_size; /** @regset_size: size of register set passed to GuC for save/restore */ -- cgit v1.2.3 From f342810a141f8a7e8b3786a6e4b6c0695a078a74 Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:56 +0000 Subject: drm/xe/guc_ads: allocate UM queues in VRAM on dGFX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On iGPU, the UM queue BO is allocated in system memory. On dGFX, the BO was previously created in system memory and later reallocated in xe_guc_realloc_post_hwconfig(). Allocate the UM queue BO directly in VRAM on dGFX, where it is ultimately required. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed-by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-3-jia.yao@intel.com (cherry picked from commit ace076ef0a854ab5940bacc539bf66afd61d118c) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc.c | 6 ------ drivers/gpu/drm/xe/xe_guc_ads.c | 8 +++++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 21cf3edef922..4023700ff2a9 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -714,12 +714,6 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc) if (ret) return ret; - if (guc->ads.um_queue_bo) { - ret = xe_managed_bo_reinit_in_vram(xe, tile, &guc->ads.um_queue_bo); - if (ret) - return ret; - } - return 0; } diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index a411d6440ba6..b6d07f2e73bf 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -393,15 +393,17 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) /* * Allocate a separate BO for the HW fault ring (UM queues). * - * Round the size up to the next power of two so that + * Round the size up to the next power of two so that on iGPU * (system memory, no IOMMU) the TTM pool issues a single * alloc_pages(order=N) call, maximising the chance of getting * a physically contiguous block. GuC requires contiguous DPA. */ - size_t um_size = roundup_pow_of_two(GUC_UM_QUEUE_SIZE * + size_t um_size = IS_DGFX(xe) ? + GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX : + roundup_pow_of_two(GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX); - u32 um_flags = XE_BO_FLAG_SYSTEM | + u32 um_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE | XE_BO_FLAG_PINNED_NORESTORE; -- cgit v1.2.3 From 8d5134ae4177fa4f5a9bc8e71e6656cfc2852882 Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:57 +0000 Subject: drm/xe/guc_ads: use uncached mapping for UM queue BO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Pre-Xe3p platform, the GAM write the UM queue through DPA using UC. if GuC reads the queue via GGTT (WB), stale data may be observed when the cacheline has been polluted by another agent. To match the GAM's UC writes, configure the GuC mapping as UC as well. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-4-jia.yao@intel.com (cherry picked from commit 9daa302a82590eeee7bdc68023ddad302df4b88c) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_ads.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index b6d07f2e73bf..886bafd31f45 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -406,7 +406,8 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) u32 um_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE | - XE_BO_FLAG_PINNED_NORESTORE; + XE_BO_FLAG_PINNED_NORESTORE | + XE_BO_FLAG_NEEDS_UC; bo = xe_managed_bo_create_pin_map(xe, tile, um_size, um_flags); if (IS_ERR(bo)) -- cgit v1.2.3 From 9f83c94469ff0fa37274b873ba24922e02531fa7 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 7 Aug 2026 10:17:16 -0700 Subject: drm/xe: Order ring writes before ring tail updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ring buffer and the LRC context image are both mapped WC, so the ring tail update can become visible to the device before the ring contents it is meant to publish. The GuC CT send does contain an xe_device_wmb(), so sending the H2G would flush the ring contents. The problem is that it comes too late: xe_lrc_set_ring_tail() publishes the tail before the H2G is sent, and the device samples the tail from the context image independently of it, either at context switch-in or while the context is already resident. A submitter which is interrupted between updating the tail and sending its H2G therefore leaves the device free to observe the new tail while the ring contents behind it are not yet visible: 1. Thread A emits a job into the ring, sets the tail to T_A and sends the H2G, which flushes A's ring contents. The GuC starts scheduling the context in, but it is not executing yet. 2. Thread B emits a job into ring[T_A..T_B]. Those writes are not yet visible to the device. 3. Thread B updates the ring tail to T_B. That write targets a different page and becomes visible first. 4. Thread B is interrupted before it sends its H2G, so the flush which would have published ring[T_A..T_B] has not happened yet. 5. The context is switched in and samples the ring tail from the context image, picking up T_B rather than T_A. 6. The GPU executes A's job, advances HEAD to T_A, and continues on to ring[T_A..T_B], which still holds the previous wrap's contents, so the CS parses stale commands. The result is command stream corruption, which typically manifests as a hang or a spurious pagefault rather than anything that points back at the submission path. Kernel jobs are by far the most likely to hit this. Kernel queues such as the migration queue are shared and can be driven by many threads concurrently, producing back-to-back submissions on an LRC which is already executing. User queues are typically tied to a single submitting thread, so the same interleaving is much harder to produce. Add an xe_device_wmb() at the end of xe_lrc_write_ring() so that it covers every ring tail publication site, and so the invariant is local: once xe_lrc_write_ring() returns, the ring contents are visible to the device. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8651 Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7810 Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Assisted-by: GitHub_Copilot:claude-opus-5 Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260807171716.140475-1-matthew.brost@intel.com (cherry picked from commit 136360290f314890428a3fbf31aaa8e4f1d43567) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_lrc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index a4292a11391d..78969d3e5904 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1849,6 +1849,13 @@ void xe_lrc_write_ring(struct xe_lrc *lrc, const void *data, size_t size) __xe_lrc_write_ring(lrc, ring, &noop, sizeof(noop)); } + + /* + * The ring and the LRC context image are both WC, so the ring tail + * update which publishes these writes can become visible to the device + * first. Ensure the ring contents are visible before returning. + */ + xe_device_wmb(xe); } u64 xe_lrc_descriptor(struct xe_lrc *lrc) -- cgit v1.2.3 From 51afaf53e01e01bda489fc6ffacf07a706e72783 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Mon, 20 Jul 2026 15:27:58 -0700 Subject: drm/xe/pxp: add termination on resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suspend/resume causes the PXP keys to become invalid, but doesn't actually kill the session. The driver also doesn't explicitly kill and re-start the session until a new PXP request comes in, which means that the "zombie" session can potentially stick around if there are no new requests from userspace. While this is not an issue for PXP, HDCP has a new behavior starting on PTL where a communication is sent to GSC if a session is active at suspend time (even if it doesn't have a valid key), which can lead to delays in the suspend flow if we suspend while the zombie session is still active. To avoid this, we can trigger a termination on resume and kill the zombie session immediately, instead of delaying the termination to the next PXP request. Due to restrictions in the rpm suspend/resume flow, we can't call the termination flow from within the resume call itself, so the pxp irq worker is expanded to cover this scenario. The existing logic in the worker doesn't work as-is for the new flow, because the pm_get_if_active will fail if the worker runs before the pci_resume call has completed (which is possible, since we queue it from within that call) or after we're started to suspend again. Given that we always want to run the worker after a resume (differently from the irq case, where we want to skip if we're suspended), we can solve this by just taking the PM reference before queueing the worker. As part of this rework, the pxp->events variable has been moved to atomic, to avoid having to take xe->irq.lock from non-irq related paths. Fixes: b1dcec9bd8a1 ("drm/xe/ptl: Enable PXP for PTL") Signed-off-by: Daniele Ceraolo Spurio Cc: Rodrigo Vivi Cc: Julia Filipchuk Cc: Alan Previn Reviewed-by: Alan Previn Link: https://patch.msgid.link/20260720222757.3876338-2-daniele.ceraolospurio@intel.com (cherry picked from commit 757bda2b8b93fa36ad9b2c7993081d5f9d0d6e3b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pxp.c | 162 ++++++++++++++++++++++++++++---------- drivers/gpu/drm/xe/xe_pxp_types.h | 27 +++++-- 2 files changed, 139 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 968b7e70b3f9..d17ab6e59df5 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -8,6 +8,8 @@ #include #include +#include + #include "xe_bo.h" #include "xe_bo_types.h" #include "xe_device_types.h" @@ -164,16 +166,9 @@ static void mark_termination_in_progress(struct xe_pxp *pxp) pxp->status = XE_PXP_TERMINATION_IN_PROGRESS; } -static void pxp_terminate(struct xe_pxp *pxp) +static bool pxp_prep_for_termination(struct xe_pxp *pxp) { - int ret = 0; - struct xe_device *xe = pxp->xe; - - if (!wait_for_completion_timeout(&pxp->activation, - msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) - drm_err(&xe->drm, "failed to wait for PXP start before termination\n"); - - mutex_lock(&pxp->mutex); + lockdep_assert_held(&pxp->mutex); if (pxp->status == XE_PXP_ACTIVE) pxp->key_instance++; @@ -182,10 +177,8 @@ static void pxp_terminate(struct xe_pxp *pxp) * we'll mark the status as needing termination on resume, so no need to * emit a termination now. */ - if (pxp->status == XE_PXP_SUSPENDED) { - mutex_unlock(&pxp->mutex); - return; - } + if (pxp->status == XE_PXP_SUSPENDED) + return false; /* * If we have a termination already in progress, we need to wait for @@ -195,15 +188,44 @@ static void pxp_terminate(struct xe_pxp *pxp) */ if (pxp->status == XE_PXP_TERMINATION_IN_PROGRESS) { pxp->status = XE_PXP_NEEDS_ADDITIONAL_TERMINATION; - mutex_unlock(&pxp->mutex); - return; + return false; } mark_termination_in_progress(pxp); - mutex_unlock(&pxp->mutex); + return true; +} + +static void pxp_terminate(struct xe_pxp *pxp, bool hw_only) +{ + struct xe_device *xe = pxp->xe; + int ret = 0; - pxp_invalidate_queues(pxp); + if (!wait_for_completion_timeout(&pxp->activation, + msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) + drm_err(&xe->drm, "failed to wait for PXP start before termination\n"); + + if (!hw_only) { + bool prep_ok; + + mutex_lock(&pxp->mutex); + + prep_ok = pxp_prep_for_termination(pxp); + + mutex_unlock(&pxp->mutex); + + if (!prep_ok) + return; + + pxp_invalidate_queues(pxp); + } else { + /* + * The caller of the HW-only termination should have already + * called pxp_prep_for_termination and marked the termination as + * in progress. + */ + xe_assert(xe, !completion_done(&pxp->termination)); + } ret = pxp_terminate_hw(pxp); if (ret) { @@ -249,33 +271,46 @@ static void pxp_terminate_complete(struct xe_pxp *pxp) mutex_unlock(&pxp->mutex); } -static void pxp_irq_work(struct work_struct *work) +static void pxp_events_work(struct work_struct *work) { - struct xe_pxp *pxp = container_of(work, typeof(*pxp), irq.work); + struct xe_pxp *pxp = container_of(work, typeof(*pxp), events.work); struct xe_device *xe = pxp->xe; + bool hw_only = false; u32 events = 0; - spin_lock_irq(&xe->irq.lock); - events = pxp->irq.events; - pxp->irq.events = 0; - spin_unlock_irq(&xe->irq.lock); + events = atomic_xchg(&pxp->events.pending, 0); if (!events) return; /* - * If we're processing a termination irq while suspending then don't - * bother, we're going to re-init everything on resume anyway. + * If the termination request comes from an irq while we're suspending, + * then we can defer it to the resume path instead of waking the device + * up. + * In the case of the termination on resume the pm reference is taken + * in xe_pxp_pm_resume() and released here. + * Note that we do not expect both events to be set at the same time, + * but if it does happen due to a spurious interrupt we want to behave + * as if the only request we got was the one from the resume path; this + * is because the termination prep has already been done in + * xe_pxp_pm_resume() and it is impossible for any PXP operations to + * occur between the prep and the termination completion, so there is no + * need for a new SW prep. */ - if ((events & PXP_TERMINATION_REQUEST) && !xe_pm_runtime_get_if_active(xe)) + if (events & PXP_TERMINATION_REQUEST_ON_RESUME) { + events &= ~PXP_TERMINATION_REQUEST_IRQ; + hw_only = true; + } + + if ((events & PXP_TERMINATION_REQUEST_IRQ) && !xe_pm_runtime_get_if_active(xe)) return; if (events & PXP_TERMINATION_REQUEST) { - events &= ~PXP_TERMINATION_COMPLETE; - pxp_terminate(pxp); + events &= ~PXP_TERMINATION_COMPLETE_IRQ; + pxp_terminate(pxp, hw_only); } - if (events & PXP_TERMINATION_COMPLETE) + if (events & PXP_TERMINATION_COMPLETE_IRQ) pxp_terminate_complete(pxp); if (events & PXP_TERMINATION_REQUEST) @@ -296,20 +331,18 @@ void xe_pxp_irq_handler(struct xe_device *xe, u16 iir) return; } - lockdep_assert_held(&xe->irq.lock); - if (unlikely(!iir)) return; if (iir & (KCR_PXP_STATE_TERMINATED_INTERRUPT | KCR_APP_TERMINATED_PER_FW_REQ_INTERRUPT)) - pxp->irq.events |= PXP_TERMINATION_REQUEST; + atomic_or(PXP_TERMINATION_REQUEST_IRQ, &pxp->events.pending); if (iir & KCR_PXP_STATE_RESET_COMPLETE_INTERRUPT) - pxp->irq.events |= PXP_TERMINATION_COMPLETE; + atomic_or(PXP_TERMINATION_COMPLETE_IRQ, &pxp->events.pending); - if (pxp->irq.events) - queue_work(pxp->irq.wq, &pxp->irq.work); + if (atomic_read(&pxp->events.pending)) + queue_work(pxp->events.wq, &pxp->events.work); } static int kcr_pxp_set_status(const struct xe_pxp *pxp, bool enable) @@ -340,7 +373,7 @@ static void pxp_fini(void *arg) { struct xe_pxp *pxp = arg; - destroy_workqueue(pxp->irq.wq); + destroy_workqueue(pxp->events.wq); xe_pxp_destroy_execution_resources(pxp); /* no need to explicitly disable KCR since we're going to do an FLR */ @@ -402,7 +435,7 @@ int xe_pxp_init(struct xe_device *xe) INIT_LIST_HEAD(&pxp->queues.list); spin_lock_init(&pxp->queues.lock); - INIT_WORK(&pxp->irq.work, pxp_irq_work); + INIT_WORK(&pxp->events.work, pxp_events_work); pxp->xe = xe; pxp->gt = gt; @@ -421,8 +454,8 @@ int xe_pxp_init(struct xe_device *xe) mutex_init(&pxp->mutex); - pxp->irq.wq = alloc_ordered_workqueue("pxp-wq", 0); - if (!pxp->irq.wq) { + pxp->events.wq = alloc_ordered_workqueue("pxp-wq", 0); + if (!pxp->events.wq) { err = -ENOMEM; goto out_free; } @@ -442,7 +475,7 @@ int xe_pxp_init(struct xe_device *xe) out_kcr_disable: kcr_pxp_disable(pxp); out_wq: - destroy_workqueue(pxp->irq.wq); + destroy_workqueue(pxp->events.wq); out_free: drmm_kfree(&xe->drm, pxp); out: @@ -889,6 +922,7 @@ wait_for_activation: fallthrough; case XE_PXP_ACTIVE: pxp->key_instance++; + pxp->needs_termination_on_resume = true; needs_queue_inval = true; break; } @@ -924,6 +958,7 @@ wait_for_activation: */ void xe_pxp_pm_resume(struct xe_pxp *pxp) { + bool has_pm = false; int err; if (!xe_pxp_is_enabled(pxp)) @@ -931,14 +966,57 @@ void xe_pxp_pm_resume(struct xe_pxp *pxp) err = kcr_pxp_enable(pxp); + /* + * We want to avoid the device runtime suspending before we're done with + * the termination queued below, so we need a runtime PM reference; we + * can't call the rpm functions from within the PXP lock, so we take the + * ref here. Note that we don't want the rpm resume code to actually run + * here as that would call back into this function, but as long as we + * don't enable DPM_FLAG_SMART_SUSPEND (which we currently do not) we're + * guaranteed to not be runtime suspended at this point, so we can + * safely use the get_noresume variant. + */ + if (pxp->needs_termination_on_resume) { + has_pm = true; + + xe_assert(pxp->xe, !dev_pm_smart_suspend(pxp->xe->drm.dev)); + xe_pm_runtime_get_noresume(pxp->xe); + } + mutex_lock(&pxp->mutex); xe_assert(pxp->xe, pxp->status == XE_PXP_SUSPENDED); - if (err) + if (err) { pxp->status = XE_PXP_ERROR; - else + } else { pxp->status = XE_PXP_NEEDS_TERMINATION; + if (pxp->needs_termination_on_resume) { + pxp->needs_termination_on_resume = false; + + /* + * We can't call pxp_terminate_hw directly from here + * because we're not allowed to do allocations within + * the rpm resume call, so we defer the termination to + * the worker that we use for the termination irqs. + * However, we do not want any PXP ops to go through + * between the suspend completing and the worker + * starting, so we need to do the termination prep + * immediately, which will mark the termination as in + * progress and stall PXP ops. + */ + if (pxp_prep_for_termination(pxp)) { + has_pm = false; /* move PM ref ownership to worker */ + + atomic_or(PXP_TERMINATION_REQUEST_ON_RESUME, &pxp->events.pending); + queue_work(pxp->events.wq, &pxp->events.work); + } + } + } + mutex_unlock(&pxp->mutex); + + if (has_pm) + xe_pm_runtime_put(pxp->xe); } diff --git a/drivers/gpu/drm/xe/xe_pxp_types.h b/drivers/gpu/drm/xe/xe_pxp_types.h index ec86306e16f4..8132a9750b6e 100644 --- a/drivers/gpu/drm/xe/xe_pxp_types.h +++ b/drivers/gpu/drm/xe/xe_pxp_types.h @@ -85,17 +85,20 @@ struct xe_pxp { /** @gsc_res: kernel-owned objects for PXP submissions to the GSCCS */ struct xe_pxp_gsc_client_resources gsc_res; - /** @irq: wrapper for the worker and queue used for PXP irq support */ + /** @events: wrapper for the worker and queue used for PXP event handling */ struct { - /** @irq.work: worker that manages irq events. */ + /** @events.work: worker that manages termination events. */ struct work_struct work; - /** @irq.wq: workqueue on which to queue the irq work. */ + /** @events.wq: workqueue on which to queue the work. */ struct workqueue_struct *wq; - /** @irq.events: pending events, protected with xe->irq.lock. */ - u32 events; -#define PXP_TERMINATION_REQUEST BIT(0) -#define PXP_TERMINATION_COMPLETE BIT(1) - } irq; + /** @events.pending: pending events */ + atomic_t pending; +#define PXP_TERMINATION_REQUEST_IRQ BIT(0) +#define PXP_TERMINATION_REQUEST_ON_RESUME BIT(1) +#define PXP_TERMINATION_REQUEST (PXP_TERMINATION_REQUEST_IRQ | \ + PXP_TERMINATION_REQUEST_ON_RESUME) +#define PXP_TERMINATION_COMPLETE_IRQ BIT(2) + } events; /** @mutex: protects the pxp status and the queue list */ struct mutex mutex; @@ -130,6 +133,14 @@ struct xe_pxp { * suspend cycles. */ u32 last_suspend_key_instance; + /** + * @needs_termination_on_resume: indicates if PXP termination is needed + * on resume. This is set if PXP was active when we suspend and it is + * cleared when we queue the termination on resume. Since the suspend + * and resume calls cannot execute at the same time, this variable does + * not need to be protected by the PXP lock. + */ + bool needs_termination_on_resume; }; #endif /* _XE_PXP_TYPES_H_ */ -- cgit v1.2.3 From cb4afddf9e018a83fec8614d8e337d313871569f Mon Sep 17 00:00:00 2001 From: Gajendra Uttamchand Date: Mon, 10 Aug 2026 07:18:14 +0000 Subject: drm/xe/lrc: document sentinel and make CTX_TIMESTAMP read TOCTOU-safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: CTX_TIMESTAMP MMIO reads could be stale if a context switched out between check and read; LRC stores a sentinel while a context starts that must not be treated as a real timestamp. Fix: Check the LRC-stored sentinel before and after the MMIO read; return the LRC value if the context switched out to avoid TOCTOU. Note: Keep XE_LRC_CTX_TIMESTAMP_ACTIVE in xe_lrc.h as the canonical sentinel. Fixes: d243ef6a39c6 ("drm/xe/lrc: Refactor xe_lrc_timestamp to simplify logic") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7956 Assisted-by: GitHub-Copilot:claude-sonnet-5 Signed-off-by: Gajendra Uttamchand Reviewed-by: Umesh Nerlige Ramappa Acked-by: Matthew Brost Signed-off-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260810071812.213358-4-gajendra.uttamchand@intel.com (cherry picked from commit a806534474df071a730d930df479976a812b699d) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_lrc.c | 22 ++++++++++++++-------- drivers/gpu/drm/xe/xe_lrc.h | 7 +++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index 78969d3e5904..9f8217ff1904 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1096,7 +1096,7 @@ static void xe_lrc_finish(struct xe_lrc *lrc) * on until it is scheduled, we also read the ENGINE_ID MMIO in the WA BB and * store it in the PPHSWP. */ -#define CONTEXT_ACTIVE 1ULL +#define CONTEXT_ACTIVE XE_LRC_CTX_TIMESTAMP_ACTIVE static ssize_t setup_utilization_wa(struct xe_lrc *lrc, struct xe_hw_engine *hwe, u32 *batch, @@ -2727,21 +2727,27 @@ static u64 xe_lrc_update_multi_queue_timestamp(struct xe_lrc *lrc, u64 *old_ts) static u64 xe_lrc_context_timestamp(struct xe_lrc *lrc) { u64 reg_ts, new_ts = lrc->ctx_timestamp; + u64 stored; /* CTX_TIMESTAMP mmio read is invalid on VF, so return the LRC value */ if (IS_SRIOV_VF(lrc_to_xe(lrc))) return xe_lrc_ctx_timestamp(lrc); - if (context_active(lrc) && - !get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), ®_ts)) + /* Safely read CTX_TIMESTAMP: Avoid TOCTOU on LRC-stored CONTEXT_ACTIVE sentinel */ + stored = xe_lrc_ctx_timestamp(lrc); + if (stored != CONTEXT_ACTIVE) + return stored; + + /* Context is active: read the live timestamp from the engine's MMIO register */ + if (!get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), ®_ts)) new_ts = reg_ts; - /* - * If context swicthed out while we were here, just return the latest - * LRC CTX TIMESTAMP value. + /* If the context switched out prefer using the value + * from context-save over the stale MMIO read. */ - if (!context_active(lrc)) - return xe_lrc_ctx_timestamp(lrc); + stored = xe_lrc_ctx_timestamp(lrc); + if (stored != CONTEXT_ACTIVE) + return stored; return new_ts; } diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h index 0a3a611391ee..7be5e3da8bc8 100644 --- a/drivers/gpu/drm/xe/xe_lrc.h +++ b/drivers/gpu/drm/xe/xe_lrc.h @@ -9,6 +9,13 @@ #include "xe_lrc_types.h" +/* + * Sentinel value stored in lrc->ctx_timestamp while a context is starting. + * The hardware hasn't yet written the real CTX_TIMESTAMP, so this is not a + * valid elapsed-time sample and must not be used as one. + */ +#define XE_LRC_CTX_TIMESTAMP_ACTIVE 1ULL + struct drm_printer; struct xe_bb; struct xe_device; -- cgit v1.2.3 From 8d33c4987cd162527375a3905017ae129ba7c3fe Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 31 Jul 2026 09:19:32 +0800 Subject: drm/xe/oa: Fix sync entry leak on OA config emit failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xe_oa_emit_oa_config() releases the sync entries and the syncs array only on its success path. When it fails before the point of no return (fence allocation, config buffer allocation or batch submission), it returns without touching stream->syncs. The stream open path handles such failures in the caller, but xe_oa_config_locked() propagates the error without any cleanup, so the syncs array and the fence references held by the parsed entries are leaked. The next config ioctl overwrites stream->syncs, making the memory unreachable for good. Clean up the parsed syncs when xe_oa_emit_oa_config() fails, matching the cleanup done by the stream open error path. Fixes: 9920c8b88c5c ("drm/xe/oa: Add syncs support to OA config ioctl") Signed-off-by: Linmao Li Reviewed-by: Ashutosh Dixit Signed-off-by: Ashutosh Dixit Link: https://patch.msgid.link/20260731011932.3426219-1-lilinmao@kylinos.cn (cherry picked from commit 8af97b3da2cfce04e6b457c6eb17ed3c1daf912b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_oa.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 2dce6a47202c..9cf962b71cb6 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -1594,6 +1594,10 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg) config = xchg(&stream->oa_config, config); drm_dbg(&stream->oa->xe->drm, "changed to oa config uuid=%s\n", stream->oa_config->uuid); + } else { + while (param.num_syncs--) + xe_sync_entry_cleanup(¶m.syncs[param.num_syncs]); + kfree(param.syncs); } err_config_put: -- cgit v1.2.3 From f110dbbfa2a94c91704bf19806907a98fd73ca14 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 13 Jul 2026 16:30:53 +0800 Subject: drm/xe/oa: Check managed mutex initialization errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drmm_mutex_init() can fail while registering its managed cleanup action. On failure, the reset path destroys the mutex, so continuing OA setup leaves an unusable lock that later paths may acquire. Return the error from per-GT OA initialization and abort device-wide OA initialization if the metrics lock cannot be initialized. Fixes: a9f905ae7b6f ("drm/xe/oa/uapi: Initialize OA units") Fixes: cdf02fe1a94a ("drm/xe/oa/uapi: Add/remove OA config perf ops") Signed-off-by: Linmao Li Reviewed-by: Umesh Nerlige Ramappa Signed-off-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260713083053.321091-1-lilinmao@kylinos.cn (cherry picked from commit 360b293de27bfdd0d07047f8efd5ba8e91fa90b7) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_oa.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 9cf962b71cb6..18d990c5d4ec 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -2717,9 +2717,7 @@ static int xe_oa_init_gt(struct xe_gt *gt) __xe_oa_init_oa_units(gt); - drmm_mutex_init(>_to_xe(gt)->drm, >->oa.gt_lock); - - return 0; + return drmm_mutex_init(>_to_xe(gt)->drm, >->oa.gt_lock); } static void xe_oa_print_gt_oa_units(struct xe_gt *gt) @@ -2859,7 +2857,10 @@ int xe_oa_init(struct xe_device *xe) oa->xe = xe; oa->oa_formats = oa_formats; - drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock); + ret = drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock); + if (ret) + goto exit; + idr_init_base(&oa->metrics_idr, 1); ret = xe_oa_init_oa_units(oa); -- cgit v1.2.3 From 5cf82c8cec90056511eb881a267aab6101eaf57a Mon Sep 17 00:00:00 2001 From: Vinay Belgaumkar Date: Wed, 5 Aug 2026 16:46:49 -0700 Subject: drm/xe: Fix a bug in pc_adjust_freq_bounds() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cases where min frequency was actually greater than BMG_MIN_FREQ, we were not using the updated min frequency as there was a missing call to pc_action_query_task_state() between the two settings of min frequency. Since we know what min_freq was last set, use that cached value while comparing to BMG_MIN_FREQ to fix this issue. v2: pc->freq_ready is not set until after pc_adjust_freq_bounds(). Stay with pc_action_query_task_state() instead. v3: Update commit message (Stuart) Fixes: bdde16c9ac5c ("drm/xe/bmg: Update Wa_14022085890") Signed-off-by: Balasubramani Vivekanandan Signed-off-by: Vinay Belgaumkar Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260805234649.2076384-1-vinay.belgaumkar@intel.com (cherry picked from commit a2c2d2b13a9ea9494d2d76b46273833111749507) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_pc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c index 59f2fa79ad42..7cf8f4858598 100644 --- a/drivers/gpu/drm/xe/xe_guc_pc.c +++ b/drivers/gpu/drm/xe/xe_guc_pc.c @@ -911,6 +911,7 @@ static bool pc_needs_min_freq_change(struct xe_guc_pc *pc) static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) { int ret; + u32 min_freq; lockdep_assert_held(&pc->freq_lock); @@ -933,8 +934,14 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) * Same thing happens for Server platforms where min is listed as * RPMax */ - if (pc_get_min_freq(pc) > pc->rp0_freq) + min_freq = pc_get_min_freq(pc); + if (min_freq > pc->rp0_freq) { ret = pc_set_min_freq(pc, pc->rp0_freq); + if (ret) + goto out; + + min_freq = pc->rp0_freq; + } /* * Setting GT RP min frequency to 1.2GHz by default for @@ -947,8 +954,8 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) * we aren't expecting high power output across board * */ - if (pc_needs_min_freq_change(pc)) - ret = pc_set_min_freq(pc, max(BMG_MIN_FREQ, pc_get_min_freq(pc))); + if (pc_needs_min_freq_change(pc) && min_freq < BMG_MIN_FREQ) + ret = pc_set_min_freq(pc, BMG_MIN_FREQ); out: return ret; -- cgit v1.2.3 From ef526d122b62af5afa437f095aa6661a953676c4 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Mon, 10 Aug 2026 18:08:20 +0530 Subject: drm/xe: Fix xe_device_probe() failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, xe_device_probe() jumps to err_unregister_display label in case of failure except for its last call, which directly returns the error without required cleanup handling. This results in stale drm device that isn't cleaned up on unwind. Fix it. [ 810.194180] sysfs: cannot create duplicate filename '/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:01.0/0000:03:00.0/drm/renderD128' [ 810.194183] CPU: 9 UID: 0 PID: 5616 Comm: modprobe Kdump: loaded Tainted: G S U E 7.2.0-rc2-xe #382 PREEMPT(full) [ 810.194185] Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER, [E]=UNSIGNED_MODULE [ 810.194186] Hardware name: ASUS System Product Name/PRIME Z790-P WIFI, BIOS 1805 10/30/2024 [ 810.194186] Call Trace: [ 810.194187] [ 810.194188] dump_stack_lvl+0xe0/0x100 [ 810.194195] dump_stack+0x14/0x20 [ 810.194197] sysfs_warn_dup+0x5f/0x80 [ 810.194204] sysfs_create_dir_ns+0xbe/0xd0 [ 810.194210] kobject_add_internal+0xbc/0x2b0 [ 810.194215] kobject_add+0x7c/0xe0 [ 810.194220] ? get_device_parent+0xcf/0x1e0 [ 810.194227] device_add+0xe3/0x870 [ 810.194231] ? __pfx_drm_gem_name_info+0x10/0x10 [drm] [ 810.194280] drm_minor_register+0x73/0x130 [drm] [ 810.194322] drm_dev_register+0x76/0x2a0 [drm] Cc: stable@vger.kernel.org Fixes: da3799c97572 ("drm/xe: Use GuC to do GGTT invalidations for the GuC firmware") Signed-off-by: Raag Jadav Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260810123821.105605-1-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit 5ce3042c67c539480882567137ff8d56118885d6) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index dcb48caa485d..a2b076b1dc60 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1102,7 +1102,11 @@ int xe_device_probe(struct xe_device *xe) if (err) goto err_unregister_display; - return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); + err = devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); + if (err) + goto err_unregister_display; + + return 0; err_unregister_display: xe_display_unregister(xe); -- cgit v1.2.3 From 921ac6cb066d09b5765db892d0db0ffaffa98767 Mon Sep 17 00:00:00 2001 From: Shixiong Ou Date: Thu, 30 Jul 2026 09:44:40 +0800 Subject: drm/log: Fix division by zero when scale module parameter is 0 The scale module parameter can be set to 0 via kernel command line. When scale is 0, scaled_font_h and scaled_font_w become 0, causing a division by zero in the rows/columns calculation. Since the scale module parameter is read-only (0444 permissions), it cannot be changed at runtime via sysfs. Clamp it to 1 once in drm_log_register(). Fixes: 8a4b913df427 ("drm/log: Add integer scaling support") Signed-off-by: Shixiong Ou Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/20260730014440.66323-1-oushixiong1025@163.com Signed-off-by: Jocelyn Falempe --- drivers/gpu/drm/clients/drm_log.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c index 8d21b785bead..a3259b8f2333 100644 --- a/drivers/gpu/drm/clients/drm_log.c +++ b/drivers/gpu/drm/clients/drm_log.c @@ -419,6 +419,9 @@ void drm_log_register(struct drm_device *dev) { struct drm_log *new; + if (!scale) + scale = 1; + new = kzalloc_obj(*new); if (!new) goto err_warn; -- cgit v1.2.3 From 60baa179ed1333535f6e2da4133511db55278ee4 Mon Sep 17 00:00:00 2001 From: Shixiong Ou Date: Wed, 29 Jul 2026 16:45:17 +0800 Subject: drm/log: Fix out-of-bounds read on empty message length drm_log_draw_kmsg_record() accesses s[len - 1] to strip the trailing newline, but len is unsigned int. If len is 0, the subtraction wraps to UINT_MAX, causing an out-of-bounds read. Add an early return when len is 0. Fixes: 25e2c2a3eff5 ("drm/log: Color the timestamp, to improve readability") Signed-off-by: Shixiong Ou Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/20260729084520.688087-1-oushixiong1025@163.com Signed-off-by: Jocelyn Falempe --- drivers/gpu/drm/clients/drm_log.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c index a3259b8f2333..f23a92f826c9 100644 --- a/drivers/gpu/drm/clients/drm_log.c +++ b/drivers/gpu/drm/clients/drm_log.c @@ -160,6 +160,9 @@ static void drm_log_draw_kmsg_record(struct drm_log_scanout *scanout, { u32 prefix_len = 0; + if (!len) + return; + if (len > TS_PREFIX_LEN && s[0] == '[' && s[6] == '.' && s[TS_PREFIX_LEN] == ']') prefix_len = TS_PREFIX_LEN + 1; -- cgit v1.2.3 From f4f2bba28df9b9aaa00262a462139dbbcdc38d9f Mon Sep 17 00:00:00 2001 From: Shixiong Ou Date: Wed, 29 Jul 2026 16:48:15 +0800 Subject: drm/log: Fix infinite loop when scale is too large for display When scale is large enough that scaled_font exceeds the display dimensions, rows or columns become 0. A columns value of 0 causes an infinite loop in drm_log_draw_kmsg_record() because the loop never decrements len. Check for zero rows/columns in drm_log_setup_modeset() and return an error, cleaning up the already allocated buffer to avoid a leak. Fixes: 8a4b913df427 ("drm/log: Add integer scaling support") Signed-off-by: Shixiong Ou Reviewed-by: Jocelyn Falempe Link: https://patch.msgid.link/20260729084815.692944-1-oushixiong1025@163.com Signed-off-by: Jocelyn Falempe --- drivers/gpu/drm/clients/drm_log.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c index f23a92f826c9..467cdce57dd3 100644 --- a/drivers/gpu/drm/clients/drm_log.c +++ b/drivers/gpu/drm/clients/drm_log.c @@ -218,6 +218,12 @@ static int drm_log_setup_modeset(struct drm_client_dev *client, scanout->scaled_font_w = scanout->font->width * scale; scanout->rows = height / scanout->scaled_font_h; scanout->columns = width / scanout->scaled_font_w; + if (!scanout->rows || !scanout->columns) { + drm_client_buffer_delete(scanout->buffer); + scanout->buffer = NULL; + mode_set->fb = NULL; + return -EINVAL; + } scanout->front_color = drm_draw_color_from_xrgb8888(0xffffff, format); scanout->prefix_color = drm_draw_color_from_xrgb8888(0x4e9a06, format); return 0; -- cgit v1.2.3 From 3aa1dcaa4f6f5ae08936491e08bd456f331f2d40 Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Tue, 4 Aug 2026 17:00:04 +0500 Subject: Revert "wifi: mt76: Disable napi when removing device" This reverts commit 13b7e6a96a005c656d38f3da51581deaf9866375. That commit made mt76_dma_cleanup() disable every RX NAPI instance before deleting it, to silence WARNs in __netif_napi_del_locked() and page_pool_disable_direct_recycling() seen when unloading mt7915e with an MT7916. On mt7921e and mt7925e the same instances are already disabled earlier, in mt7921e_unregister_device() and mt7925e_unregister_device(), which only afterwards call mt792x_dma_cleanup() -> mt76_dma_cleanup(). Each instance is therefore disabled twice, and napi_disable() is not idempotent: on return it leaves NAPIF_STATE_SCHED and NAPIF_STATE_NPSVC set, so the second call spins in usleep_range() forever, waiting for bits that nobody will clear. mt7921_pci_shutdown() and mt7925_pci_shutdown() reuse the remove path, so this is hit on every reboot, poweroff and module unload. It is silent: the stuck task keeps sleeping and rescheduling, so neither the hung task detector nor the lockup detectors fire, and the last line on the console is "systemd-shutdown[1]: Rebooting." task:modprobe state:D stack:25720 pid:7954 tgid:7954 Call Trace: __schedule+0x11b8/0x26d0 schedule+0xe7/0x2f0 schedule_hrtimeout_range_clock+0x218/0x330 usleep_range_state+0x133/0x1b0 napi_disable_locked+0x37d/0x5f0 napi_disable+0x43/0x80 mt76_dma_cleanup+0x2b4/0x860 [mt76] mt7921_pci_remove+0x17f/0x350 [mt7921e] pci_device_remove+0xb6/0x1e0 device_release_driver_internal+0x38d/0x540 driver_detach+0xd0/0x1b0 bus_remove_driver+0x127/0x2d0 pci_unregister_driver+0x2a/0x280 __do_sys_delete_module+0x36a/0x5b0 do_syscall_64+0x11c/0x6d0 entry_SYSCALL_64_after_hwframe+0x76/0x7e Dropping the two driver-side loops instead was tried and rejected: with them gone, the RX poll can reach mt76_token_release() via PKT_TYPE_TXRX_NOTIFY and mt7921_mac_tx_free() while mt76_connac2_tx_token_put() is running idr_destroy(&dev->token) outside token_lock, which is a use-after-free rather than a hang [1]. Revert for now, so that reboot, poweroff and module unload work again. The WARNs on mt7915e are a less severe problem than an unbootable machine, and fixing them belongs in the drivers that delete the NAPI instances, where each one can pick a point that is safe for its own teardown order, rather than in the shared mt76_dma_cleanup(). [ This is the "landing soonish" known regression fix mentioned in the previous networking merge commit - Linus ] Reported-by: Bert Karwatzki Closes: https://lore.kernel.org/all/20260724151419.26014-1-spasswolf@web.de/ Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221818 Link: https://lore.kernel.org/all/20260730050428.GA73812@sol/ [1] Signed-off-by: Mikhail Gavrilov Acked-by: Nicolas Cavallari Fixes: 13b7e6a96a00 ("wifi: mt76: Disable napi when removing device") Tested-by: Devin Wittmayer Signed-off-by: Linus Torvalds --- drivers/net/wireless/mediatek/mt76/dma.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 322041859217..f8c2fe5f2f58 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -1189,10 +1189,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev) mt76_for_each_q_rx(dev, i) { struct mt76_queue *q = &dev->q_rx[i]; - if (!mt76_queue_is_wed_rro(q)) { - napi_disable(&dev->napi[i]); - netif_napi_del(&dev->napi[i]); - } + netif_napi_del(&dev->napi[i]); mt76_dma_rx_cleanup(dev, q); page_pool_destroy(q->page_pool); -- cgit v1.2.3 From 11058bd3d47d57eb3473935feae53868d6d168b7 Mon Sep 17 00:00:00 2001 From: Jasper Wise Date: Thu, 13 Aug 2026 08:46:18 +0000 Subject: spi: virtio: mark device ready before registering the controller virtio_spi_probe() registers the SPI controller with devm_spi_register_controller(). spi_register_controller() binds a child inline unless its driver has asked for asynchronous probing, so a peripheral that performs a transfer during its own probe reaches virtio_spi_transfer_one(), which kicks the virtqueue before probe has returned. The driver never calls virtio_device_ready(), so DRIVER_OK is set on its behalf by virtio_dev_probe(), only once probe has returned. The virtio spec is explicit about that ordering in 3.1 Device Initialization: | The driver MUST NOT send any buffer available notifications to the | device before setting DRIVER_OK. A device that waits for DRIVER_OK before servicing the queue therefore leaves the transfer unanswered, and virtio_spi_transfer_one() waits for its completion with no timeout, so probe never returns. Mark the device ready before registering the controller, as done for the same reason in commit f5866db64f34 ("virtio_console: enable VQs early") and commit 1d774589f924 ("i2c: virtio: mark device ready before registering the adapter"). Fixes: f98cabe3f6cf ("SPI: Add virtio SPI driver") Signed-off-by: Jasper Wise Link: https://patch.msgid.link/20260813084618.613172-1-jaspwise@amazon.co.uk Signed-off-by: Mark Brown --- drivers/spi/spi-virtio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-virtio.c b/drivers/spi/spi-virtio.c index fa4c68049eca..2256dfec5407 100644 --- a/drivers/spi/spi-virtio.c +++ b/drivers/spi/spi-virtio.c @@ -358,6 +358,8 @@ static int virtio_spi_probe(struct virtio_device *vdev) if (ret) return dev_err_probe(&vdev->dev, ret, "Cannot register virtqueue cleanup\n"); + virtio_device_ready(vdev); + /* Use devm version to register controller */ ret = devm_spi_register_controller(&vdev->dev, ctrl); if (ret) -- cgit v1.2.3 From b69859204d4db3acd86c1c2dadcef0d77b451933 Mon Sep 17 00:00:00 2001 From: Hui Su Date: Wed, 12 Aug 2026 01:39:03 +0800 Subject: tracing: Fix NULL pointer dereference in module event cache removal A module-only event filter such as ":mod:foo" is cached with a NULL event_mod->match when foo has not been loaded. If a later write tries to remove a specific match from the same module, remove_cache_mod() passes the NULL cached match to strcmp(), causing a NULL pointer dereference. The issue can be reproduced from userspace: echo ':mod:trace_events_kunit_missing' > /sys/kernel/tracing/set_event echo '!foo_bar:mod:trace_events_kunit_missing' >> /sys/kernel/tracing/set_event The second write must be a concatenation (">>") to not include O_TRUNC as that would cause ftrace_clear_events() to clear the cached modules lines. The crash was reproduced on x86_64 QEMU while KUnit workers contended on the event tracing path: BUG: kernel NULL pointer dereference, address: 0000000000000000 #PF: supervisor read access in kernel mode RIP: 0010:strcmp+0x10/0x30 Call Trace: __ftrace_set_clr_event_nolock+0x373/0x4a0 ftrace_set_clr_event+0xf0/0x180 ftrace_event_write+0xdf/0x110 vfs_write+0xf6/0x440 ksys_write+0x68/0xe0 do_syscall_64+0xf9/0x540 entry_SYSCALL_64_after_hwframe+0x77/0x7f Check event_mod->match before comparing it, consistent with the existing NULL checks for the cached system and event fields. The mismatched removal continues to return -EINVAL; a broad cached module filter is removed with "!:mod:". Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260811173902.1927376-2-sh_def@163.com Fixes: b355247df104 ("tracing: Cache \":mod:\" events for modules not loaded yet") Reported-by: syzbot+4d3143c8e28f6266c636@syzkaller.appspotmail.com Closes: https://lore.kernel.org/lkml/6a7a6b7f.9c11d2ce.289b96.00f8.GAE@google.com/ Signed-off-by: Hui Su Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index c01b10b99f67..032f741ba616 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -945,7 +945,7 @@ static int remove_cache_mod(struct trace_array *tr, const char *mod, if (strcmp(event_mod->module, mod) != 0) continue; - if (match && strcmp(event_mod->match, match) != 0) + if (match && (!event_mod->match || strcmp(event_mod->match, match) != 0)) continue; if (system && -- cgit v1.2.3 From c3730b8373bb5059d735509b9e6a00d7eb337d7c Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Mon, 10 Aug 2026 14:32:30 +0800 Subject: tracing: Fix race between update_event_fields and, event_define_fields The following sequence may leads race between event_define_fields() and update_event_fields(): CPU0 (loads module A) CPU1 (loads module B) =============================== =============================== load_module(A) load_module(B) notifier_call_chain notifier_call_chain trace_module_notify trace_module_notify mutex_lock(&event_mutex) trace_event_update_all() trace_module_add_events(A) down_write(&trace_event_sem) __register_event(call_A) __add_event_to_tracers(call_A) event_define_fields(call_A) for each f: list_for_each_entry(field, list_add(&f->link, &class->fields, link) &class->fields) field = class->fields->next; Where access to the class->fields is not protected by the event_mutex in trace_event_update_all(). This produces the following panic: Unable to handle kernel access ... at virtual address 0000000000000018 pc : update_event_fields+0xf8/0x368 Call trace: update_event_fields+0xf8/0x368 trace_event_update_all+0x7c/0x2b4 trace_module_notify+0x4c/0x1dc notifier_call_chain+0x84/0x168 blocking_notifier_call_chain_robust+0x64/0xd4 load_module+0x10c8/0x123c __arm64_sys_finit_module+0x230/0x31c Fix by taking event_mutex in trace_event_update_all() before trace_event_sem. Cc: stable@vger.kernel.org Fixes: b3bc8547d3be ("tracing: Have TRACE_DEFINE_ENUM affect trace event types as well") Link: https://patch.msgid.link/2e5730d2-c631-da41-3a3a-ae35bb4895f3@allwinnertech.com Signed-off-by: Michael Wu Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 032f741ba616..3650d84d4f16 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -3566,6 +3566,7 @@ void trace_event_update_all(struct trace_eval_map **map, int len) int last_i; int i; + mutex_lock(&event_mutex); down_write(&trace_event_sem); list_for_each_entry_safe(call, p, &ftrace_events, list) { /* events are usually grouped together with systems */ @@ -3604,6 +3605,7 @@ void trace_event_update_all(struct trace_eval_map **map, int len) cond_resched(); } up_write(&trace_event_sem); + mutex_unlock(&event_mutex); } static bool event_in_systems(struct trace_event_call *call, -- cgit v1.2.3 From 333238da9a193ffc58792995f3e951e4cb87bfd2 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Fri, 14 Aug 2026 15:52:41 +0200 Subject: sched: Update time before requeueing delayed entities In order to compute the right lag, it is required to update time to 'now'. Without this, the delayed entity might appear younger than it really is and receive less compensation for having waited. Signed-off-by: Vincent Guittot Signed-off-by: Peter Zijlstra (Intel) --- kernel/sched/fair.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d78467ec6ee1..f15f5764818e 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7779,6 +7779,8 @@ requeue_delayed_entity(struct sched_entity *se) WARN_ON_ONCE(!se->sched_delayed); WARN_ON_ONCE(!se->on_rq); + update_curr(cfs_rq); + if (update_entity_lag(cfs_rq, se)) { cfs_rq->nr_queued--; if (se != cfs_rq->curr) -- cgit v1.2.3 From a64d500b0078e16e9abb25baca4dee1dbc9054fc Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 12 Aug 2026 21:11:43 +0800 Subject: erofs: fix EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS on some UP platforms CONFIG_NR_CPUS doesn't define on some UP platforms (e.g. arm), so this can cause make oldconfig to loop indefinitely when CONFIG_SMP=n: $ make ARCH=arm allmodconfig $ sed -i "/CONFIG_SMP=y/d" .config $ sed -i "/CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS.*/d" .config EROFS LZMA default maximum decompression streams (EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS) [0] (NEW) EROFS LZMA default maximum decompression streams (EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS) [0] (NEW) ... Let's guard NR_CPUS with SMP instead of using a hardcoded arbitrary CPU uplimit here, similar to commit a3344078101c ("mm: make SPLIT_PTE_PTLOCKS depend on SMP"). The initial report from SJ Park was for m68k [1] (m68k is the only arch without NR_CPUS in Kconfig), and that got fixed in commit 1fd495ef09ee ("m68k: Define NR_CPUS to 1") Reported-by: SJ Park Link: https://lore.kernel.org/all/anuyFHLUGDjZWY4K@XiangdeMacBook-Pro.local/T/#u [1] Closes: https://lore.kernel.org/r/20260728065447.91511-1-sj@kernel.org Reported-by: Guenter Roeck Closes: https://lore.kernel.org/r/87853c96-cc8f-49e6-81b1-02bfe409e372@roeck-us.net Fixes: c9b47e6b2311 ("erofs: cap LZMA stream pool size") Signed-off-by: Gao Xiang Tested-by: SJ Park Tested-by: Geert Uytterhoeven Signed-off-by: Linus Torvalds --- fs/erofs/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index 8ca1767dafb6..2dfc313588d2 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -134,7 +134,8 @@ config EROFS_FS_ZIP_LZMA config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS int "EROFS LZMA default maximum decompression streams" depends on EROFS_FS_ZIP_LZMA - range 1 NR_CPUS + range 1 NR_CPUS if SMP + range 1 1 if !SMP default 16 help By default EROFS allocates one LZMA decompression stream per CPU. -- cgit v1.2.3 From 32ef1b30ad736519f7a207bcc2986f3d4129d972 Mon Sep 17 00:00:00 2001 From: Ali Ahmet Memis Date: Fri, 7 Aug 2026 23:42:30 +0000 Subject: openrisc: signal: do not restore privileged SR bits on sigreturn restore_sigcontext() copies the whole supervision register (SR) from the signal frame and only clears SPR_SR_SM before the value is reloaded into the hardware SR (through ESR and l.rfe) on the return to user space. All other SR bits are left under user control. An unprivileged task can thus return from a signal handler through a crafted sigframe that clears SPR_SR_DME. With the data MMU disabled the CPU performs no translation or protection on data accesses, so the task gains read and write access to arbitrary physical memory, a local privilege escalation. SPR_SR_IME, SPR_SR_SUMRA, SPR_SR_LEE, SPR_SR_EPH and the cache-enable bits are exposed the same way. The ptrace GPR regset already refuses any change to SR for exactly this reason. Restore only the arithmetic flag bits (F, CY, OV) from the signal frame and take every privileged control bit from the SR the kernel saved on signal entry. Verified with qemu-system-or1k -M or1k-sim: before this change an unprivileged PoC clears SPR_SR_DME in rt_sigreturn and writes a marker to physical address 0x03000000 (beyond the kernel's mem=32M); afterwards the same PoC receives SIGSEGV and physical memory is unchanged. Fixes: ac689eb7f9d4 ("OpenRISC: Signal handling") Cc: stable@vger.kernel.org Signed-off-by: Ali Ahmet Memis Signed-off-by: Stafford Horne --- arch/openrisc/include/asm/processor.h | 2 ++ arch/openrisc/kernel/signal.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h index 3ff893a67c13..ae43fe79b2b5 100644 --- a/arch/openrisc/include/asm/processor.h +++ b/arch/openrisc/include/asm/processor.h @@ -26,6 +26,8 @@ | SPR_SR_DCE | SPR_SR_SM) #define USER_SR (SPR_SR_DME | SPR_SR_IME | SPR_SR_ICE \ | SPR_SR_DCE | SPR_SR_IEE | SPR_SR_TEE) +/* SR bits user space may change via sigreturn, the rest stay kernel owned */ +#define SPR_SR_USER_MASK (SPR_SR_F | SPR_SR_CY | SPR_SR_OV) /* * User space process size. This is hardcoded into a few places, diff --git a/arch/openrisc/kernel/signal.c b/arch/openrisc/kernel/signal.c index f70a13ee0593..2be5af3b9832 100644 --- a/arch/openrisc/kernel/signal.c +++ b/arch/openrisc/kernel/signal.c @@ -74,6 +74,7 @@ static long save_fp_state(struct sigcontext __user *sc) static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { + unsigned long old_sr = regs->sr; int err = 0; /* Always make any pending restarted system calls return -EINTR */ @@ -89,8 +90,8 @@ static int restore_sigcontext(struct pt_regs *regs, err |= __copy_from_user(®s->sr, &sc->regs.sr, sizeof(unsigned long)); err |= restore_fp_state(sc); - /* make sure the SM-bit is cleared so user-mode cannot fool us */ - regs->sr &= ~SPR_SR_SM; + /* keep the privileged SR bits kernel owned, restore only user flags */ + regs->sr = (old_sr & ~SPR_SR_USER_MASK) | (regs->sr & SPR_SR_USER_MASK); regs->orig_gpr11 = -1; /* Avoid syscall restart checks */ -- cgit v1.2.3 From c71bf113dfdf426bdaf106636f573ef87b6613a0 Mon Sep 17 00:00:00 2001 From: Christoph Böhmwalder Date: Fri, 14 Aug 2026 17:16:17 +0200 Subject: drbd: don't leak the shared secret to unprivileged netlink dumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversion to explicit netlink serialization dropped the exclude_sensitive parameter from net_conf_to_skb(), so each caller has to sanitize by hand. Two dump paths were missed: drbd_nl_get_connections_dumpit() and the volume-less connection branch of get_one_status(). Neither op carries GENL_ADMIN_PERM, so any unprivileged local user could read the CRAM-HMAC secret. Add a net_conf_to_skb_sanitized() wrapper and route all three callers through it. Fixes: 8098eeb693c4 ("drbd: replace genl_magic with explicit netlink serialization") Reported-by: Vivek Parikh Signed-off-by: Christoph Böhmwalder Link: https://patch.msgid.link/20260814151617.73752-1-christoph.boehmwalder@linbit.com Signed-off-by: Jens Axboe --- drivers/block/drbd/drbd_nl.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index f9ffcd67607b..b77f901fc3ef 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -3306,6 +3306,26 @@ nla_put_failure: return -EMSGSIZE; } +/* + * net_conf_to_skb() serializes the shared secret verbatim. Any path that can + * answer a request from an unprivileged process must pass exclude_sensitive, + * so the secret is blanked in a private copy before it reaches the skb. + */ +static int net_conf_to_skb_sanitized(struct sk_buff *skb, struct net_conf *nc, + bool exclude_sensitive) +{ + struct net_conf nc_clean; + + if (!exclude_sensitive) + return net_conf_to_skb(skb, nc); + + nc_clean = *nc; + memset(nc_clean.shared_secret, 0, sizeof(nc_clean.shared_secret)); + nc_clean.shared_secret_len = 0; + + return net_conf_to_skb(skb, &nc_clean); +} + /* * The generic netlink dump callbacks are called outside the genl_lock(), so * they cannot use the simple attribute parsing code which uses global @@ -3621,7 +3641,8 @@ put_result: goto out; net_conf = rcu_dereference(connection->net_conf); if (net_conf) { - err = net_conf_to_skb(skb, net_conf); + err = net_conf_to_skb_sanitized(skb, net_conf, + !capable(CAP_SYS_ADMIN)); if (err) goto out; } @@ -3842,18 +3863,8 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device, struct net_conf *nc; nc = rcu_dereference(first_peer_device(device)->connection->net_conf); - if (nc) { - if (exclude_sensitive) { - struct net_conf nc_clean = *nc; - - memset(nc_clean.shared_secret, 0, - sizeof(nc_clean.shared_secret)); - nc_clean.shared_secret_len = 0; - err = net_conf_to_skb(skb, &nc_clean); - } else { - err = net_conf_to_skb(skb, nc); - } - } + if (nc) + err = net_conf_to_skb_sanitized(skb, nc, exclude_sensitive); } rcu_read_unlock(); if (err) @@ -4058,7 +4069,7 @@ next_resource: struct net_conf *nc; nc = rcu_dereference(connection->net_conf); - if (nc && net_conf_to_skb(skb, nc) != 0) + if (nc && net_conf_to_skb_sanitized(skb, nc, true) != 0) goto cancel; } goto done; -- cgit v1.2.3 From d5b95e612cde33f9def1a7a6c3242d03d3bbde3a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 16 Aug 2026 10:40:14 -0700 Subject: Revert "i2c: designware: defer probe if child GpioInt controllers are not bound" This reverts commit 0a4bb2abc3e56d7be6e69b050c88ba52c87e22bf. This was reported to break the touchpad on at least some Thinkpads, and while the revert has hit the i2c tree, it hasn't hit mine. So I'm reverting it directly just to have this resolved for the imminent 7.2 release. Reported-by: Thorsten Leemhuis Link: https://lore.kernel.org/all/b4a4eadb-282f-464c-843a-19d415a34d0c@leemhuis.info/ Cc: Mario Limonciello CC: Hardik Prakash Signed-off-by: Linus Torvalds --- drivers/i2c/busses/i2c-designware-platdrv.c | 80 ----------------------------- 1 file changed, 80 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index c8a203fff4d1..6d6e81242f74 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -8,14 +8,12 @@ * Copyright (C) 2007 MontaVista Software Inc. * Copyright (C) 2009 Provigent Ltd. */ -#include #include #include #include #include #include #include -#include #include #include #include @@ -132,80 +130,6 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) return 0; } -#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB) -/* - * Check whether an ACPI GpioInt resource's referenced GPIO controller - * has finished probing. Resources with no named controller (resource - * source string) are skipped, since they can't be resolved to a - * struct device. - */ -static int check_gpioint_resource(struct acpi_resource *ares, void *data) -{ - struct acpi_resource_gpio *agpio; - struct acpi_device *gpio_adev; - struct device *gpio_dev; - acpi_handle handle; - acpi_status status; - - if (!acpi_gpio_get_irq_resource(ares, &agpio)) - return 1; /* not a GpioInt resource, skip */ - - if (!agpio->resource_source.string_length) - return 1; /* no named controller, skip */ - - status = acpi_get_handle(NULL, agpio->resource_source.string_ptr, &handle); - if (ACPI_FAILURE(status)) - return 1; - - gpio_adev = acpi_fetch_acpi_dev(handle); - if (!gpio_adev) - return 1; - - struct gpio_device *gdev __free(gpio_device_put) = - gpio_device_find_by_fwnode(acpi_fwnode_handle(gpio_adev)); - if (!gdev) - return -EPROBE_DEFER; /* controller not registered yet: abort walk */ - - gpio_dev = gpio_device_to_device(gdev)->parent; - - guard(device)(gpio_dev); - if (!device_is_bound(gpio_dev)) - return -EPROBE_DEFER; /* controller not bound yet: abort walk */ - - return 1; /* bound, skip adding to resource list, continue walk */ -} - -static int check_child_gpioint(struct acpi_device *adev, void *data) -{ - LIST_HEAD(res_list); - int ret; - - ret = acpi_dev_get_resources(adev, &res_list, check_gpioint_resource, NULL); - if (ret < 0) - return ret; - - acpi_dev_free_resource_list(&res_list); - - return 0; -} - -static int i2c_dw_check_gpio_dependencies(struct device *dev) -{ - struct acpi_device *adev; - - adev = ACPI_COMPANION(dev); - if (!adev) - return 0; - - return acpi_dev_for_each_child(adev, check_child_gpioint, NULL); -} -#else -static int i2c_dw_check_gpio_dependencies(struct device *dev) -{ - return 0; -} -#endif /* CONFIG_ACPI && CONFIG_GPIOLIB */ - static int dw_i2c_plat_probe(struct platform_device *pdev) { u32 flags = (uintptr_t)device_get_match_data(&pdev->dev); @@ -214,10 +138,6 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) struct dw_i2c_dev *dev; int irq, ret; - ret = i2c_dw_check_gpio_dependencies(device); - if (ret) - return ret; - irq = platform_get_irq_optional(pdev, 0); if (irq == -ENXIO) flags |= ACCESS_POLLING; -- cgit v1.2.3 From d6e7d57ed967def9c964a58328ffba409f7efb64 Mon Sep 17 00:00:00 2001 From: Charlie-cy Wu Date: Mon, 29 Jun 2026 16:35:43 +0800 Subject: wifi: mt76: mt7921: refactor regd update to fix recursive mutex deadlock Split mt7921_mcu_regd_update() into two functions to prevent recursive mutex acquisition. Introduce __mt7921_mcu_regd_update() as the internal implementation that assumes the mutex is already held by the caller, while mt7921_mcu_regd_update() remains as the external interface that handles mutex acquisition and release. This fixes a deadlock issue when mt7921_regd_set_6ghz_power_type() is called with the device mutex already held. Without this change, calling mt7921_mcu_regd_update() would attempt to acquire the same mutex again, causing a recursive lock deadlock. The __mt7921_mcu_regd_update() function can be safely called when the caller has already acquired the device mutex, avoiding the deadlock while maintaining proper synchronization for regulatory domain updates. Fixes: dc2608cf5224 ("wifi: mt76: mt7921: refactor regulatory notifier flow") Signed-off-by: Charlie-cy Wu Tested-by: Mikhail Gavrilov Tested-by: Devin Wittmayer Signed-off-by: Linus Torvalds --- drivers/net/wireless/mediatek/mt76/mt7921/main.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7921/regd.c | 30 +++++++++++++++--------- drivers/net/wireless/mediatek/mt76/mt7921/regd.h | 2 ++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 3480205d5fb9..68a059504e83 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -802,7 +802,7 @@ mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add) out: if (vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) - mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); + __mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); } int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index c0e2b48a50bf..4722007abafd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -71,36 +71,44 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) } } -int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, - enum environment_cap country_ie_env) +/* Internal version that assumes mutex is already held by caller */ +int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) { struct mt76_dev *mdev = &dev->mt76; struct ieee80211_hw *hw = mdev->hw; struct wiphy *wiphy = hw->wiphy; int ret = 0; - dev->regd_in_progress = true; - - mt792x_mutex_acquire(dev); if (!dev->regd_change) - goto err; + return 0; ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env); if (ret < 0) - goto err; + return ret; mt7921_regd_channel_update(wiphy, dev); ret = mt76_connac_mcu_set_channel_domain(hw->priv); if (ret < 0) - goto err; + return ret; ret = mt7921_set_tx_sar_pwr(hw, NULL); - if (ret < 0) - goto err; -err: + return ret; +} + +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) +{ + int ret = 0; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + ret = __mt7921_mcu_regd_update(dev, alpha2, country_ie_env); mt792x_mutex_release(dev); + dev->regd_change = false; dev->regd_in_progress = false; wake_up(&dev->wait); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h index 571f31629e9e..5b24d0902c36 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -10,6 +10,8 @@ struct regulatory_request; int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap country_ie_env); +int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env); void mt7921_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); bool mt7921_regd_clc_supported(struct mt792x_dev *dev); -- cgit v1.2.3 From 8d3ae59288f1e7d58d76558a6ee96d533bc5019f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 16 Aug 2026 14:32:26 -0700 Subject: Linux 7.2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0e6fc0803775..ddcc6dca1ece 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc7 +EXTRAVERSION = NAME = Baby Opossum Posse # *DOCUMENTATION* -- cgit v1.2.3