From: Greg Kroah-Hartman Date: Thu, 16 Jul 2026 11:45:32 +0000 (+0200) Subject: 7.1-stable patches X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fc1cd8497c3b7eff72c5caf6d94ee2ce4acc0477;p=thirdparty%2Fkernel%2Fstable-queue.git 7.1-stable patches added patches: audit-fix-removal-of-dangling-executable-rules.patch dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch iommu-vt-d-avoid-warning-in-sva-unbind-path.patch iommufd-avoid-partial-fault-group-delivery-in-iommufd_fault_fops_read.patch iommufd-break-the-loop-on-failure-in-iommufd_fault_fops_read.patch iommufd-fix-data_len-byte-count-vs-element-count-mismatch.patch iommufd-move-vevent-memory-allocation-outside-spinlock.patch iommufd-propagate-allocation-failure-in-iommufd_veventq_deliver_fetch.patch iommufd-reject-invalid-read-count-in-iommufd_fault_fops_read.patch iommufd-reject-invalid-read-count-in-iommufd_veventq_fops_read.patch iommufd-rewind-header-length-in-done-if-iommufd_veventq_fops_read-fails.patch iommufd-set-upper-bounds-on-cache-invalidation-entry_num-and-entry_len.patch iommufd-set-veventq_depth-upper-bound.patch iommufd-use-sizeof-hdr-instead-of-sizeof-hdr-in-veventq-read.patch kvm-arm64-bound-used_lrs-when-flushing-the-pkvm-hyp-vcpu.patch kvm-arm64-clear-__hyp_running_vcpu-when-flushing-the-pkvm-hyp-vcpu.patch kvm-arm64-don-t-leak-pfn-when-kvm_translate_vncr-races-mmu-notifier.patch kvm-arm64-nv-avoid-dereferencing-null-vncr-pseudo-tlb.patch kvm-guest_memfd-treat-memslot-binding-offset-size-as-unsigned-values.patch kvm-sev-don-t-terminate-snp-vms-on-vmgexit-without-a-registered-ghcb.patch kvm-sev-pin-source-page-for-write-when-adding-cpuid-data-for-snp-guest.patch kvm-svm-disable-x2avic-rdmsr-interception-for-msrs-kvm-actually-supports.patch kvm-svm-only-disable-x2avic-wrmsr-interception-for-msrs-that-are-accelerated.patch kvm-tdx-account-all-non-transient-page-allocations-for-per-td-structures.patch kvm-vmx-handle-bad-values-on-proxied-writes-to-lbr-msrs.patch kvm-vmx-refresh-guest_pending_dbg_exceptions.bs-on-all-injected-dbs.patch kvm-x86-add-dedicated-api-for-getting-mask-of-accelerated-x2apic-msrs.patch kvm-x86-ensure-vendor-s-exit-handler-runs-before-fastpath-userspace-exits.patch landlock-account-all-audit-data-allocations-to-user-space.patch landlock-set-audit_net.sk-for-socket-access-checks.patch loongarch-kvm-add-missing-slots_lock-for-device-register-unregister.patch revert-svcrdma-use-contiguous-pages-for-rdma-read-sink-buffers.patch selftests-landlock-explicitly-disable-audit-in-teardowns.patch selftests-landlock-filter-dealloc-records-in-audit_count_records.patch selftests-landlock-increase-default-audit-socket-timeout.patch svcrdma-wake-sq-waiters-when-the-transport-closes.patch udmabuf-fix-dma-direction-mismatch-in-release_udmabuf.patch --- diff --git a/queue-7.1/audit-fix-removal-of-dangling-executable-rules.patch b/queue-7.1/audit-fix-removal-of-dangling-executable-rules.patch new file mode 100644 index 0000000000..f8443f1385 --- /dev/null +++ b/queue-7.1/audit-fix-removal-of-dangling-executable-rules.patch @@ -0,0 +1,61 @@ +From 888a0396e154524f4027f27da84bdbec9eb68916 Mon Sep 17 00:00:00 2001 +From: Ricardo Robaina +Date: Wed, 13 May 2026 18:47:59 -0300 +Subject: audit: fix removal of dangling executable rules + +From: Ricardo Robaina + +commit 888a0396e154524f4027f27da84bdbec9eb68916 upstream. + +When an audited executable is deleted from the disk, its dentry +becomes negative. Any later attempt to delete the associated audit +rule will lead to audit_alloc_mark() encountering this negative +dentry and immediately aborting, returning -ENOENT. + +This early abort prevents the subsystem from allocating the temporary +fsnotify mark needed to construct the search key, meaning the kernel +cannot find the existing rule in its own lists to delete it. This +leaves a dangling rule in memory, resulting in the following error +while attempting to delete the rule: + + # ./audit-dupe-exe-deadlock.sh + No rules + Error deleting rule (No such file or directory) + There was an error while processing parameters + + # auditctl -l + -a always,exit -S all -F exe=/tmp/file -F path=/tmp/file -F key=dr + + # auditctl -D + Error deleting rule (No such file or directory) + There was an error while processing parameters + +This patch fixes this issue by removing the d_really_is_negative() +check. By doing so, a dummy mark can be successfully generated for +the deleted path, which allows the audit subsystem to properly match +and flush the dangling rule. + +Cc: stable@kernel.org +Fixes: 76a53de6f7ff ("VFS/audit: introduce kern_path_parent() for audit") +Acked-by: Waiman Long +Acked-by: Richard Guy Briggs +Signed-off-by: Ricardo Robaina +Signed-off-by: Paul Moore +Signed-off-by: Greg Kroah-Hartman +--- + kernel/audit_fsnotify.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/kernel/audit_fsnotify.c ++++ b/kernel/audit_fsnotify.c +@@ -84,10 +84,6 @@ struct audit_fsnotify_mark *audit_alloc_ + dentry = kern_path_parent(pathname, &path); + if (IS_ERR(dentry)) + return ERR_CAST(dentry); /* returning an error */ +- if (d_really_is_negative(dentry)) { +- audit_mark = ERR_PTR(-ENOENT); +- goto out; +- } + + audit_mark = kzalloc_obj(*audit_mark); + if (unlikely(!audit_mark)) { diff --git a/queue-7.1/dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch b/queue-7.1/dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch new file mode 100644 index 0000000000..6df4c9bca0 --- /dev/null +++ b/queue-7.1/dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch @@ -0,0 +1,81 @@ +From 504e2b4ab97a51d56d966cd36d0997ad30b65b2d Mon Sep 17 00:00:00 2001 +From: Mikhail Gavrilov +Date: Tue, 31 Mar 2026 11:16:57 +0500 +Subject: dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning + +From: Mikhail Gavrilov + +commit 504e2b4ab97a51d56d966cd36d0997ad30b65b2d upstream. + +When CONFIG_DMA_API_DEBUG_SG is enabled, importing a udmabuf into a DRM +driver (e.g. amdgpu for video playback in GNOME Videos / Showtime) +triggers a spurious warning: + + DMA-API: amdgpu 0000:03:00.0: cacheline tracking EEXIST, \ + overlapping mappings aren't supported + WARNING: kernel/dma/debug.c:619 at add_dma_entry+0x473/0x5f0 + +The call chain is: + + amdgpu_cs_ioctl + -> amdgpu_ttm_backend_bind + -> dma_buf_map_attachment + -> [udmabuf] map_udmabuf -> get_sg_table + -> dma_map_sgtable(dev, sg, direction, 0) // attrs=0 + -> debug_dma_map_sg -> add_dma_entry -> EEXIST + +This happens because udmabuf builds a per-page scatter-gather list via +sg_set_folio(). When begin_cpu_udmabuf() has already created an sg +table mapped for the misc device, and an importer such as amdgpu maps +the same pages for its own device via map_udmabuf(), the DMA debug +infrastructure sees two active mappings whose physical addresses share +cacheline boundaries and warns about the overlap. + +The DMA_ATTR_SKIP_CPU_SYNC flag suppresses this check in +add_dma_entry() because it signals that no CPU cache maintenance is +performed at map/unmap time, making the cacheline overlap harmless. + +All other major dma-buf exporters already pass this flag: + - drm_gem_map_dma_buf() passes DMA_ATTR_SKIP_CPU_SYNC + - amdgpu_dma_buf_map() passes DMA_ATTR_SKIP_CPU_SYNC + +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. + +Pass DMA_ATTR_SKIP_CPU_SYNC to dma_map_sgtable() and +dma_unmap_sgtable() in udmabuf to suppress the spurious warning and +skip the redundant sync. + +Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks") +Cc: stable@vger.kernel.org +Signed-off-by: Mikhail Gavrilov +Acked-by: Vivek Kasireddy +Signed-off-by: Vivek Kasireddy +Link: https://patch.msgid.link/20260331061657.79983-1-mikhail.v.gavrilov@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/dma-buf/udmabuf.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/dma-buf/udmabuf.c ++++ b/drivers/dma-buf/udmabuf.c +@@ -163,7 +163,7 @@ static struct sg_table *get_sg_table(str + sg_set_folio(sgl, ubuf->folios[i], PAGE_SIZE, + ubuf->offsets[i]); + +- ret = dma_map_sgtable(dev, sg, direction, 0); ++ ret = dma_map_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC); + if (ret < 0) + goto err_map; + return sg; +@@ -178,7 +178,7 @@ err_alloc: + static void put_sg_table(struct device *dev, struct sg_table *sg, + enum dma_data_direction direction) + { +- dma_unmap_sgtable(dev, sg, direction, 0); ++ dma_unmap_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC); + sg_free_table(sg); + kfree(sg); + } diff --git a/queue-7.1/fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch b/queue-7.1/fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch new file mode 100644 index 0000000000..9b7d4bc93a --- /dev/null +++ b/queue-7.1/fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch @@ -0,0 +1,42 @@ +From fc3b071a7c8dc0f5d56defddf6e6fd5aaa3e1e27 Mon Sep 17 00:00:00 2001 +From: Sebastian Alba Vives +Date: Mon, 18 May 2026 13:07:41 -0600 +Subject: fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region() + +From: Sebastian Alba Vives + +commit fc3b071a7c8dc0f5d56defddf6e6fd5aaa3e1e27 upstream. + +afu_ioctl_dma_map() accepts a 64-bit length from userspace via +DFL_FPGA_PORT_DMA_MAP ioctl without an upper bound check. The value +is passed to afu_dma_pin_pages() where npages is derived as +length >> PAGE_SHIFT and passed to pin_user_pages_fast() which takes +int nr_pages, causing implicit truncation if length is very large. + +Validate map.length at the ioctl entry point before calling +afu_dma_map_region(), rejecting values whose page count exceeds +INT_MAX. + +Fixes: fa8dda1edef9 ("fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support") +Cc: stable@vger.kernel.org +Signed-off-by: Sebastian Alba Vives +Reviewed-by: Xu Yilun +Link: https://lore.kernel.org/r/20260518190742.61426-3-sebasjosue84@gmail.com +Signed-off-by: Xu Yilun +Signed-off-by: Greg Kroah-Hartman +--- + drivers/fpga/dfl-afu-main.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/fpga/dfl-afu-main.c ++++ b/drivers/fpga/dfl-afu-main.c +@@ -723,6 +723,9 @@ afu_ioctl_dma_map(struct dfl_feature_dev + if (map.argsz < minsz || map.flags) + return -EINVAL; + ++ if (map.length >> PAGE_SHIFT > (u64)INT_MAX) ++ return -EINVAL; ++ + ret = afu_dma_map_region(fdata, map.user_addr, map.length, &map.iova); + if (ret) + return ret; diff --git a/queue-7.1/iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch b/queue-7.1/iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch new file mode 100644 index 0000000000..72de8d6304 --- /dev/null +++ b/queue-7.1/iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch @@ -0,0 +1,61 @@ +From 69fe699afe1afcb730164b86c228483c2da05f94 Mon Sep 17 00:00:00 2001 +From: Weinan Liu +Date: Thu, 28 May 2026 22:31:47 +0000 +Subject: iommu/amd: Don't split flush for amd_iommu_domain_flush_all() + +From: Weinan Liu + +commit 69fe699afe1afcb730164b86c228483c2da05f94 upstream. + +We have observed multiple full invalidations occurring during device +detach when we are done using the vfio-device. + +blocked_domain_attach_device() + -> detach_device() + -> amd_iommu_domain_flush_all() + -> amd_iommu_domain_flush_pages(..., CMD_INV_IOMMU_ALL_PAGES_ADDRESS) + + while (size != 0) { + + -> __domain_flush_pages( flush_size /* power of 2 flush_size */) + -> domain_flush_pages_v1() + -> build_inv_iommu_pages() + -> build_inv_address() + + } + +build_inv_address() will trigger a full invalidation if the chunk +size > (1 << 51). Consequently, the guest will issue multiple full +invalidations for a single call to amd_iommu_domain_flush_all() + +Without this patch, we will see 10 time instead of 1 time full +invalidations for every amd_iommu_domain_flush_all(). + +Cc: stable@vger.kernel.org +Fixes: a270be1b3fdf ("iommu/amd: Use only natural aligned flushes in a VM") +Suggested-by: Josef Bacik +Suggested-by: Jason Gunthorpe +Signed-off-by: Weinan Liu +Reviewed-by: Wei Wang +Reviewed-by: Jason Gunthorpe +Reviewed-by: Samiullah Khawaja +Reviewed-by: Suravee Suthikulpanit +Reviewed-by: Vasant Hegde +Signed-off-by: Joerg Roedel +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/amd/iommu.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/iommu/amd/iommu.c ++++ b/drivers/iommu/amd/iommu.c +@@ -1774,7 +1774,8 @@ void amd_iommu_domain_flush_pages(struct + { + lockdep_assert_held(&domain->lock); + +- if (likely(!amd_iommu_np_cache)) { ++ if (likely(!amd_iommu_np_cache) || ++ size >= (1ULL<<52)) { + __domain_flush_pages(domain, address, size); + + /* Wait until IOMMU TLB and all device IOTLB flushes are complete */ diff --git a/queue-7.1/iommu-vt-d-avoid-warning-in-sva-unbind-path.patch b/queue-7.1/iommu-vt-d-avoid-warning-in-sva-unbind-path.patch new file mode 100644 index 0000000000..a407cdf0e1 --- /dev/null +++ b/queue-7.1/iommu-vt-d-avoid-warning-in-sva-unbind-path.patch @@ -0,0 +1,103 @@ +From 534b5f98ab7319d8004bbc7dab6481462243e883 Mon Sep 17 00:00:00 2001 +From: Lu Baolu +Date: Thu, 4 Jun 2026 14:03:06 +0800 +Subject: iommu/vt-d: Avoid WARNING in sva unbind path + +From: Lu Baolu + +commit 534b5f98ab7319d8004bbc7dab6481462243e883 upstream. + +The Intel IOMMU driver allows SVA on devices even if they do not support +PCI/PRI. Commit 39c20c4e83b9 ("iommu/vt-d: Only handle IOPF for SVA when +PRI is supported") modified the SVA bind path to allow this configuration +by skipping IOPF enablement when PRI is missing. However, it failed to +update the unbind path. + +This creates an imbalance: the unbind path attempts to disable IOPF for +a device that never had it enabled, triggering a WARNING in +intel_iommu_disable_iopf(): + + WARNING: drivers/iommu/intel/iommu.c:3475 at intel_iommu_disable_iopf+0x4f/0x90d + Call Trace: + + blocking_domain_set_dev_pasid+0x50/0x70 + iommu_detach_device_pasid+0x89/0xc0 + iommu_sva_unbind_device+0x73/0x150 + xe_vm_close_and_put+0x4d2/0x1200 [xe] + +Fix this by bypassing IOPF operations for SVA domains on non-PRI hardware +in both the bind and unbind paths. + +Fixes: 39c20c4e83b9 ("iommu/vt-d: Only handle IOPF for SVA when PRI is supported") +Cc: stable@vger.kernel.org +Reported-by: Nareshkumar Gollakoti +Signed-off-by: Lu Baolu +Reviewed-by: Kevin Tian +Link: https://lore.kernel.org/r/20260519052917.3729796-1-baolu.lu@linux.intel.com +Signed-off-by: Joerg Roedel +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/intel/iommu.h | 11 +++++++++++ + drivers/iommu/intel/svm.c | 12 ++++-------- + 2 files changed, 15 insertions(+), 8 deletions(-) + +--- a/drivers/iommu/intel/iommu.h ++++ b/drivers/iommu/intel/iommu.h +@@ -1254,18 +1254,29 @@ void intel_iommu_disable_iopf(struct dev + static inline int iopf_for_domain_set(struct iommu_domain *domain, + struct device *dev) + { ++ struct device_domain_info *info = dev_iommu_priv_get(dev); ++ + if (!domain || !domain->iopf_handler) + return 0; + ++ /* SVA with non-IOMMU/PRI IOPF handling is allowed. */ ++ if (domain->type == IOMMU_DOMAIN_SVA && !info->pri_supported) ++ return 0; ++ + return intel_iommu_enable_iopf(dev); + } + + static inline void iopf_for_domain_remove(struct iommu_domain *domain, + struct device *dev) + { ++ struct device_domain_info *info = dev_iommu_priv_get(dev); ++ + if (!domain || !domain->iopf_handler) + return; + ++ if (domain->type == IOMMU_DOMAIN_SVA && !info->pri_supported) ++ return; ++ + intel_iommu_disable_iopf(dev); + } + +--- a/drivers/iommu/intel/svm.c ++++ b/drivers/iommu/intel/svm.c +@@ -164,12 +164,9 @@ static int intel_svm_set_dev_pasid(struc + if (IS_ERR(dev_pasid)) + return PTR_ERR(dev_pasid); + +- /* SVA with non-IOMMU/PRI IOPF handling is allowed. */ +- if (info->pri_supported) { +- ret = iopf_for_domain_replace(domain, old, dev); +- if (ret) +- goto out_remove_dev_pasid; +- } ++ ret = iopf_for_domain_replace(domain, old, dev); ++ if (ret) ++ goto out_remove_dev_pasid; + + /* Setup the pasid table: */ + sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0; +@@ -184,8 +181,7 @@ static int intel_svm_set_dev_pasid(struc + + return 0; + out_unwind_iopf: +- if (info->pri_supported) +- iopf_for_domain_replace(old, domain, dev); ++ iopf_for_domain_replace(old, domain, dev); + out_remove_dev_pasid: + domain_remove_dev_pasid(domain, dev, pasid); + return ret; diff --git a/queue-7.1/iommufd-avoid-partial-fault-group-delivery-in-iommufd_fault_fops_read.patch b/queue-7.1/iommufd-avoid-partial-fault-group-delivery-in-iommufd_fault_fops_read.patch new file mode 100644 index 0000000000..7c5ce1006c --- /dev/null +++ b/queue-7.1/iommufd-avoid-partial-fault-group-delivery-in-iommufd_fault_fops_read.patch @@ -0,0 +1,70 @@ +From 091ab6d70dc444f56ed14faedbcacfc979f4c613 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Mon, 1 Jun 2026 13:42:37 -0700 +Subject: iommufd: Avoid partial fault group delivery in iommufd_fault_fops_read() + +From: Nicolin Chen + +commit 091ab6d70dc444f56ed14faedbcacfc979f4c613 upstream. + +The cookie returned by xa_alloc() in iommufd_fault_fops_read() is per fault +group, but the inner copy_to_user() runs per fault inside the group. If a +copy fails mid-group, xa_erase clears the cookie and the group is restored +to the deliver list, yet done is not rolled back. The function returns the +partial byte count, with the successfully copied faults sitting at offsets +below done carrying the now-erased cookie. The next read() then re-fetches +the group, allocates a fresh cookie, and re-delivers every fault including +the ones already copied; userspace sees duplicates carrying the new cookie, +and a stale cookie that can never be responded to. + +Use a local group_done variable that tracks the per-group progress inside +the inner loop, and only commit done = group_done after the inner loop has +finished successfully. On a copy_to_user failure the outer break skips the +commit, so done remains at its prior start-of-group baseline; the partial +bytes already written past done are undefined to userspace per the read(2) +contract, and the next read re-delivers the whole group atomically. + +Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") +Link: https://patch.msgid.link/r/360cab4d4aeccb0bae275a970e2b3c340a71e0e0.1780343944.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Nicolin Chen +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/eventq.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/iommu/iommufd/eventq.c ++++ b/drivers/iommu/iommufd/eventq.c +@@ -139,6 +139,8 @@ static ssize_t iommufd_fault_fops_read(s + + mutex_lock(&fault->mutex); + while ((group = iommufd_fault_deliver_fetch(fault))) { ++ size_t group_done = done; ++ + if (done >= count || + group->fault_count * fault_size > count - done) { + iommufd_fault_deliver_restore(fault, group); +@@ -160,16 +162,17 @@ static ssize_t iommufd_fault_fops_read(s + iommufd_compose_fault_message(&iopf->fault, + &data, idev, + group->cookie); +- if (copy_to_user(buf + done, &data, fault_size)) { ++ if (copy_to_user(buf + group_done, &data, fault_size)) { + xa_erase(&fault->response, group->cookie); + iommufd_fault_deliver_restore(fault, group); + rc = -EFAULT; + break; + } +- done += fault_size; ++ group_done += fault_size; + } + if (rc) + break; ++ done = group_done; + } + mutex_unlock(&fault->mutex); + diff --git a/queue-7.1/iommufd-break-the-loop-on-failure-in-iommufd_fault_fops_read.patch b/queue-7.1/iommufd-break-the-loop-on-failure-in-iommufd_fault_fops_read.patch new file mode 100644 index 0000000000..0b4a0d768a --- /dev/null +++ b/queue-7.1/iommufd-break-the-loop-on-failure-in-iommufd_fault_fops_read.patch @@ -0,0 +1,39 @@ +From 172fc8b19825a0f5884c38f2289188284e2d45ee Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Mon, 1 Jun 2026 13:42:36 -0700 +Subject: iommufd: Break the loop on failure in iommufd_fault_fops_read() + +From: Nicolin Chen + +commit 172fc8b19825a0f5884c38f2289188284e2d45ee upstream. + +On a copy_to_user() failure inside the inner list_for_each_entry, only the +inner loop breaks; the outer while re-fetches the just-restored fault group +and retries the failing copy_to_user() forever, spinning the reader at 100% +CPU with fault->mutex held. + +Check rc after the inner loop and break the outer while as well. + +Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") +Link: https://patch.msgid.link/r/336a9b6e44fe66a24199d3be777c405c85c98622.1780343944.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Signed-off-by: Nicolin Chen +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/eventq.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/iommu/iommufd/eventq.c ++++ b/drivers/iommu/iommufd/eventq.c +@@ -168,6 +168,8 @@ static ssize_t iommufd_fault_fops_read(s + } + done += fault_size; + } ++ if (rc) ++ break; + } + mutex_unlock(&fault->mutex); + diff --git a/queue-7.1/iommufd-fix-data_len-byte-count-vs-element-count-mismatch.patch b/queue-7.1/iommufd-fix-data_len-byte-count-vs-element-count-mismatch.patch new file mode 100644 index 0000000000..6a7e3d04f3 --- /dev/null +++ b/queue-7.1/iommufd-fix-data_len-byte-count-vs-element-count-mismatch.patch @@ -0,0 +1,40 @@ +From 85345becfead3255a5f875d4b4d82ea01d926239 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Thu, 21 May 2026 17:36:32 -0700 +Subject: iommufd: Fix data_len byte-count vs element-count mismatch + +From: Nicolin Chen + +commit 85345becfead3255a5f875d4b4d82ea01d926239 upstream. + +kzalloc_flex() computes the allocation size. With event_data typed as u64, +data_len is interpreted as a u64 element count. Yet, every caller and the +read path treat data_len as a byte count. The current code over-allocates +by sizeof(u64) and the __counted_by() annotation overstates the length by +the same factor. + +Re-type event_data as u8. No functional change in user-visible behavior. + +Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") +Link: https://patch.msgid.link/r/f7665f839b9dce917d6bd394375a1cf56568d86b.1779408671.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Reviewed-by: Jason Gunthorpe +Signed-off-by: Nicolin Chen +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/iommufd_private.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/iommu/iommufd/iommufd_private.h ++++ b/drivers/iommu/iommufd/iommufd_private.h +@@ -602,7 +602,7 @@ struct iommufd_vevent { + struct iommufd_vevent_header header; + struct list_head node; /* for iommufd_eventq::deliver */ + ssize_t data_len; +- u64 event_data[] __counted_by(data_len); ++ u8 event_data[] __counted_by(data_len); + }; + + #define vevent_for_lost_events_header(vevent) \ diff --git a/queue-7.1/iommufd-move-vevent-memory-allocation-outside-spinlock.patch b/queue-7.1/iommufd-move-vevent-memory-allocation-outside-spinlock.patch new file mode 100644 index 0000000000..b8fdd28f84 --- /dev/null +++ b/queue-7.1/iommufd-move-vevent-memory-allocation-outside-spinlock.patch @@ -0,0 +1,63 @@ +From 47443565d10c51366c9382dbc8597cd6c460b8a2 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Thu, 21 May 2026 17:36:33 -0700 +Subject: iommufd: Move vevent memory allocation outside spinlock + +From: Nicolin Chen + +commit 47443565d10c51366c9382dbc8597cd6c460b8a2 upstream. + +The veventq memory allocation happens inside the spinlock. Given its depth +is decided by the user space, this leaves a vulnerability, where userspace +can allocate large queues to exhaust atomic memory reserves. + +Move the allocation outside the spinlock and use GFP_NOWAIT, which can fail +fast under memory pressure without dipping into the GFP_ATOMIC reserves or +direct-reclaiming from the threaded IRQ handler. On allocation failure, +queue the lost_events_header (so userspace learns of the drop) and return +-ENOMEM so the caller learns of the kernel-side memory pressure. + +This is intentionally distinct from the queue-overflow path, which also +queues the lost_events_header but returns 0: a full queue is an expected +userspace-pacing condition rather than a kernel error. + +A subsequent change will cap the upper bound of the veventq_depth. + +Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") +Link: https://patch.msgid.link/r/5ff36b5d80f7f6299f851be532a5195c1d2f1dae.1779408671.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Reviewed-by: Jason Gunthorpe +Signed-off-by: Nicolin Chen +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/driver.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +--- a/drivers/iommu/iommufd/driver.c ++++ b/drivers/iommu/iommufd/driver.c +@@ -149,15 +149,18 @@ int iommufd_viommu_report_event(struct i + goto out_unlock_veventqs; + } + +- spin_lock(&veventq->common.lock); +- if (veventq->num_events == veventq->depth) { ++ /* Pre-allocate to avoid GFP_ATOMIC; use GFP_NOWAIT to avoid sleeping */ ++ vevent = kzalloc_flex(*vevent, event_data, data_len, GFP_NOWAIT); ++ if (!vevent) { ++ spin_lock(&veventq->common.lock); + vevent = &veventq->lost_events_header; ++ rc = -ENOMEM; + goto out_set_header; + } + +- vevent = kzalloc_flex(*vevent, event_data, data_len, GFP_ATOMIC); +- if (!vevent) { +- rc = -ENOMEM; ++ spin_lock(&veventq->common.lock); ++ if (veventq->num_events == veventq->depth) { ++ kfree(vevent); + vevent = &veventq->lost_events_header; + goto out_set_header; + } diff --git a/queue-7.1/iommufd-propagate-allocation-failure-in-iommufd_veventq_deliver_fetch.patch b/queue-7.1/iommufd-propagate-allocation-failure-in-iommufd_veventq_deliver_fetch.patch new file mode 100644 index 0000000000..d577f74764 --- /dev/null +++ b/queue-7.1/iommufd-propagate-allocation-failure-in-iommufd_veventq_deliver_fetch.patch @@ -0,0 +1,53 @@ +From 489e63dd120bad52eba63f5506c214750cd5bc75 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Mon, 1 Jun 2026 13:42:34 -0700 +Subject: iommufd: Propagate allocation failure in iommufd_veventq_deliver_fetch() + +From: Nicolin Chen + +commit 489e63dd120bad52eba63f5506c214750cd5bc75 upstream. + +When the kzalloc_obj() fails in iommufd_veventq_deliver_fetch(), it returns +NULL, falsely advertising to userspace that the queue is empty. + +Propagate the -ENOMEM properly to the caller. + +Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") +Link: https://patch.msgid.link/r/25d29feac909e36f78c145fa99ef2d4cb7a415da.1780343944.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Signed-off-by: Nicolin Chen +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/eventq.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/iommu/iommufd/eventq.c ++++ b/drivers/iommu/iommufd/eventq.c +@@ -264,8 +264,10 @@ iommufd_veventq_deliver_fetch(struct iom + /* Make a copy of the lost_events_header for copy_to_user */ + if (next == &veventq->lost_events_header) { + vevent = kzalloc_obj(*vevent, GFP_ATOMIC); +- if (!vevent) ++ if (!vevent) { ++ vevent = ERR_PTR(-ENOMEM); + goto out_unlock; ++ } + } + list_del(&next->node); + if (vevent) +@@ -315,6 +317,12 @@ static ssize_t iommufd_veventq_fops_read + return -EINVAL; + + while ((cur = iommufd_veventq_deliver_fetch(veventq))) { ++ if (IS_ERR(cur)) { ++ if (done == 0) ++ rc = PTR_ERR(cur); ++ break; ++ } ++ + /* Validate the remaining bytes against the header size */ + if (done >= count || sizeof(*hdr) > count - done) { + iommufd_veventq_deliver_restore(veventq, cur); diff --git a/queue-7.1/iommufd-reject-invalid-read-count-in-iommufd_fault_fops_read.patch b/queue-7.1/iommufd-reject-invalid-read-count-in-iommufd_fault_fops_read.patch new file mode 100644 index 0000000000..df7b7b70d3 --- /dev/null +++ b/queue-7.1/iommufd-reject-invalid-read-count-in-iommufd_fault_fops_read.patch @@ -0,0 +1,40 @@ +From 47916a54eeb2a9e654512ee609f71bd5b29db702 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Mon, 1 Jun 2026 13:42:35 -0700 +Subject: iommufd: Reject invalid read count in iommufd_fault_fops_read() + +From: Nicolin Chen + +commit 47916a54eeb2a9e654512ee609f71bd5b29db702 upstream. + +The read count must be large enough to hold one fault or a group's faults. + +iommufd_fault_fops_read() does not validate the count, but returns 0 as if +the read had succeeded while leaving the pending fault in the queue. + +Return -EINVAL in the undersize cases. + +Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") +Link: https://patch.msgid.link/r/85c118a606fbedc5c132a1f5ec223a5ba23b92d2.1780343944.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Signed-off-by: Nicolin Chen +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/eventq.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/iommu/iommufd/eventq.c ++++ b/drivers/iommu/iommufd/eventq.c +@@ -142,6 +142,9 @@ static ssize_t iommufd_fault_fops_read(s + if (done >= count || + group->fault_count * fault_size > count - done) { + iommufd_fault_deliver_restore(fault, group); ++ /* Read count doesn't fit the first fault group */ ++ if (done == 0) ++ rc = -EINVAL; + break; + } + diff --git a/queue-7.1/iommufd-reject-invalid-read-count-in-iommufd_veventq_fops_read.patch b/queue-7.1/iommufd-reject-invalid-read-count-in-iommufd_veventq_fops_read.patch new file mode 100644 index 0000000000..d56bc89709 --- /dev/null +++ b/queue-7.1/iommufd-reject-invalid-read-count-in-iommufd_veventq_fops_read.patch @@ -0,0 +1,51 @@ +From 00203ca8323f9714630408c19a209b52397975e6 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Mon, 1 Jun 2026 13:42:33 -0700 +Subject: iommufd: Reject invalid read count in iommufd_veventq_fops_read() + +From: Nicolin Chen + +commit 00203ca8323f9714630408c19a209b52397975e6 upstream. + +The read count must be large enough to hold a vEVENT header. For a normal +vEVENT, it must also hold the trailing data following the header. + +iommufd_veventq_fops_read() does not validate the count, but returns 0 as +if the read had succeeded while leaving the pending event in the queue. + +Return -EINVAL in both undersize cases. + +Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") +Link: https://patch.msgid.link/r/e1111adcc8a8882fbfd84accd6674dc846dc5689.1780343944.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Signed-off-by: Nicolin Chen +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/eventq.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/iommu/iommufd/eventq.c ++++ b/drivers/iommu/iommufd/eventq.c +@@ -310,6 +310,9 @@ static ssize_t iommufd_veventq_fops_read + + if (*ppos) + return -ESPIPE; ++ /* Minimum read count is a vEVENT header */ ++ if (count < sizeof(*hdr)) ++ return -EINVAL; + + while ((cur = iommufd_veventq_deliver_fetch(veventq))) { + /* Validate the remaining bytes against the header size */ +@@ -323,6 +326,9 @@ static ssize_t iommufd_veventq_fops_read + if (!vevent_for_lost_events_header(cur) && + sizeof(*hdr) + cur->data_len > count - done) { + iommufd_veventq_deliver_restore(veventq, cur); ++ /* Read count doesn't fit a single normal vEVENT */ ++ if (done == 0) ++ rc = -EINVAL; + break; + } + diff --git a/queue-7.1/iommufd-rewind-header-length-in-done-if-iommufd_veventq_fops_read-fails.patch b/queue-7.1/iommufd-rewind-header-length-in-done-if-iommufd_veventq_fops_read-fails.patch new file mode 100644 index 0000000000..5fd235940f --- /dev/null +++ b/queue-7.1/iommufd-rewind-header-length-in-done-if-iommufd_veventq_fops_read-fails.patch @@ -0,0 +1,36 @@ +From 01e41ad76c12ae5c49ab4ef4fc7dd54e9b8784d6 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Mon, 1 Jun 2026 13:42:32 -0700 +Subject: iommufd: Rewind header length in done if iommufd_veventq_fops_read() fails + +From: Nicolin Chen + +commit 01e41ad76c12ae5c49ab4ef4fc7dd54e9b8784d6 upstream. + +When the first event copy fails, rc = -EFAULT will not be reported as done +is set to the length of the copied header. + +Rewind it to report rc correctly. + +Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") +Link: https://patch.msgid.link/r/78f8caeb6a5d667a26b870e3068cec47dd4b5be1.1780343944.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Signed-off-by: Nicolin Chen +Reviewed-by: Pranjal Shrivastava +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/eventq.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/iommu/iommufd/eventq.c ++++ b/drivers/iommu/iommufd/eventq.c +@@ -336,6 +336,7 @@ static ssize_t iommufd_veventq_fops_read + if (cur->data_len && + copy_to_user(buf + done, cur->event_data, cur->data_len)) { + iommufd_veventq_deliver_restore(veventq, cur); ++ done -= sizeof(*hdr); + rc = -EFAULT; + break; + } diff --git a/queue-7.1/iommufd-set-upper-bounds-on-cache-invalidation-entry_num-and-entry_len.patch b/queue-7.1/iommufd-set-upper-bounds-on-cache-invalidation-entry_num-and-entry_len.patch new file mode 100644 index 0000000000..d67dc55078 --- /dev/null +++ b/queue-7.1/iommufd-set-upper-bounds-on-cache-invalidation-entry_num-and-entry_len.patch @@ -0,0 +1,62 @@ +From 4d70986002f2f3eaaed89124fb2522bded38b016 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Wed, 3 Jun 2026 14:26:53 -0700 +Subject: iommufd: Set upper bounds on cache invalidation entry_num and entry_len + +From: Nicolin Chen + +commit 4d70986002f2f3eaaed89124fb2522bded38b016 upstream. + +iommufd_hwpt_invalidate() takes a user-controlled entry_num and entry_len, +each bounded only by U32_MAX. An entry_len beyond the kernel's struct size +makes the copy helper verify the extra bytes are zero, scanning that excess +in one uninterruptible pass; a multi-gigabyte value over zeroed user memory +trips the soft-lockup watchdog. + +A large entry_num is the other half, driving the backend invalidation loop +with no reschedule. The VT-d nested handler, for one, copies each entry and +flushes caches per iteration, pinning the CPU on a non-preemptible kernel. + +Cap both in the ioctl. entry_len is held under PAGE_SIZE, above any request +struct, and entry_num under 1 << 19, the order of a hardware invalidation +queue and well beyond any real batch, bounding the per-call loop length. + +Fixes: 8c6eabae3807 ("iommufd: Add IOMMU_HWPT_INVALIDATE") +Link: https://patch.msgid.link/r/447fa93663f7526eb361719e83fa8b649464483d.1780521606.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Nicolin Chen +Reviewed-by: Lu Baolu +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/hw_pagetable.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/drivers/iommu/iommufd/hw_pagetable.c ++++ b/drivers/iommu/iommufd/hw_pagetable.c +@@ -489,6 +489,9 @@ int iommufd_hwpt_get_dirty_bitmap(struct + return rc; + } + ++/* An arbitrary entry_num cap, far above any realistic invalidation batch */ ++#define IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX (1U << 19) ++ + int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) + { + struct iommu_hwpt_invalidate *cmd = ucmd->cmd; +@@ -507,7 +510,13 @@ int iommufd_hwpt_invalidate(struct iommu + goto out; + } + +- if (cmd->entry_num && (!cmd->data_uptr || !cmd->entry_len)) { ++ /* ++ * Bound entry_num and entry_len so a single call cannot pin the CPU; ++ * entry_len also caps the copy_struct_from_user() trailing-zero scan. ++ */ ++ if (cmd->entry_num && ++ (!cmd->data_uptr || !cmd->entry_len || cmd->entry_len > PAGE_SIZE || ++ cmd->entry_num > IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX)) { + rc = -EINVAL; + goto out; + } diff --git a/queue-7.1/iommufd-set-veventq_depth-upper-bound.patch b/queue-7.1/iommufd-set-veventq_depth-upper-bound.patch new file mode 100644 index 0000000000..0ed09f4449 --- /dev/null +++ b/queue-7.1/iommufd-set-veventq_depth-upper-bound.patch @@ -0,0 +1,51 @@ +From 6ebf2eb46fbd5b40393ff8fbb847ba96925beaff Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Thu, 21 May 2026 17:36:34 -0700 +Subject: iommufd: Set veventq_depth upper bound + +From: Nicolin Chen + +commit 6ebf2eb46fbd5b40393ff8fbb847ba96925beaff upstream. + +iommufd_veventq_alloc() accepts any !0 veventq_depth from userspace, with +an upper bound at U32_MAX. + +This leaves a vulnerability where userspace can allocate excessively large +queues to exhaust kernel memory reserves. + +Cap the veventq_depth (maximum number of entries) to 1 << 19, matching the +maximum number of entries in the SMMUv3 EVTQ (the largest use case today). + +Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") +Link: https://patch.msgid.link/r/8426cbaa5e8294472ec7f076ef427cc473be5985.1779408671.git.nicolinc@nvidia.com +Cc: stable@vger.kernel.org +Reviewed-by: Jason Gunthorpe +Signed-off-by: Nicolin Chen +Reviewed-by: Kevin Tian +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/eventq.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/iommu/iommufd/eventq.c ++++ b/drivers/iommu/iommufd/eventq.c +@@ -473,6 +473,9 @@ int iommufd_fault_iopf_handler(struct io + static const struct file_operations iommufd_veventq_fops = + INIT_EVENTQ_FOPS(iommufd_veventq_fops_read, NULL); + ++/* An arbitrary upper bound for veventq_depth that fits all existing HWs */ ++#define VEVENTQ_MAX_DEPTH (1U << 19) ++ + int iommufd_veventq_alloc(struct iommufd_ucmd *ucmd) + { + struct iommu_veventq_alloc *cmd = ucmd->cmd; +@@ -484,7 +487,7 @@ int iommufd_veventq_alloc(struct iommufd + if (cmd->flags || cmd->__reserved || + cmd->type == IOMMU_VEVENTQ_TYPE_DEFAULT) + return -EOPNOTSUPP; +- if (!cmd->veventq_depth) ++ if (!cmd->veventq_depth || cmd->veventq_depth > VEVENTQ_MAX_DEPTH) + return -EINVAL; + + viommu = iommufd_get_viommu(ucmd, cmd->viommu_id); diff --git a/queue-7.1/iommufd-use-sizeof-hdr-instead-of-sizeof-hdr-in-veventq-read.patch b/queue-7.1/iommufd-use-sizeof-hdr-instead-of-sizeof-hdr-in-veventq-read.patch new file mode 100644 index 0000000000..3fbbe66477 --- /dev/null +++ b/queue-7.1/iommufd-use-sizeof-hdr-instead-of-sizeof-hdr-in-veventq-read.patch @@ -0,0 +1,68 @@ +From be93d186ae88a92e7aa77e122d4e661fa57b1e39 Mon Sep 17 00:00:00 2001 +From: Kai Aizen +Date: Thu, 30 Apr 2026 20:56:30 +0300 +Subject: iommufd: Use sizeof(*hdr) instead of sizeof(hdr) in veventq read + +From: Kai Aizen + +commit be93d186ae88a92e7aa77e122d4e661fa57b1e39 upstream. + +The bound-check in iommufd_veventq_fops_read() for the normal vEVENT +path uses sizeof(hdr) where the surrounding code uses sizeof(*hdr): + + if (!vevent_for_lost_events_header(cur) && + sizeof(hdr) + cur->data_len > count - done) { + +hdr is declared as struct iommufd_vevent_header *, so sizeof(hdr) +evaluates to the size of the pointer. Surrounding code uses +sizeof(*hdr) consistently: + + if (done >= count || sizeof(*hdr) > count - done) { + ... + if (copy_to_user(buf + done, hdr, sizeof(*hdr))) { + ... + done += sizeof(*hdr); + +struct iommufd_vevent_header is currently 8 bytes (two __u32 fields, +flags and sequence), so on 64-bit (sizeof(void *) == 8) the two +expressions happen to be equal and the check works as intended. + +On 32-bit (sizeof(void *) == 4) the check under-counts the header by +4 bytes: a vEVENT whose data_len causes 8 + cur->data_len to exceed +count - done while 4 + cur->data_len does not will pass the check, +then the loop will copy_to_user 8 bytes of header followed by data_len +bytes of payload, writing past the user-supplied buffer. + +It is also a latent bug for any future expansion of struct +iommufd_vevent_header beyond sizeof(void *) on 64-bit; the check +should not depend on the type happening to match the host pointer +width. + +Use sizeof(*hdr) to match the rest of the function and the actual +amount that will be copied. + +Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC") +Link: https://patch.msgid.link/r/20260430175630.67078-1-kai.aizen.dev@gmail.com +Cc: stable@vger.kernel.org +Reported-by: Kai Aizen +Signed-off-by: Kai Aizen +Reviewed-by: Kevin Tian +Reviewed-by: Nicolin Chen +Reviewed-by: Lu Baolu +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/eventq.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/iommu/iommufd/eventq.c ++++ b/drivers/iommu/iommufd/eventq.c +@@ -321,7 +321,7 @@ static ssize_t iommufd_veventq_fops_read + + /* If being a normal vEVENT, validate against the full size */ + if (!vevent_for_lost_events_header(cur) && +- sizeof(hdr) + cur->data_len > count - done) { ++ sizeof(*hdr) + cur->data_len > count - done) { + iommufd_veventq_deliver_restore(veventq, cur); + break; + } diff --git a/queue-7.1/kvm-arm64-bound-used_lrs-when-flushing-the-pkvm-hyp-vcpu.patch b/queue-7.1/kvm-arm64-bound-used_lrs-when-flushing-the-pkvm-hyp-vcpu.patch new file mode 100644 index 0000000000..3d57feae37 --- /dev/null +++ b/queue-7.1/kvm-arm64-bound-used_lrs-when-flushing-the-pkvm-hyp-vcpu.patch @@ -0,0 +1,83 @@ +From 8cc8bbbfab14c22c5551d0dd19b208a44b141c76 Mon Sep 17 00:00:00 2001 +From: Hyunwoo Kim +Date: Sun, 7 Jun 2026 02:56:11 +0900 +Subject: KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU + +From: Hyunwoo Kim + +commit 8cc8bbbfab14c22c5551d0dd19b208a44b141c76 upstream. + +flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU +on every run. The vGIC list register save and restore use used_lrs as +their loop bound and expect it to stay within the number of implemented +list registers. While this is generally the case, flush_hyp_vcpu() +copies vgic_v3 verbatim and does not enforce this, so a value provided +by the host is used at EL2 to index vgic_lr[] and access ICH_LR_EL2 +(host -> EL2). + +Fix by clamping used_lrs to the number of implemented list registers +after the copy, as the trusted path already does in +vgic_flush_lr_state(). The number of implemented list registers is +constant after init, so it is replicated once from +kvm_vgic_global_state.nr_lr into hyp_gicv3_nr_lr rather than read on +every entry. + +Cc: stable@vger.kernel.org +Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()") +Signed-off-by: Hyunwoo Kim +Reviewed-by: Fuad Tabba +Tested-by: Fuad Tabba +Link: https://patch.msgid.link/20260606175614.83273-3-imv4bel@gmail.com +Signed-off-by: Marc Zyngier +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/include/asm/kvm_hyp.h | 1 + + arch/arm64/kvm/arm.c | 2 ++ + arch/arm64/kvm/hyp/nvhe/hyp-main.c | 9 +++++++++ + 3 files changed, 12 insertions(+) + +--- a/arch/arm64/include/asm/kvm_hyp.h ++++ b/arch/arm64/include/asm/kvm_hyp.h +@@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icac + extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits); + extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl); + extern unsigned long kvm_nvhe_sym(hyp_nr_cpus); ++extern unsigned int kvm_nvhe_sym(hyp_gicv3_nr_lr); + + #endif /* __ARM64_KVM_HYP_H__ */ +--- a/arch/arm64/kvm/arm.c ++++ b/arch/arm64/kvm/arm.c +@@ -2426,6 +2426,8 @@ static int __init init_subsystems(void) + switch (err) { + case 0: + vgic_present = true; ++ if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) ++ kvm_nvhe_sym(hyp_gicv3_nr_lr) = kvm_vgic_global_state.nr_lr; + break; + case -ENODEV: + case -ENXIO: +--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c ++++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c +@@ -24,6 +24,9 @@ + + DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); + ++/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */ ++unsigned int hyp_gicv3_nr_lr; ++ + void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt); + + static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu) +@@ -139,6 +142,12 @@ static void flush_hyp_vcpu(struct pkvm_h + + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3; + ++ /* Bound used_lrs by the number of implemented list registers. */ ++ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs = ++ min_t(unsigned int, ++ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs, ++ hyp_gicv3_nr_lr); ++ + hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid; + } + diff --git a/queue-7.1/kvm-arm64-clear-__hyp_running_vcpu-when-flushing-the-pkvm-hyp-vcpu.patch b/queue-7.1/kvm-arm64-clear-__hyp_running_vcpu-when-flushing-the-pkvm-hyp-vcpu.patch new file mode 100644 index 0000000000..322017693c --- /dev/null +++ b/queue-7.1/kvm-arm64-clear-__hyp_running_vcpu-when-flushing-the-pkvm-hyp-vcpu.patch @@ -0,0 +1,43 @@ +From e8042f6e1d7befb2fb6b10a75918642bcd0acf9a Mon Sep 17 00:00:00 2001 +From: Hyunwoo Kim +Date: Sun, 7 Jun 2026 02:56:10 +0900 +Subject: KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU + +From: Hyunwoo Kim + +commit e8042f6e1d7befb2fb6b10a75918642bcd0acf9a upstream. + +flush_hyp_vcpu() copies the host vCPU context into the hyp's private +vCPU on every run. ctxt_to_vcpu() expects a guest context to have a +NULL __hyp_running_vcpu, which is only ever set on the host context, so +that it resolves the vCPU via container_of(). While this is generally +the case, flush_hyp_vcpu() copies the context verbatim and does not +enforce this, so a value provided by the host is dereferenced at EL2 +(host -> EL2). + +Fix by clearing __hyp_running_vcpu after the copy. + +Cc: stable@vger.kernel.org +Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()") +Signed-off-by: Hyunwoo Kim +Reviewed-by: Fuad Tabba +Tested-by: Fuad Tabba +Link: https://patch.msgid.link/20260606175614.83273-2-imv4bel@gmail.com +Signed-off-by: Marc Zyngier +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/kvm/hyp/nvhe/hyp-main.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c ++++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c +@@ -131,6 +131,9 @@ static void flush_hyp_vcpu(struct pkvm_h + + hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt; + ++ /* __hyp_running_vcpu must be NULL in a guest context. */ ++ hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL; ++ + hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; + hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE); + hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) & diff --git a/queue-7.1/kvm-arm64-don-t-leak-pfn-when-kvm_translate_vncr-races-mmu-notifier.patch b/queue-7.1/kvm-arm64-don-t-leak-pfn-when-kvm_translate_vncr-races-mmu-notifier.patch new file mode 100644 index 0000000000..d69523dd7c --- /dev/null +++ b/queue-7.1/kvm-arm64-don-t-leak-pfn-when-kvm_translate_vncr-races-mmu-notifier.patch @@ -0,0 +1,38 @@ +From 9f76b039a72d7e06374aa96862f0232ed53f7787 Mon Sep 17 00:00:00 2001 +From: Oliver Upton +Date: Tue, 2 Jun 2026 16:54:46 -0700 +Subject: KVM: arm64: Don't leak PFN when kvm_translate_vncr() races MMU notifier + +From: Oliver Upton + +commit 9f76b039a72d7e06374aa96862f0232ed53f7787 upstream. + +In the case that kvm_translate_vncr() races with an MMU notifier the +early return does not release a reference on the faulted in PFN. Add +the necessary call to kvm_release_faultin_page() for the unused PFN. + +Cc: stable@vger.kernel.org +Fixes: 069a05e535496 ("KVM: arm64: nv: Handle VNCR_EL2-triggered faults") +Reported-by: Sashiko (local):gemini-3.1-pro +Signed-off-by: Oliver Upton +Link: https://patch.msgid.link/20260602235450.103057-2-oupton@kernel.org +Signed-off-by: Marc Zyngier +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/kvm/nested.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/arch/arm64/kvm/nested.c ++++ b/arch/arm64/kvm/nested.c +@@ -1327,8 +1327,10 @@ static int kvm_translate_vncr(struct kvm + } + + scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { +- if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) ++ if (mmu_invalidate_retry(vcpu->kvm, mmu_seq)) { ++ kvm_release_faultin_page(vcpu->kvm, page, true, false); + return -EAGAIN; ++ } + + vt->gva = va; + vt->hpa = pfn << PAGE_SHIFT; diff --git a/queue-7.1/kvm-arm64-nv-avoid-dereferencing-null-vncr-pseudo-tlb.patch b/queue-7.1/kvm-arm64-nv-avoid-dereferencing-null-vncr-pseudo-tlb.patch new file mode 100644 index 0000000000..08233c51c0 --- /dev/null +++ b/queue-7.1/kvm-arm64-nv-avoid-dereferencing-null-vncr-pseudo-tlb.patch @@ -0,0 +1,104 @@ +From 4be6cbeb93d26994bd1827ddbce391e3c4395c8f Mon Sep 17 00:00:00 2001 +From: Marc Zyngier +Date: Sun, 7 Jun 2026 18:57:45 +0100 +Subject: KVM: arm64: nv: Avoid dereferencing NULL VNCR pseudo-TLB + +From: Marc Zyngier + +commit 4be6cbeb93d26994bd1827ddbce391e3c4395c8f upstream. + +VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, +and either can race against a vcpu not being onlined yet (no pseudo-TLB +allocated). Similarly, the TLB might be invalid, and the invalidation +should be skipped in this case. + +Both kvm_invalidate_vncr_ipa() and kvm_invalidate_vncr_va() are +expected to perform the same checks, except that the latter doesn't +check for the allocation and blindly dereferences the pointer. + +Solve this by introducing a new iterator built on top of the usual +kvm_for_each_vcpu() that checks for both of the above conditions, +and convert the two users to it. + +Reported-by: Hyunwoo Kim +Link: https://lore.kernel.org/r/aiUvSbrWndQeUPc8@v4bel +Fixes: 4ffa72ad8f37 ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2") +Cc: stable@vger.kernel.org +Reviewed-by: Oliver Upton +Link: https://patch.msgid.link/20260607175745.297793-1-maz@kernel.org +Signed-off-by: Marc Zyngier +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/kvm/nested.c | 36 +++++++++++++++--------------------- + 1 file changed, 15 insertions(+), 21 deletions(-) + +--- a/arch/arm64/kvm/nested.c ++++ b/arch/arm64/kvm/nested.c +@@ -904,9 +904,21 @@ static void invalidate_vncr(struct vncr_ + clear_fixmap(vncr_fixmap(vt->cpu)); + } + ++/* ++ * VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and ++ * either can race against a vcpu not being onlined yet (no pseudo-TLB ++ * allocated). Similarly, the TLB might be invalid. Skip those, as they ++ * obviously don't participate in the invalidation at this stage. ++ */ ++#define kvm_for_each_vncr_tlb(idx, vcpup, tlbp, kvm) \ ++ kvm_for_each_vcpu(idx, vcpup, kvm) \ ++ if (((tlbp) = vcpup->arch.vncr_tlb) && \ ++ (tlbp)->valid) ++ + static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end) + { + struct kvm_vcpu *vcpu; ++ struct vncr_tlb *vt; + unsigned long i; + + lockdep_assert_held_write(&kvm->mmu_lock); +@@ -914,24 +926,9 @@ static void kvm_invalidate_vncr_ipa(stru + if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY)) + return; + +- kvm_for_each_vcpu(i, vcpu, kvm) { +- struct vncr_tlb *vt = vcpu->arch.vncr_tlb; ++ kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { + u64 ipa_start, ipa_end, ipa_size; + +- /* +- * Careful here: We end-up here from an MMU notifier, +- * and this can race against a vcpu not being onlined +- * yet, without the pseudo-TLB being allocated. +- * +- * Skip those, as they obviously don't participate in +- * the invalidation at this stage. +- */ +- if (!vt) +- continue; +- +- if (!vt->valid) +- continue; +- + ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, + vt->wr.level)); + ipa_start = vt->wr.pa & ~(ipa_size - 1); +@@ -961,17 +958,14 @@ static void invalidate_vncr_va(struct kv + struct s1e2_tlbi_scope *scope) + { + struct kvm_vcpu *vcpu; ++ struct vncr_tlb *vt; + unsigned long i; + + lockdep_assert_held_write(&kvm->mmu_lock); + +- kvm_for_each_vcpu(i, vcpu, kvm) { +- struct vncr_tlb *vt = vcpu->arch.vncr_tlb; ++ kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { + u64 va_start, va_end, va_size; + +- if (!vt->valid) +- continue; +- + va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, + vt->wr.level)); + va_start = vt->gva & ~(va_size - 1); diff --git a/queue-7.1/kvm-guest_memfd-treat-memslot-binding-offset-size-as-unsigned-values.patch b/queue-7.1/kvm-guest_memfd-treat-memslot-binding-offset-size-as-unsigned-values.patch new file mode 100644 index 0000000000..196c6f3377 --- /dev/null +++ b/queue-7.1/kvm-guest_memfd-treat-memslot-binding-offset-size-as-unsigned-values.patch @@ -0,0 +1,126 @@ +From eba85fee7fc6cf28fec38a5bf3c378bef9a79ca6 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Tue, 2 Jun 2026 10:09:19 -0700 +Subject: KVM: guest_memfd: Treat memslot binding offset+size as unsigned values + +From: Sean Christopherson + +commit eba85fee7fc6cf28fec38a5bf3c378bef9a79ca6 upstream. + +When binding a memslot to a guest_memfd file, treat the offset and size as +unsigned values to fix a bug where the sum of the two can result in a false +negative when checking for overflow against the size of the file. Passing +unsigned values also avoids relying on somewhat obscure checks in other +flows for safety, and tracks the offset and size as they are intended to be +tracked, as unsigned values. + +On 64-bit kernels, the number of pages a memslot contains and thus the size +(and offset) of its guest_memfd binding are unsigned 64-bit values. Taking +the offset+size as an loff_t instead of a uoff_t inadvertently converts +the unsigned value to a signed value if the offset and/or size is massive. + +Locally storing the offset and size as signed values is benign in and of +itself (though even that is *extremely* difficult to discern), but +operating on their sum is not. + +For the offset, KVM explicitly checks against a negative value, which might +seem like a bug as KVM could incorrectly reject a legitimate binding, but +that's not actually the case as KVM_CREATE_GUEST_MEMFD takes a signed value +for its size, i.e. a would-be-negative offset is also greater than the +maximum possible size of any guest_memfd file. + +Regarding the size, while KVM lacks an explicit check for a negative value, +i.e. seemingly has a flawed overflow check, KVM restricts the number of +pages in a single memslot to the largest positive signed 32-bit value: + + if (id < KVM_USER_MEM_SLOTS && + (mem->memory_size >> PAGE_SHIFT) > KVM_MEM_MAX_NR_PAGES) + return -EINVAL; + +and so that maximum "size" will ever be is 0x7fffffff000. + +The sum of the two is, however, problematic. While the size is restricted +by KVM's memslot logic, the offset is not, i.e. the offset is completely +unchecked until the "offset + size > i_size_read(inode)" check. If the +offset is the (nearly) largest possible _positive_ value, then adding size +to the offset can result in a signed, negative 64-bit value. When compared +against the size of the file (guaranteed to be positive), the negative sum +is always smaller, and KVM incorrectly allows the absurd offset. + +Opportunistically add missing includes in kvm_mm.h (instead of relying on +its parents). + +Fixes: a7800aa80ea4 ("KVM: Add KVM_CREATE_GUEST_MEMFD ioctl() for guest-specific backing memory") +Cc: stable@vger.kernel.org +Cc: Ackerley Tng +Reviewed-by: Michael Roth +Reviewed-by: Ackerley Tng +Link: https://patch.msgid.link/20260602170921.1304394-2-seanjc@google.com +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + virt/kvm/guest_memfd.c | 8 ++++---- + virt/kvm/kvm_mm.h | 7 +++++-- + 2 files changed, 9 insertions(+), 6 deletions(-) + +--- a/virt/kvm/guest_memfd.c ++++ b/virt/kvm/guest_memfd.c +@@ -640,15 +640,16 @@ int kvm_gmem_create(struct kvm *kvm, str + } + + int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, +- unsigned int fd, loff_t offset) ++ unsigned int fd, uoff_t offset) + { +- loff_t size = slot->npages << PAGE_SHIFT; ++ uoff_t size = slot->npages << PAGE_SHIFT; + unsigned long start, end; + struct gmem_file *f; + struct inode *inode; + struct file *file; + int r = -EINVAL; + ++ BUILD_BUG_ON(sizeof(gpa_t) != sizeof(offset)); + BUILD_BUG_ON(sizeof(gfn_t) != sizeof(slot->gmem.pgoff)); + + file = fget(fd); +@@ -664,8 +665,7 @@ int kvm_gmem_bind(struct kvm *kvm, struc + + inode = file_inode(file); + +- if (offset < 0 || !PAGE_ALIGNED(offset) || +- offset + size > i_size_read(inode)) ++ if (!PAGE_ALIGNED(offset) || offset + size > i_size_read(inode)) + goto err; + + filemap_invalidate_lock(inode->i_mapping); +--- a/virt/kvm/kvm_mm.h ++++ b/virt/kvm/kvm_mm.h +@@ -3,6 +3,9 @@ + #ifndef __KVM_MM_H__ + #define __KVM_MM_H__ 1 + ++#include ++#include ++ + /* + * Architectures can choose whether to use an rwlock or spinlock + * for the mmu_lock. These macros, for use in common code +@@ -72,7 +75,7 @@ int kvm_gmem_init(struct module *module) + void kvm_gmem_exit(void); + int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args); + int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, +- unsigned int fd, loff_t offset); ++ unsigned int fd, uoff_t offset); + void kvm_gmem_unbind(struct kvm_memory_slot *slot); + #else + static inline int kvm_gmem_init(struct module *module) +@@ -82,7 +85,7 @@ static inline int kvm_gmem_init(struct m + static inline void kvm_gmem_exit(void) {}; + static inline int kvm_gmem_bind(struct kvm *kvm, + struct kvm_memory_slot *slot, +- unsigned int fd, loff_t offset) ++ unsigned int fd, uoff_t offset) + { + WARN_ON_ONCE(1); + return -EIO; diff --git a/queue-7.1/kvm-sev-don-t-terminate-snp-vms-on-vmgexit-without-a-registered-ghcb.patch b/queue-7.1/kvm-sev-don-t-terminate-snp-vms-on-vmgexit-without-a-registered-ghcb.patch new file mode 100644 index 0000000000..b69d33261f --- /dev/null +++ b/queue-7.1/kvm-sev-don-t-terminate-snp-vms-on-vmgexit-without-a-registered-ghcb.patch @@ -0,0 +1,47 @@ +From 1797e00bf802d64b859ea18505087daad92019c8 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Fri, 29 May 2026 20:35:43 +0200 +Subject: KVM: SEV: Don't terminate SNP VMs on #VMGEXIT without a registered GHCB + +From: Sean Christopherson + +commit 1797e00bf802d64b859ea18505087daad92019c8 upstream. + +If the guest attempts a non-MSR #VMGEXIT without the registered GHCB, +return a GHCB_HV_RESP_MALFORMED_INPUT+GHCB_ERR_NOT_REGISTERED error to the +guest instead of exiting KVM_RUN with -EINVAL (and in likelihood killing +the VM). KVM has already mapped the requested GHCB, i.e. can cleanly +report an error, and so exiting with -EINVAL is completely unjustified. + +Fixes: 0c76b1d08280 ("KVM: SEV: Add support to handle GHCB GPA register VMGEXIT") +Cc: stable@vger.kernel.org +Reviewed-by: Tom Lendacky +Reviewed-by: Michael Roth +Signed-off-by: Sean Christopherson +Message-ID: <20260501202250.2115252-19-seanjc@google.com> +Signed-off-by: Paolo Bonzini +Message-ID: <20260529183549.1104619-19-pbonzini@redhat.com> +Signed-off-by: Paolo Bonzini +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/svm/sev.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/arch/x86/kvm/svm/sev.c ++++ b/arch/x86/kvm/svm/sev.c +@@ -4522,9 +4522,12 @@ int sev_handle_vmgexit(struct kvm_vcpu * + sev_es_sync_from_ghcb(svm); + + /* SEV-SNP guest requires that the GHCB GPA must be registered */ +- if (is_sev_snp_guest(vcpu) && !ghcb_gpa_is_registered(svm, ghcb_gpa)) { +- vcpu_unimpl(&svm->vcpu, "vmgexit: GHCB GPA [%#llx] is not registered.\n", ghcb_gpa); +- return -EINVAL; ++ if (is_sev_snp_guest(vcpu) && ++ !ghcb_gpa_is_registered(svm, control->ghcb_gpa)) { ++ vcpu_unimpl(vcpu, "vmgexit: GHCB GPA [%#llx] is not registered.\n", ++ control->ghcb_gpa); ++ svm_vmgexit_bad_input(svm, GHCB_ERR_NOT_REGISTERED); ++ return 1; + } + + ret = sev_es_validate_vmgexit(svm); diff --git a/queue-7.1/kvm-sev-pin-source-page-for-write-when-adding-cpuid-data-for-snp-guest.patch b/queue-7.1/kvm-sev-pin-source-page-for-write-when-adding-cpuid-data-for-snp-guest.patch new file mode 100644 index 0000000000..dea5aed333 --- /dev/null +++ b/queue-7.1/kvm-sev-pin-source-page-for-write-when-adding-cpuid-data-for-snp-guest.patch @@ -0,0 +1,89 @@ +From f13e900599089b10113ceb36013423f0837c6792 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Fri, 22 May 2026 15:46:06 -0700 +Subject: KVM: SEV: Pin source page for write when adding CPUID data for SNP guest + +From: Sean Christopherson + +commit f13e900599089b10113ceb36013423f0837c6792 upstream. + +When populating a guest_memfd instance with the initial CPUID data for an +SNP guest, acquire a writable pin on the source page as KVM will write back +the "correct" CPUID information if the userspace provided data is rejected +by trusted firmware. Because KVM writes to the source page using a kernel +mapping, pinning for read could result in KVM clobbering read-only memory. + +Note, well-behaved VMMs are unlikely to be affected, as CPUID information +is almost always dynamically generated by userspace, i.e. it's unlikely for +the CPUID information to be backed by a read-only mapping. + +Fixes: 2a62345b30529 ("KVM: guest_memfd: GUP source pages prior to populating guest memory") +Cc: stable@vger.kernel.org +Signed-off-by: Ackerley Tng +Link: https://patch.msgid.link/20260522-fix-sev-gmem-post-populate-v2-1-3f196bfad5a1@google.com +[sean: rewrite shortlog and changelog, tag for stable@] +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/svm/sev.c | 1 + + arch/x86/kvm/vmx/tdx.c | 2 +- + include/linux/kvm_host.h | 3 ++- + virt/kvm/guest_memfd.c | 6 ++++-- + 4 files changed, 8 insertions(+), 4 deletions(-) + +--- a/arch/x86/kvm/svm/sev.c ++++ b/arch/x86/kvm/svm/sev.c +@@ -2470,6 +2470,7 @@ static int snp_launch_update(struct kvm + sev_populate_args.type = params.type; + + count = kvm_gmem_populate(kvm, params.gfn_start, src, npages, ++ params.type == KVM_SEV_SNP_PAGE_TYPE_CPUID, + sev_gmem_post_populate, &sev_populate_args); + if (count < 0) { + argp->error = sev_populate_args.fw_error; +--- a/arch/x86/kvm/vmx/tdx.c ++++ b/arch/x86/kvm/vmx/tdx.c +@@ -3185,7 +3185,7 @@ static int tdx_vcpu_init_mem_region(stru + }; + gmem_ret = kvm_gmem_populate(kvm, gpa_to_gfn(region.gpa), + u64_to_user_ptr(region.source_addr), +- 1, tdx_gmem_post_populate, &arg); ++ 1, false, tdx_gmem_post_populate, &arg); + if (gmem_ret < 0) { + ret = gmem_ret; + break; +--- a/include/linux/kvm_host.h ++++ b/include/linux/kvm_host.h +@@ -2601,7 +2601,8 @@ int kvm_arch_gmem_prepare(struct kvm *kv + typedef int (*kvm_gmem_populate_cb)(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, + struct page *page, void *opaque); + +-long kvm_gmem_populate(struct kvm *kvm, gfn_t gfn, void __user *src, long npages, ++long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, ++ long npages, bool may_writeback_src, + kvm_gmem_populate_cb post_populate, void *opaque); + #endif + +--- a/virt/kvm/guest_memfd.c ++++ b/virt/kvm/guest_memfd.c +@@ -858,7 +858,8 @@ out_unlock: + return ret; + } + +-long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long npages, ++long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, ++ long npages, bool may_writeback_src, + kvm_gmem_populate_cb post_populate, void *opaque) + { + struct kvm_memory_slot *slot; +@@ -892,8 +893,9 @@ long kvm_gmem_populate(struct kvm *kvm, + + if (src) { + unsigned long uaddr = (unsigned long)src + i * PAGE_SIZE; ++ unsigned int flags = may_writeback_src ? FOLL_WRITE : 0; + +- ret = get_user_pages_fast(uaddr, 1, 0, &src_page); ++ ret = get_user_pages_fast(uaddr, 1, flags, &src_page); + if (ret < 0) + break; + if (ret != 1) { diff --git a/queue-7.1/kvm-svm-disable-x2avic-rdmsr-interception-for-msrs-kvm-actually-supports.patch b/queue-7.1/kvm-svm-disable-x2avic-rdmsr-interception-for-msrs-kvm-actually-supports.patch new file mode 100644 index 0000000000..a431c37ef5 --- /dev/null +++ b/queue-7.1/kvm-svm-disable-x2avic-rdmsr-interception-for-msrs-kvm-actually-supports.patch @@ -0,0 +1,79 @@ +From 7f4b7092d9a173a4271e28c0ed1fc235994e309b Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Thu, 14 May 2026 14:31:14 -0700 +Subject: KVM: SVM: Disable x2AVIC RDMSR interception for MSRs KVM actually supports + +From: Sean Christopherson + +commit 7f4b7092d9a173a4271e28c0ed1fc235994e309b upstream. + +When toggling x2AVIC on/off, use KVM's curated mask of x2APIC MSRs that +can/should be passed through to the guest (or not) when 2AVIC is enabled. +Using the effective list provided by the local APIC emulation fixes +multiple (classes of) bugs, as the existing hand-coded list of MSRs is +wrong on multiple fronts: + + - ARBPRI isn't supported by KVM, isn't accelerated by AVIC (for read or + write), and its #VMEXIT is fault-like, i.e. requires decoding the + instruction. Disabling interception is nonsensical and suboptimal. + + - DFR and ICR2 aren't supported by x2APIC and so don't need their + intercepts disabled for performance reasons. While the #GP due to + x2APIC being abled has higher priority than the trap-like #VMEXIT, + disabling interception of unsupported MSRs is confusing and unnecessary. + + - RRR is completely unsupported. + + - AVIC currently fails to pass through the "range of vectors" registers, + IRR, ISR, and TMR, as e.g. X2APIC_MSR(APIC_IRR) only affects IRR0, and + thus only disables intercept for vectors 31:0 (which are the *least* + interesting registers). + + - TMCCT (the current APIC timer count) isn't accelerated by hardware, and + generates a fault-like AVIC_UNACCELERATED_ACCESS #VMEXIT, i.e. requires + KVM to decode the instruction to figure out what the guest was trying to + access. Note, the only reason this isn't a fatal bug is that the AVIC + architecture had the foresight to guard against buggy hypervisors. E.g. + if hardware simply read from the virtual APIC page, the guest would get + garbage (because the timer is emulated in software). + +Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") +Cc: stable@vger.kernel.org +Reviewed-by: Naveen N Rao (AMD) +Link: https://patch.msgid.link/20260514213115.1637082-3-seanjc@google.com +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/svm/avic.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/arch/x86/kvm/svm/avic.c ++++ b/arch/x86/kvm/svm/avic.c +@@ -122,6 +122,9 @@ static u32 x2avic_max_physical_id; + static void avic_set_x2apic_msr_interception(struct vcpu_svm *svm, + bool intercept) + { ++ struct kvm_vcpu *vcpu = &svm->vcpu; ++ u64 rd_regs; ++ + static const u32 x2avic_passthrough_msrs[] = { + X2APIC_MSR(APIC_ID), + X2APIC_MSR(APIC_LVR), +@@ -162,9 +165,15 @@ static void avic_set_x2apic_msr_intercep + if (!x2avic_enabled) + return; + ++ rd_regs = kvm_x2apic_disable_read_intercept_reg_mask(vcpu); ++ ++ for_each_set_bit(i, (unsigned long *)&rd_regs, BITS_PER_TYPE(rd_regs)) ++ svm_set_intercept_for_msr(vcpu, APIC_BASE_MSR + i, ++ MSR_TYPE_R, intercept); ++ + for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) +- svm_set_intercept_for_msr(&svm->vcpu, x2avic_passthrough_msrs[i], +- MSR_TYPE_RW, intercept); ++ svm_set_intercept_for_msr(vcpu, x2avic_passthrough_msrs[i], ++ MSR_TYPE_W, intercept); + + svm->x2avic_msrs_intercepted = intercept; + } diff --git a/queue-7.1/kvm-svm-only-disable-x2avic-wrmsr-interception-for-msrs-that-are-accelerated.patch b/queue-7.1/kvm-svm-only-disable-x2avic-wrmsr-interception-for-msrs-that-are-accelerated.patch new file mode 100644 index 0000000000..3e1dd07a66 --- /dev/null +++ b/queue-7.1/kvm-svm-only-disable-x2avic-wrmsr-interception-for-msrs-that-are-accelerated.patch @@ -0,0 +1,100 @@ +From 8c63179d975f2029c948ecce622f72af616dbff7 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Thu, 14 May 2026 14:31:15 -0700 +Subject: KVM: SVM: Only disable x2AVIC WRMSR interception for MSRs that are accelerated + +From: Sean Christopherson + +commit 8c63179d975f2029c948ecce622f72af616dbff7 upstream. + +When x2AVIC is enabled, disable WRMSR interception only for MSRs that are +actually accelerated by hardware. Disabling interception for MSRs that +aren't accelerated is functionally "fine", and in some cases a weird "win" +for performance, but only for cases that should never be triggered by a +well-behaved VM (writes to read-only registers; the #GP will typically +occur in the guest without taking a #VMEXIT, even for fault-like exits). + +But overall, disabling interception for MSRs that aren't accelerated is at +best confusing and unintuitive, and at worst introduces avoidable risk, as +the APM's documentation is imperfect and contradictory. The table in +"15.29.3.1 Virtual APIC Register Accesses" of simply states that such +writes generate exits, where as "Section 15.29.10 x2AVIC" says: + + x2APIC MSR intercept checks and access checks have higher priority than + AVIC access permission checks. + +CPU behavior follows the latter (which makes perfect sense), but all in +all there's simply no reason to disable interception just to make a #GP +faster. + +Note, the set of MSRs that are passed through for write is identical to +VMX's set when IPI virtualization is enabled. This is not a coincidence, +and is another motiviating factor for cleaning up the intercepts, as x2AVIC +is functionally equivalent to APICv+IPIv. + +Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") +Cc: stable@vger.kernel.org +Reviewed-by: Naveen N Rao (AMD) +Link: https://patch.msgid.link/20260514213115.1637082-4-seanjc@google.com +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/svm/avic.c | 40 ++++------------------------------------ + 1 file changed, 4 insertions(+), 36 deletions(-) + +--- a/arch/x86/kvm/svm/avic.c ++++ b/arch/x86/kvm/svm/avic.c +@@ -124,39 +124,6 @@ static void avic_set_x2apic_msr_intercep + { + struct kvm_vcpu *vcpu = &svm->vcpu; + u64 rd_regs; +- +- static const u32 x2avic_passthrough_msrs[] = { +- X2APIC_MSR(APIC_ID), +- X2APIC_MSR(APIC_LVR), +- X2APIC_MSR(APIC_TASKPRI), +- X2APIC_MSR(APIC_ARBPRI), +- X2APIC_MSR(APIC_PROCPRI), +- X2APIC_MSR(APIC_EOI), +- X2APIC_MSR(APIC_RRR), +- X2APIC_MSR(APIC_LDR), +- X2APIC_MSR(APIC_DFR), +- X2APIC_MSR(APIC_SPIV), +- X2APIC_MSR(APIC_ISR), +- X2APIC_MSR(APIC_TMR), +- X2APIC_MSR(APIC_IRR), +- X2APIC_MSR(APIC_ESR), +- X2APIC_MSR(APIC_ICR), +- X2APIC_MSR(APIC_ICR2), +- +- /* +- * Note! Always intercept LVTT, as TSC-deadline timer mode +- * isn't virtualized by hardware, and the CPU will generate a +- * #GP instead of a #VMEXIT. +- */ +- X2APIC_MSR(APIC_LVTTHMR), +- X2APIC_MSR(APIC_LVTPC), +- X2APIC_MSR(APIC_LVT0), +- X2APIC_MSR(APIC_LVT1), +- X2APIC_MSR(APIC_LVTERR), +- X2APIC_MSR(APIC_TMICT), +- X2APIC_MSR(APIC_TMCCT), +- X2APIC_MSR(APIC_TDCR), +- }; + int i; + + if (intercept == svm->x2avic_msrs_intercepted) +@@ -171,9 +138,10 @@ static void avic_set_x2apic_msr_intercep + svm_set_intercept_for_msr(vcpu, APIC_BASE_MSR + i, + MSR_TYPE_R, intercept); + +- for (i = 0; i < ARRAY_SIZE(x2avic_passthrough_msrs); i++) +- svm_set_intercept_for_msr(vcpu, x2avic_passthrough_msrs[i], +- MSR_TYPE_W, intercept); ++ svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TASKPRI), MSR_TYPE_W, intercept); ++ svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W, intercept); ++ svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W, intercept); ++ svm_set_intercept_for_msr(vcpu, X2APIC_MSR(APIC_ICR), MSR_TYPE_W, intercept); + + svm->x2avic_msrs_intercepted = intercept; + } diff --git a/queue-7.1/kvm-tdx-account-all-non-transient-page-allocations-for-per-td-structures.patch b/queue-7.1/kvm-tdx-account-all-non-transient-page-allocations-for-per-td-structures.patch new file mode 100644 index 0000000000..2e21991ab3 --- /dev/null +++ b/queue-7.1/kvm-tdx-account-all-non-transient-page-allocations-for-per-td-structures.patch @@ -0,0 +1,81 @@ +From a8b2924676ec42d76597f849baba31acc3ba1bfc Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Wed, 28 Jan 2026 17:14:35 -0800 +Subject: KVM: TDX: Account all non-transient page allocations for per-TD structures + +From: Sean Christopherson + +commit a8b2924676ec42d76597f849baba31acc3ba1bfc upstream. + +Account all non-transient allocations associated with a single TD (or its +vCPUs), as KVM's ABI is that allocations that are active for the lifetime +of a VM are accounted. Leave temporary allocations, i.e. allocations that +are freed within a single function/ioctl, unaccounted, to again align with +KVM's existing behavior, e.g. see commit dd103407ca31 ("KVM: X86: Remove +unnecessary GFP_KERNEL_ACCOUNT for temporary variables"). + +Fixes: 8d032b683c29 ("KVM: TDX: create/destroy VM structure") +Fixes: a50f673f25e0 ("KVM: TDX: Do TDX specific vcpu initialization") +Cc: stable@vger.kernel.org +Reviewed-by: Kai Huang +Reviewed-by: Rick Edgecombe +Link: https://patch.msgid.link/20260129011517.3545883-4-seanjc@google.com +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/vmx/tdx.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/arch/x86/kvm/vmx/tdx.c ++++ b/arch/x86/kvm/vmx/tdx.c +@@ -2387,20 +2387,20 @@ static int __tdx_td_init(struct kvm *kvm + + ret = -ENOMEM; + +- tdr_page = alloc_page(GFP_KERNEL); ++ tdr_page = alloc_page(GFP_KERNEL_ACCOUNT); + if (!tdr_page) + goto free_hkid; + + kvm_tdx->td.tdcs_nr_pages = tdx_sysinfo->td_ctrl.tdcs_base_size / PAGE_SIZE; + /* TDVPS = TDVPR(4K page) + TDCX(multiple 4K pages), -1 for TDVPR. */ + kvm_tdx->td.tdcx_nr_pages = tdx_sysinfo->td_ctrl.tdvps_base_size / PAGE_SIZE - 1; +- tdcs_pages = kzalloc_objs(*kvm_tdx->td.tdcs_pages, +- kvm_tdx->td.tdcs_nr_pages); ++ tdcs_pages = kzalloc_objs(*kvm_tdx->td.tdcs_pages, kvm_tdx->td.tdcs_nr_pages, ++ GFP_KERNEL_ACCOUNT); + if (!tdcs_pages) + goto free_tdr; + + for (i = 0; i < kvm_tdx->td.tdcs_nr_pages; i++) { +- tdcs_pages[i] = alloc_page(GFP_KERNEL); ++ tdcs_pages[i] = alloc_page(GFP_KERNEL_ACCOUNT); + if (!tdcs_pages[i]) + goto free_tdcs; + } +@@ -2875,7 +2875,7 @@ static int tdx_td_vcpu_init(struct kvm_v + int ret, i; + u64 err; + +- page = alloc_page(GFP_KERNEL); ++ page = alloc_page(GFP_KERNEL_ACCOUNT); + if (!page) + return -ENOMEM; + tdx->vp.tdvpr_page = page; +@@ -2888,14 +2888,14 @@ static int tdx_td_vcpu_init(struct kvm_v + tdx->vp.tdvpr_pa = page_to_phys(tdx->vp.tdvpr_page); + + tdx->vp.tdcx_pages = kcalloc(kvm_tdx->td.tdcx_nr_pages, sizeof(*tdx->vp.tdcx_pages), +- GFP_KERNEL); ++ GFP_KERNEL_ACCOUNT); + if (!tdx->vp.tdcx_pages) { + ret = -ENOMEM; + goto free_tdvpr; + } + + for (i = 0; i < kvm_tdx->td.tdcx_nr_pages; i++) { +- page = alloc_page(GFP_KERNEL); ++ page = alloc_page(GFP_KERNEL_ACCOUNT); + if (!page) { + ret = -ENOMEM; + goto free_tdcx; diff --git a/queue-7.1/kvm-vmx-handle-bad-values-on-proxied-writes-to-lbr-msrs.patch b/queue-7.1/kvm-vmx-handle-bad-values-on-proxied-writes-to-lbr-msrs.patch new file mode 100644 index 0000000000..a98368e5e1 --- /dev/null +++ b/queue-7.1/kvm-vmx-handle-bad-values-on-proxied-writes-to-lbr-msrs.patch @@ -0,0 +1,59 @@ +From ca674df13b195eb6d124ab059799d4e03fa40624 Mon Sep 17 00:00:00 2001 +From: Xuanqing Shi <1356292400@qq.com> +Date: Tue, 26 May 2026 19:26:17 -0700 +Subject: KVM: VMX: Handle bad values on proxied writes to LBR MSRs + +From: Xuanqing Shi <1356292400@qq.com> + +commit ca674df13b195eb6d124ab059799d4e03fa40624 upstream. + +Use the "safe" WRMSR API when writing LBRs on behalf of the guest (or host +userspace), and propagate any errors back to the instigator, as the value +being written is untrusted. E.g. if the guest (or host userspace) attempts +to set reserved bits in LBR_SELECT, then KVM needs to return an error, and +not WARN on the bad value. + +Continue using the "unsafe" version of RDMSR, as it should be impossible to +reach the helper with a completely bogus MSR, i.e. WARNing on RDMSR failure +is very desirable, e.g. to make KVM bugs more visible. + + unchecked MSR access error: WRMSR to 0x1c8 (tried to write 0x0000000000004000) + Call Trace: + intel_pmu_set_msr+0x4e0/0x7f0 [kvm_intel] + kvm_pmu_set_msr+0x17e/0x1c0 [kvm] + kvm_set_msr_common+0xc76/0x1440 [kvm] + vmx_set_msr+0x5e6/0x1570 [kvm_intel] + kvm_emulate_wrmsr+0x54/0x1d0 [kvm] + vmx_handle_exit+0x7fc/0x970 [kvm_intel] + +Fixes: 1b5ac3226a1a ("KVM: vmx/pmu: Pass-through LBR msrs when the guest LBR event is ACTIVE") +Cc: stable@vger.kernel.org +Signed-off-by: Xuanqing Shi <1356292400@qq.com> +[sean: rework changelog, only modify WRMSR path, tag for stable@] +Link: https://patch.msgid.link/20260527022617.3973884-1-seanjc@google.com +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/vmx/pmu_intel.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/arch/x86/kvm/vmx/pmu_intel.c ++++ b/arch/x86/kvm/vmx/pmu_intel.c +@@ -308,13 +308,15 @@ static bool intel_pmu_handle_lbr_msrs_ac + */ + local_irq_disable(); + if (lbr_desc->event->state == PERF_EVENT_STATE_ACTIVE) { ++ int err = 0; ++ + if (read) + rdmsrq(index, msr_info->data); + else +- wrmsrq(index, msr_info->data); ++ err = wrmsrq_safe(index, msr_info->data); + __set_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use); + local_irq_enable(); +- return true; ++ return !err; + } + clear_bit(INTEL_PMC_IDX_FIXED_VLBR, vcpu_to_pmu(vcpu)->pmc_in_use); + local_irq_enable(); diff --git a/queue-7.1/kvm-vmx-refresh-guest_pending_dbg_exceptions.bs-on-all-injected-dbs.patch b/queue-7.1/kvm-vmx-refresh-guest_pending_dbg_exceptions.bs-on-all-injected-dbs.patch new file mode 100644 index 0000000000..54af893256 --- /dev/null +++ b/queue-7.1/kvm-vmx-refresh-guest_pending_dbg_exceptions.bs-on-all-injected-dbs.patch @@ -0,0 +1,98 @@ +From c5bad4fa2d5dfd8c25140051a9807eba387a19b8 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Fri, 15 May 2026 15:26:29 -0700 +Subject: KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs + +From: Sean Christopherson + +commit c5bad4fa2d5dfd8c25140051a9807eba387a19b8 upstream. + +Move KVM's stuffing of GUEST_PENDING_DBG_EXCEPTIONS.BS when RFLAGS.TF=1 and +MOV/POP SS or STI blocking is active into the exception injection code so +that KVM fixes up the VMCS for all injected #DBs, not only those that are +reflected back into the guest after #DB interception. E.g. if KVM queues +a #DB in the emulator, or more importantly if userspace does save/restore +exactly on the #DB+shadow boundary, then KVM needs to massage the VMCS to +avoid the VM-Entry consistency check. + +Opportunistically update the wording of the comment to describe the +behavior as a workaround of flawed CPU behavior/architecture, to make it +clear that the *only* thing KVM is doing is fudging around a consistency +check. Per the SDM: + + There are no pending debug exceptions after VM entry if any of the + following are true: + + * The VM entry is vectoring with one of the following interruption + types: external interrupt, non-maskable interrupt (NMI), hardware + exception, or privileged software exception. + +I.e. forcing GUEST_PENDING_DBG_EXCEPTIONS.BS does *not* impact guest- +visible behavior. + +Fixes: b9bed78e2fa9 ("KVM: VMX: Set vmcs.PENDING_DBG.BS on #DB in STI/MOVSS blocking shadow") +Cc: stable@vger.kernel.org +Reported-by: Hou Wenlong +Closes: https://lore.kernel.org/all/b1a294bc9ed4dae532474a5dc6c8cb6e5962de7c.1757416809.git.houwenlong.hwl@antgroup.com +Reviewed-by: Hou Wenlong +Link: https://patch.msgid.link/20260515222638.1949982-2-seanjc@google.com +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/vmx/vmx.c | 35 ++++++++++++++++++----------------- + 1 file changed, 18 insertions(+), 17 deletions(-) + +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1908,6 +1908,24 @@ void vmx_inject_exception(struct kvm_vcp + u32 intr_info = ex->vector | INTR_INFO_VALID_MASK; + struct vcpu_vmx *vmx = to_vmx(vcpu); + ++ /* ++ * When injecting a #DB, single-stepping is enabled in RFLAGS, and STI ++ * or MOV-SS blocking is active, set vmcs.PENDING_DBG_EXCEPTIONS.BS to ++ * prevent a false positive from VM-Entry consistency check. VM-Entry ++ * asserts that a single-step #DB _must_ be pending in this scenario, ++ * as the previous instruction cannot have toggled RFLAGS.TF 0=>1 ++ * (because STI and POP/MOV don't modify RFLAGS), therefore the one ++ * instruction delay when activating single-step breakpoints must have ++ * already expired. However, the CPU isn't smart enough to peek at ++ * vmcs.VM_ENTRY_INTR_INFO_FIELD and so doesn't realize that yes, there ++ * is indeed a #DB pending/imminent. ++ */ ++ if (ex->vector == DB_VECTOR && ++ (vmx_get_rflags(vcpu) & X86_EFLAGS_TF) && ++ vmx_get_interrupt_shadow(vcpu)) ++ vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, ++ vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS); ++ + kvm_deliver_exception_payload(vcpu, ex); + + if (ex->has_error_code) { +@@ -5477,26 +5495,9 @@ static int handle_exception_nmi(struct k + * avoid single-step #DB and MTF updates, as ICEBP is + * higher priority. Note, skipping ICEBP still clears + * STI and MOVSS blocking. +- * +- * For all other #DBs, set vmcs.PENDING_DBG_EXCEPTIONS.BS +- * if single-step is enabled in RFLAGS and STI or MOVSS +- * blocking is active, as the CPU doesn't set the bit +- * on VM-Exit due to #DB interception. VM-Entry has a +- * consistency check that a single-step #DB is pending +- * in this scenario as the previous instruction cannot +- * have toggled RFLAGS.TF 0=>1 (because STI and POP/MOV +- * don't modify RFLAGS), therefore the one instruction +- * delay when activating single-step breakpoints must +- * have already expired. Note, the CPU sets/clears BS +- * as appropriate for all other VM-Exits types. + */ + if (is_icebp(intr_info)) + WARN_ON(!skip_emulated_instruction(vcpu)); +- else if ((vmx_get_rflags(vcpu) & X86_EFLAGS_TF) && +- (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & +- (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS))) +- vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, +- vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS); + + kvm_queue_exception_p(vcpu, DB_VECTOR, dr6); + return 1; diff --git a/queue-7.1/kvm-x86-add-dedicated-api-for-getting-mask-of-accelerated-x2apic-msrs.patch b/queue-7.1/kvm-x86-add-dedicated-api-for-getting-mask-of-accelerated-x2apic-msrs.patch new file mode 100644 index 0000000000..1829c6736c --- /dev/null +++ b/queue-7.1/kvm-x86-add-dedicated-api-for-getting-mask-of-accelerated-x2apic-msrs.patch @@ -0,0 +1,97 @@ +From 8ba621f335a519b47cb7d3e3f4f15b5101b3a56f Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Thu, 14 May 2026 14:31:13 -0700 +Subject: KVM: x86: Add dedicated API for getting mask of accelerated x2APIC MSRs + +From: Sean Christopherson + +commit 8ba621f335a519b47cb7d3e3f4f15b5101b3a56f upstream. + +Add a dedicated local APIC API, kvm_x2apic_disable_intercept_reg_mask(), +to provide the mask of x2APIC registers whose MSRs can and should be passed +through to the guest when x2APIC virtualization is enable, and use it in +lieu of the open-coded equivalent VMX logic. Providing a common helper +will allow sharing the logic with SVM (x2AVIC), and as a bonus eliminates +the somewhat confusing code where KVM enables interception for MSR_TYPE_RW, +even though only the READ case actually needs to be updated. + +No functional change intended. + +Cc: stable@vger.kernel.org +Reviewed-by: Naveen N Rao (AMD) +Link: https://patch.msgid.link/20260514213115.1637082-2-seanjc@google.com +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/lapic.c | 21 +++++++++++++++++++-- + arch/x86/kvm/lapic.h | 2 +- + arch/x86/kvm/vmx/vmx.c | 3 +-- + 3 files changed, 21 insertions(+), 5 deletions(-) + +--- a/arch/x86/kvm/lapic.c ++++ b/arch/x86/kvm/lapic.c +@@ -1732,7 +1732,7 @@ static inline struct kvm_lapic *to_lapic + #define APIC_REGS_MASK(first, count) \ + (APIC_REG_MASK(first) * ((1ull << (count)) - 1)) + +-u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) ++static u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic) + { + /* Leave bits '0' for reserved and write-only registers. */ + u64 valid_reg_mask = +@@ -1768,7 +1768,24 @@ u64 kvm_lapic_readable_reg_mask(struct k + + return valid_reg_mask; + } +-EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_lapic_readable_reg_mask); ++ ++u64 kvm_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu) ++{ ++ if (WARN_ON_ONCE(!lapic_in_kernel(vcpu))) ++ return 0; ++ ++ /* ++ * TMMCT, a.k.a. the current APIC timer count, reads aren't accelerated ++ * by hardware (Intel or AMD) as the timer is emulated in software (by ++ * KVM), i.e. reads from the virtual APIC page would return garbage. ++ * Intercept RDMSR, as handling the fault-like APIC-access VM-Exit is ++ * more expensive than handling a RDMSR VM-Exit (the APIC-access exit ++ * requires slow emulation of the code stream). ++ */ ++ return kvm_lapic_readable_reg_mask(vcpu->arch.apic) & ++ ~APIC_REG_MASK(APIC_TMCCT); ++} ++EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_x2apic_disable_read_intercept_reg_mask); + + static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len, + void *data) +--- a/arch/x86/kvm/lapic.h ++++ b/arch/x86/kvm/lapic.h +@@ -157,7 +157,7 @@ int kvm_hv_vapic_msr_read(struct kvm_vcp + int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len); + void kvm_lapic_exit(void); + +-u64 kvm_lapic_readable_reg_mask(struct kvm_lapic *apic); ++u64 kvm_x2apic_disable_read_intercept_reg_mask(struct kvm_vcpu *vcpu); + + static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic) + { +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -4159,7 +4159,7 @@ static void vmx_update_msr_bitmap_x2apic + * mode, only the current timer count needs on-demand emulation by KVM. + */ + if (mode & MSR_BITMAP_MODE_X2APIC_APICV) +- msr_bitmap[read_idx] = ~kvm_lapic_readable_reg_mask(vcpu->arch.apic); ++ msr_bitmap[read_idx] = ~kvm_x2apic_disable_read_intercept_reg_mask(vcpu); + else + msr_bitmap[read_idx] = ~0ull; + msr_bitmap[write_idx] = ~0ull; +@@ -4172,7 +4172,6 @@ static void vmx_update_msr_bitmap_x2apic + !(mode & MSR_BITMAP_MODE_X2APIC)); + + if (mode & MSR_BITMAP_MODE_X2APIC_APICV) { +- vmx_enable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TMCCT), MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W); + vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W); + if (enable_ipiv) diff --git a/queue-7.1/kvm-x86-ensure-vendor-s-exit-handler-runs-before-fastpath-userspace-exits.patch b/queue-7.1/kvm-x86-ensure-vendor-s-exit-handler-runs-before-fastpath-userspace-exits.patch new file mode 100644 index 0000000000..93a7009a04 --- /dev/null +++ b/queue-7.1/kvm-x86-ensure-vendor-s-exit-handler-runs-before-fastpath-userspace-exits.patch @@ -0,0 +1,69 @@ +From 0ffedf43910e44b76c2c1db4e9fbf12b268190c1 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Thu, 23 Apr 2026 09:26:27 -0700 +Subject: KVM: x86: Ensure vendor's exit handler runs before fastpath userspace exits + +From: Sean Christopherson + +commit 0ffedf43910e44b76c2c1db4e9fbf12b268190c1 upstream. + +Move the handling of fastpath userspace exits into vendor code to ensure +KVM runs vendor specific operations that need to run before userspace gains +control of the vCPU. E.g. for VMX (and soon to be for SVM as well), KVM +needs to flush the PML buffer prior to exiting to userspace, otherwise any +memory written by the final KVM_RUN might never be flagged as dirty. + +Note, waiting to snapshot CR0 and CR3 until svm_handle_exit() is flawed in +general, as that risks consuming stale state in a fastpath handler. That +will be addressed in a future change. + +Fixes: f7f39c50edb9 ("KVM: x86: Exit to userspace if fastpath triggers one on instruction skip") +Cc: stable@vger.kernel.org +Cc: Nikunj A. Dadhania +Reviewed-by: Nikunj A. Dadhania +Reviewed-by: Kai Huang +Link: https://patch.msgid.link/20260423162628.490962-2-seanjc@google.com +Signed-off-by: Sean Christopherson +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/svm/svm.c | 3 +++ + arch/x86/kvm/vmx/vmx.c | 3 +++ + arch/x86/kvm/x86.c | 3 --- + 3 files changed, 6 insertions(+), 3 deletions(-) + +--- a/arch/x86/kvm/svm/svm.c ++++ b/arch/x86/kvm/svm/svm.c +@@ -3663,6 +3663,9 @@ static int svm_handle_exit(struct kvm_vc + vcpu->arch.cr3 = svm->vmcb->save.cr3; + } + ++ if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) ++ return 0; ++ + if (is_guest_mode(vcpu)) { + int vmexit; + +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -6698,6 +6698,9 @@ static int __vmx_handle_exit(struct kvm_ + if (enable_pml && !is_guest_mode(vcpu)) + vmx_flush_pml_buffer(vcpu); + ++ if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) ++ return 0; ++ + /* + * KVM should never reach this point with a pending nested VM-Enter. + * More specifically, short-circuiting VM-Entry to emulate L2 due to +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -11578,9 +11578,6 @@ static int vcpu_enter_guest(struct kvm_v + if (vcpu->arch.apic_attention) + kvm_lapic_sync_from_vapic(vcpu); + +- if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE)) +- return 0; +- + r = kvm_x86_call(handle_exit)(vcpu, exit_fastpath); + return r; + diff --git a/queue-7.1/landlock-account-all-audit-data-allocations-to-user-space.patch b/queue-7.1/landlock-account-all-audit-data-allocations-to-user-space.patch new file mode 100644 index 0000000000..7482ce8b36 --- /dev/null +++ b/queue-7.1/landlock-account-all-audit-data-allocations-to-user-space.patch @@ -0,0 +1,79 @@ +From b232bd12789fa57405b5092f28788be97aae9999 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= +Date: Wed, 13 May 2026 20:03:08 +0200 +Subject: landlock: Account all audit data allocations to user space +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Mickaël Salaün + +commit b232bd12789fa57405b5092f28788be97aae9999 upstream. + +Mark the kzalloc_flex() of struct landlock_details with +GFP_KERNEL_ACCOUNT so the allocation is charged to the calling task, +like the other Landlock per-domain allocations which have used +GFP_KERNEL_ACCOUNT forever. + +Every property of landlock_details is caller-attributable: allocated by +landlock_restrict_self(2), owned by the caller's landlock_hierarchy, +contents are the caller's pid, uid, comm, and exe_path, lifetime bounded +by the caller's domain. While the caller may not know nor control the +size of this allocation (i.e. exe_path), this data should still be +accounted for it. + +The deciding factor is whether userspace can trigger the allocation, not +whether the size of the data is known nor controlled by the caller. +This aligns with the kmemcg accounting policy established by commit +5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg"). + +No new failure modes: the hierarchy and ruleset are allocated before +details and are already accounted, so landlock_restrict_self(2) already +returns -ENOMEM under memcg pressure. This change widens that existing +failure window slightly; it does not introduce a new error code. + +Cc: Günther Noack +Cc: Paul Moore +Cc: stable@vger.kernel.org +Fixes: 1d636984e088 ("landlock: Add AUDIT_LANDLOCK_DOMAIN and log domain status") +Link: https://patch.msgid.link/20260513180309.165840-1-mic@digikod.net +Signed-off-by: Mickaël Salaün +Signed-off-by: Greg Kroah-Hartman +--- + security/landlock/domain.c | 9 +++++---- + security/landlock/domain.h | 5 +---- + 2 files changed, 6 insertions(+), 8 deletions(-) + +--- a/security/landlock/domain.c ++++ b/security/landlock/domain.c +@@ -90,11 +90,12 @@ static struct landlock_details *get_curr + return ERR_CAST(buffer); + + /* +- * Create the new details according to the path's length. Do not +- * allocate with GFP_KERNEL_ACCOUNT because it is independent from the +- * caller. ++ * Create the new details according to the path's length. Account to ++ * the calling task's memcg, like the other Landlock per-domain ++ * allocations, even if it may not control the related size. + */ +- details = kzalloc_flex(*details, exe_path, path_size); ++ details = ++ kzalloc_flex(*details, exe_path, path_size, GFP_KERNEL_ACCOUNT); + if (!details) + return ERR_PTR(-ENOMEM); + +--- a/security/landlock/domain.h ++++ b/security/landlock/domain.h +@@ -33,10 +33,7 @@ enum landlock_log_status { + * Rarely accessed, mainly when logging the first domain's denial. + * + * The contained pointers are initialized at the domain creation time and never +- * changed again. Contrary to most other Landlock object types, this one is +- * not allocated with GFP_KERNEL_ACCOUNT because its size may not be under the +- * caller's control (e.g. unknown exe_path) and the data is not explicitly +- * requested nor used by tasks. ++ * changed again. + */ + struct landlock_details { + /** diff --git a/queue-7.1/landlock-set-audit_net.sk-for-socket-access-checks.patch b/queue-7.1/landlock-set-audit_net.sk-for-socket-access-checks.patch new file mode 100644 index 0000000000..35814a00b8 --- /dev/null +++ b/queue-7.1/landlock-set-audit_net.sk-for-socket-access-checks.patch @@ -0,0 +1,125 @@ +From d936e1a9170f9cadaa5f37586b1dfe6f20f98799 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= +Date: Fri, 12 Jun 2026 19:27:55 +0200 +Subject: landlock: Set audit_net.sk for socket access checks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Mickaël Salaün + +commit d936e1a9170f9cadaa5f37586b1dfe6f20f98799 upstream. + +Set audit_net.sk in current_check_access_socket() to provide the socket +object to audit_log_lsm_data(). This makes Landlock consistent with +AppArmor, which always sets .sk for socket operations, and with +SELinux's generic socket permission checks. + +The socket's local and foreign address information (laddr, lport, faddr, +fport) is logged by the shared lsm_audit.c infrastructure when the +socket has bound or connected state. Fields with zero values are +suppressed by print_ipv4_addr()/print_ipv6_addr(), so the audit output +is unchanged for the common case of bind denials on unbound sockets. +For connect denials after a prior bind, the bound local address (laddr, +lport) appears before the existing sockaddr fields (daddr, dest). + +No existing fields are removed or reordered, and the new field names +(laddr, lport, faddr, fport) are standard audit fields already emitted +by other LSMs through the same lsm_audit.c code path. + +Add a connect_tcp_bound audit test that binds to an allowed port and +then connects to a denied one, verifying that the denial record reports +laddr/lport from the bound socket in addition to the connect +destination. + +Cc: Günther Noack +Cc: Tingmao Wang +Cc: stable@vger.kernel.org +Fixes: 9f74411a40ce ("landlock: Log TCP bind and connect denials") +Link: https://patch.msgid.link/20260612172757.1003481-1-mic@digikod.net +Signed-off-by: Mickaël Salaün +Signed-off-by: Greg Kroah-Hartman +--- + security/landlock/net.c | 1 + tools/testing/selftests/landlock/net_test.c | 62 ++++++++++++++++++++++++++++ + 2 files changed, 63 insertions(+) + +--- a/security/landlock/net.c ++++ b/security/landlock/net.c +@@ -198,6 +198,7 @@ static int current_check_access_socket(s + return 0; + + audit_net.family = address->sa_family; ++ audit_net.sk = sock->sk; + landlock_log_denial(subject, + &(struct landlock_request){ + .type = LANDLOCK_REQUEST_NET_ACCESS, +--- a/tools/testing/selftests/landlock/net_test.c ++++ b/tools/testing/selftests/landlock/net_test.c +@@ -2026,4 +2026,66 @@ TEST_F(audit, connect) + EXPECT_EQ(0, close(sock_fd)); + } + ++static int matches_log_tcp_bound(int audit_fd, const char *const addr, ++ __u16 lport, __u16 dport) ++{ ++ static const char log_template[] = REGEX_LANDLOCK_PREFIX ++ " blockers=net\\.connect_tcp laddr=%s lport=%u daddr=%s dest=%u$"; ++ /* Slack for two addresses and two port numbers. */ ++ char log_match[sizeof(log_template) + 40]; ++ int log_match_len; ++ ++ log_match_len = snprintf(log_match, sizeof(log_match), log_template, ++ addr, lport, addr, dport); ++ if (log_match_len > sizeof(log_match)) ++ return -E2BIG; ++ ++ return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, ++ NULL); ++} ++ ++/* ++ * After a bind() to an allowed port, a denied connect must report laddr/lport ++ * from the bound socket (made available through audit_net.sk) in addition to ++ * the connect sockaddr's daddr/dest. ++ */ ++TEST_F(audit, connect_tcp_bound) ++{ ++ const struct landlock_ruleset_attr ruleset_attr = { ++ .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | ++ LANDLOCK_ACCESS_NET_CONNECT_TCP, ++ }; ++ const struct landlock_net_port_attr rule_bind = { ++ .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP, ++ .port = self->srv0.port, ++ }; ++ struct service_fixture srv_remote; ++ struct audit_records records; ++ int ruleset_fd, sock_fd; ++ ++ /* Uses a second port as the denied connect target. */ ++ ASSERT_EQ(0, set_service(&srv_remote, variant->prot, 1)); ++ ++ ruleset_fd = ++ landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); ++ ASSERT_LE(0, ruleset_fd); ++ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, ++ &rule_bind, 0)); ++ enforce_ruleset(_metadata, ruleset_fd); ++ EXPECT_EQ(0, close(ruleset_fd)); ++ ++ sock_fd = socket_variant(&self->srv0); ++ ASSERT_LE(0, sock_fd); ++ EXPECT_EQ(0, bind_variant(sock_fd, &self->srv0)); ++ EXPECT_EQ(-EACCES, connect_variant(sock_fd, &srv_remote)); ++ EXPECT_EQ(0, matches_log_tcp_bound(self->audit_fd, variant->addr, ++ self->srv0.port, srv_remote.port)); ++ ++ EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); ++ EXPECT_EQ(0, records.access); ++ EXPECT_EQ(1, records.domain); ++ ++ EXPECT_EQ(0, close(sock_fd)); ++} ++ + TEST_HARNESS_MAIN diff --git a/queue-7.1/loongarch-kvm-add-missing-slots_lock-for-device-register-unregister.patch b/queue-7.1/loongarch-kvm-add-missing-slots_lock-for-device-register-unregister.patch new file mode 100644 index 0000000000..162f932ba9 --- /dev/null +++ b/queue-7.1/loongarch-kvm-add-missing-slots_lock-for-device-register-unregister.patch @@ -0,0 +1,77 @@ +From aeded601d6aceb57cdda4b2701d2ee00c43a8b69 Mon Sep 17 00:00:00 2001 +From: Zeng Chi +Date: Thu, 11 Jun 2026 20:46:44 +0800 +Subject: LoongArch: KVM: Add missing slots_lock for device register/unregister + +From: Zeng Chi + +commit aeded601d6aceb57cdda4b2701d2ee00c43a8b69 upstream. + +kvm_io_bus_register_dev() and kvm_io_bus_unregister_dev() should be +called under kvm->slots_lock. The unregister calls in ipi.c, eiointc.c +and pch_pic.c were also missing this protection. Add it to match the +register side. + +Cc: stable@vger.kernel.org +Reviewed-by: Bibo Mao +Signed-off-by: Zeng Chi +Signed-off-by: Huacai Chen +Signed-off-by: Greg Kroah-Hartman +--- + arch/loongarch/kvm/intc/eiointc.c | 6 ++++++ + arch/loongarch/kvm/intc/ipi.c | 2 ++ + arch/loongarch/kvm/intc/pch_pic.c | 2 ++ + 3 files changed, 10 insertions(+) + +--- a/arch/loongarch/kvm/intc/eiointc.c ++++ b/arch/loongarch/kvm/intc/eiointc.c +@@ -645,10 +645,14 @@ static int kvm_eiointc_create(struct kvm + + device = &s->device_vext; + kvm_iodevice_init(device, &kvm_eiointc_virt_ops); ++ mutex_lock(&kvm->slots_lock); + ret = kvm_io_bus_register_dev(kvm, KVM_IOCSR_BUS, + EIOINTC_VIRT_BASE, EIOINTC_VIRT_SIZE, device); ++ mutex_unlock(&kvm->slots_lock); + if (ret < 0) { ++ mutex_lock(&kvm->slots_lock); + kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &s->device); ++ mutex_unlock(&kvm->slots_lock); + kfree(s); + return ret; + } +@@ -667,8 +671,10 @@ static void kvm_eiointc_destroy(struct k + + kvm = dev->kvm; + eiointc = kvm->arch.eiointc; ++ mutex_lock(&kvm->slots_lock); + kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device); + kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &eiointc->device_vext); ++ mutex_unlock(&kvm->slots_lock); + kfree(eiointc); + kfree(dev); + } +--- a/arch/loongarch/kvm/intc/ipi.c ++++ b/arch/loongarch/kvm/intc/ipi.c +@@ -447,7 +447,9 @@ static void kvm_ipi_destroy(struct kvm_d + + kvm = dev->kvm; + ipi = kvm->arch.ipi; ++ mutex_lock(&kvm->slots_lock); + kvm_io_bus_unregister_dev(kvm, KVM_IOCSR_BUS, &ipi->device); ++ mutex_unlock(&kvm->slots_lock); + kfree(ipi); + kfree(dev); + } +--- a/arch/loongarch/kvm/intc/pch_pic.c ++++ b/arch/loongarch/kvm/intc/pch_pic.c +@@ -481,7 +481,9 @@ static void kvm_pch_pic_destroy(struct k + kvm = dev->kvm; + s = kvm->arch.pch_pic; + /* unregister pch pic device and free it's memory */ ++ mutex_lock(&kvm->slots_lock); + kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &s->device); ++ mutex_unlock(&kvm->slots_lock); + kfree(s); + kfree(dev); + } diff --git a/queue-7.1/revert-svcrdma-use-contiguous-pages-for-rdma-read-sink-buffers.patch b/queue-7.1/revert-svcrdma-use-contiguous-pages-for-rdma-read-sink-buffers.patch new file mode 100644 index 0000000000..692b11bac7 --- /dev/null +++ b/queue-7.1/revert-svcrdma-use-contiguous-pages-for-rdma-read-sink-buffers.patch @@ -0,0 +1,289 @@ +From a39f0ce0c9da20986b429e2db3e4e8739035d61b Mon Sep 17 00:00:00 2001 +From: Chuck Lever +Date: Sat, 6 Jun 2026 22:24:56 -0400 +Subject: Revert "svcrdma: Use contiguous pages for RDMA Read sink buffers" + +From: Chuck Lever + +commit a39f0ce0c9da20986b429e2db3e4e8739035d61b upstream. + +Jonathan Flynn reports that commit 18755b8c2f24 ("svcrdma: Use +contiguous pages for RDMA Read sink buffers") regresses NFS/RDMA +WRITE throughput from 73.9 GiB/s to 30.3 GiB/s on a 128-core +single-NUMA-node server driving dual 400Gb/s links with 640 nfsd +threads. Server CPU utilization rises from 8.5% to 76%, with +roughly three quarters of all cycles spent spinning on zone->lock. + +The sink buffers are allocated as high-order page blocks, split +into single pages so each sub-page carries an independent refcount, +and later released one page at a time through folio batches. The +per-CPU page caches cannot satisfy an allocation stream whose alloc +order differs from its free order, so every sink buffer page makes +a round trip through the buddy allocator's free lists, serialized +on the zone lock of the single NUMA node. The rq_pages entries that +the split pages displace, bulk-allocated moments earlier by +svc_alloc_arg(), are freed without ever being used, doubling the +allocator traffic. + +The regression cannot be addressed trivially. Revert the commit +now; a reworked approach can return in an upcoming merge window. + +Reported-by: Jonathan Flynn +Reported-by: Mike Snitzer +Closes: https://lore.kernel.org/linux-nfs/aiHlPmeZq3WgMwoJ@kernel.org/ +Closes: https://lore.kernel.org/linux-nfs/3cb119b4b2a8aada30c0c60286778a54@mail.gmail.com/ +Fixes: 18755b8c2f24 ("svcrdma: Use contiguous pages for RDMA Read sink buffers") +Cc: stable@vger.kernel.org +Tested-by: Jonathan Flynn +Signed-off-by: Chuck Lever +Signed-off-by: Greg Kroah-Hartman +--- + net/sunrpc/xprtrdma/svc_rdma_rw.c | 223 -------------------------------------- + 1 file changed, 223 deletions(-) + +--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c ++++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c +@@ -754,216 +754,6 @@ int svc_rdma_prepare_reply_chunk(struct + return xdr->len; + } + +-/* +- * Cap contiguous RDMA Read sink allocations at order-4. +- * Higher orders risk allocation failure under +- * __GFP_NORETRY, which would negate the benefit of the +- * contiguous fast path. +- */ +-#define SVC_RDMA_CONTIG_MAX_ORDER 4 +- +-/** +- * svc_rdma_alloc_read_pages - Allocate physically contiguous pages +- * @nr_pages: number of pages needed +- * @order: on success, set to the allocation order +- * +- * Attempts a higher-order allocation, falling back to smaller orders. +- * The returned pages are split immediately so each sub-page has its +- * own refcount and can be freed independently. +- * +- * Returns a pointer to the first page on success, or NULL if even +- * order-1 allocation fails. +- */ +-static struct page * +-svc_rdma_alloc_read_pages(unsigned int nr_pages, unsigned int *order) +-{ +- unsigned int o; +- struct page *page; +- +- o = min(get_order(nr_pages << PAGE_SHIFT), +- SVC_RDMA_CONTIG_MAX_ORDER); +- +- while (o >= 1) { +- page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN, +- o); +- if (page) { +- split_page(page, o); +- *order = o; +- return page; +- } +- o--; +- } +- return NULL; +-} +- +-/* +- * svc_rdma_fill_contig_bvec - Replace rq_pages with a contiguous allocation +- * @rqstp: RPC transaction context +- * @head: context for ongoing I/O +- * @bv: bvec entry to fill +- * @pages_left: number of data pages remaining in the segment +- * @len_left: bytes remaining in the segment +- * +- * On success, fills @bv with a bvec spanning the contiguous range and +- * advances rc_curpage/rc_page_count. Returns the byte length covered, +- * or zero if the allocation failed or would overrun rq_maxpages. +- */ +-static unsigned int +-svc_rdma_fill_contig_bvec(struct svc_rqst *rqstp, +- struct svc_rdma_recv_ctxt *head, +- struct bio_vec *bv, unsigned int pages_left, +- unsigned int len_left) +-{ +- unsigned int order, npages, chunk_pages, chunk_len, i; +- struct page *page; +- +- page = svc_rdma_alloc_read_pages(pages_left, &order); +- if (!page) +- return 0; +- npages = 1 << order; +- +- if (head->rc_curpage + npages > rqstp->rq_maxpages) { +- for (i = 0; i < npages; i++) +- __free_page(page + i); +- return 0; +- } +- +- /* +- * Replace rq_pages[] entries with pages from the contiguous +- * allocation. If npages exceeds chunk_pages, the extra pages +- * stay in rq_pages[] for later reuse or normal rqst teardown. +- */ +- for (i = 0; i < npages; i++) { +- svc_rqst_page_release(rqstp, +- rqstp->rq_pages[head->rc_curpage + i]); +- rqstp->rq_pages[head->rc_curpage + i] = page + i; +- } +- +- chunk_pages = min(npages, pages_left); +- chunk_len = min_t(unsigned int, chunk_pages << PAGE_SHIFT, len_left); +- bvec_set_page(bv, page, chunk_len, 0); +- head->rc_page_count += chunk_pages; +- head->rc_curpage += chunk_pages; +- return chunk_len; +-} +- +-/* +- * svc_rdma_fill_page_bvec - Add a single rq_page to the bvec array +- * @head: context for ongoing I/O +- * @ctxt: R/W context whose bvec array is being filled +- * @cur: page to add +- * @bvec_idx: pointer to current bvec index, not advanced on merge +- * @len_left: bytes remaining in the segment +- * +- * If @cur is physically contiguous with the preceding bvec, it is +- * merged by extending that bvec's length. Otherwise a new bvec +- * entry is created. Returns the byte length covered. +- */ +-static unsigned int +-svc_rdma_fill_page_bvec(struct svc_rdma_recv_ctxt *head, +- struct svc_rdma_rw_ctxt *ctxt, struct page *cur, +- unsigned int *bvec_idx, unsigned int len_left) +-{ +- unsigned int chunk_len = min_t(unsigned int, PAGE_SIZE, len_left); +- +- head->rc_page_count++; +- head->rc_curpage++; +- +- if (*bvec_idx > 0) { +- struct bio_vec *prev = &ctxt->rw_bvec[*bvec_idx - 1]; +- +- if (page_to_phys(prev->bv_page) + prev->bv_offset + +- prev->bv_len == page_to_phys(cur)) { +- prev->bv_len += chunk_len; +- return chunk_len; +- } +- } +- +- bvec_set_page(&ctxt->rw_bvec[*bvec_idx], cur, chunk_len, 0); +- (*bvec_idx)++; +- return chunk_len; +-} +- +-/** +- * svc_rdma_build_read_segment_contig - Build RDMA Read WR with contiguous pages +- * @rqstp: RPC transaction context +- * @head: context for ongoing I/O +- * @segment: co-ordinates of remote memory to be read +- * +- * Greedily allocates higher-order pages to cover the segment, +- * building one bvec per contiguous chunk. Each allocation is +- * split so sub-pages have independent refcounts. When a +- * higher-order allocation fails, remaining pages are covered +- * individually, merging adjacent pages into the preceding bvec +- * when they are physically contiguous. The split sub-pages +- * replace entries in rq_pages[] so downstream cleanup is +- * unchanged. +- * +- * Returns: +- * %0: the Read WR was constructed successfully +- * %-ENOMEM: allocation failed +- * %-EIO: a DMA mapping error occurred +- */ +-static int svc_rdma_build_read_segment_contig(struct svc_rqst *rqstp, +- struct svc_rdma_recv_ctxt *head, +- const struct svc_rdma_segment *segment) +-{ +- struct svcxprt_rdma *rdma = svc_rdma_rqst_rdma(rqstp); +- struct svc_rdma_chunk_ctxt *cc = &head->rc_cc; +- unsigned int nr_data_pages, bvec_idx; +- struct svc_rdma_rw_ctxt *ctxt; +- unsigned int len_left; +- int ret; +- +- nr_data_pages = PAGE_ALIGN(segment->rs_length) >> PAGE_SHIFT; +- if (head->rc_curpage + nr_data_pages > rqstp->rq_maxpages) +- return -ENOMEM; +- +- ctxt = svc_rdma_get_rw_ctxt(rdma, nr_data_pages); +- if (!ctxt) +- return -ENOMEM; +- +- bvec_idx = 0; +- len_left = segment->rs_length; +- while (len_left) { +- unsigned int pages_left = PAGE_ALIGN(len_left) >> PAGE_SHIFT; +- unsigned int chunk_len = 0; +- +- if (pages_left >= 2) +- chunk_len = svc_rdma_fill_contig_bvec(rqstp, head, +- &ctxt->rw_bvec[bvec_idx], +- pages_left, len_left); +- if (chunk_len) { +- bvec_idx++; +- } else { +- struct page *cur = +- rqstp->rq_pages[head->rc_curpage]; +- chunk_len = svc_rdma_fill_page_bvec(head, ctxt, cur, +- &bvec_idx, +- len_left); +- } +- +- len_left -= chunk_len; +- } +- +- ctxt->rw_nents = bvec_idx; +- +- head->rc_pageoff = offset_in_page(segment->rs_length); +- if (head->rc_pageoff) +- head->rc_curpage--; +- +- ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset, +- segment->rs_handle, segment->rs_length, +- DMA_FROM_DEVICE); +- if (ret < 0) +- return -EIO; +- percpu_counter_inc(&svcrdma_stat_read); +- +- list_add(&ctxt->rw_list, &cc->cc_rwctxts); +- cc->cc_sqecount += ret; +- return 0; +-} +- + /** + * svc_rdma_build_read_segment - Build RDMA Read WQEs to pull one RDMA segment + * @rqstp: RPC transaction context +@@ -990,14 +780,6 @@ static int svc_rdma_build_read_segment(s + if (check_add_overflow(head->rc_pageoff, len, &total)) + return -EINVAL; + nr_bvec = PAGE_ALIGN(total) >> PAGE_SHIFT; +- +- if (head->rc_pageoff == 0 && nr_bvec >= 2) { +- ret = svc_rdma_build_read_segment_contig(rqstp, head, +- segment); +- if (ret != -ENOMEM) +- return ret; +- } +- + ctxt = svc_rdma_get_rw_ctxt(rdma, nr_bvec); + if (!ctxt) + return -ENOMEM; +@@ -1343,11 +1125,6 @@ static void svc_rdma_clear_rqst_pages(st + { + unsigned int i; + +- /* +- * Move only pages containing RPC data into rc_pages[]. Pages +- * from a contiguous allocation that were not used for the +- * payload remain in rq_pages[] for subsequent reuse. +- */ + for (i = 0; i < head->rc_page_count; i++) { + head->rc_pages[i] = rqstp->rq_pages[i]; + rqstp->rq_pages[i] = NULL; diff --git a/queue-7.1/selftests-landlock-explicitly-disable-audit-in-teardowns.patch b/queue-7.1/selftests-landlock-explicitly-disable-audit-in-teardowns.patch new file mode 100644 index 0000000000..c18e4acf3a --- /dev/null +++ b/queue-7.1/selftests-landlock-explicitly-disable-audit-in-teardowns.patch @@ -0,0 +1,107 @@ +From 0302cd72fe196aee933e3fb76f6d175d1ab0e843 Mon Sep 17 00:00:00 2001 +From: Maximilian Heyne +Date: Fri, 29 May 2026 20:03:41 +0000 +Subject: selftests/landlock: Explicitly disable audit in teardowns +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Maximilian Heyne + +commit 0302cd72fe196aee933e3fb76f6d175d1ab0e843 upstream. + +I'm seeing sporadic selftest failures, such as + + # RUN scoped_audit.connect_to_child ... + # scoped_abstract_unix_test.c:314:connect_to_child:Expected 0 (0) == records.access (8) + # connect_to_child: Test failed + # FAIL scoped_audit.connect_to_child + not ok 19 scoped_audit.connect_to_child + +This seems similar to what commit 3647a4977fb73d ("selftests/landlock: +Drain stale audit records on init") tried to fix. However, the added +drain loop is not effective. When setting the AUDIT_STATUS_PID, the +kauditd_thread is woken up starting to send messages from the hold queue +to the netlink. Depending on scheduling of this kthread not all messages +might be send via the netlink in the 1 us interval. + +Therefore, instead of trying to drain the queue, let's just disable +audit when running non-audit tests or more precisely disable it after +audit-tests. This way we won't generate any new audit message that could +interfere with the other tests. + +The comment saying that on process exit audit will be disabled is wrong. +The closed file descriptor just causes an auditd_reset(), not a +disablement. So future messages will be queued in the hold queue. + +Cc: stable@vger.kernel.org +Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") +Signed-off-by: Maximilian Heyne +Link: https://patch.msgid.link/20260529-welsh-nagoya-b4d9ca60@mheyne-amazon +[mic: Fix FD leak, update subject, call audit_cleanup() in audit_exec teardown] +Signed-off-by: Mickaël Salaün +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/landlock/audit.h | 21 ++++++++------------- + tools/testing/selftests/landlock/audit_test.c | 4 +--- + 2 files changed, 9 insertions(+), 16 deletions(-) + +--- a/tools/testing/selftests/landlock/audit.h ++++ b/tools/testing/selftests/landlock/audit.h +@@ -494,10 +494,9 @@ static int audit_init_filter_exe(struct + static int audit_cleanup(int audit_fd, struct audit_filter *filter) + { + struct audit_filter new_filter; ++ int err = 0; + + if (audit_fd < 0 || !filter) { +- int err; +- + /* + * Simulates audit_init_with_exe_filter() when called from + * FIXTURE_TEARDOWN_PARENT(). +@@ -508,23 +507,19 @@ static int audit_cleanup(int audit_fd, s + + filter = &new_filter; + err = audit_init_filter_exe(filter, NULL); +- if (err) { +- close(audit_fd); +- return err; +- } ++ if (err) ++ goto err_close; + } + + /* Filters might not be in place. */ + audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE); + audit_filter_drop(audit_fd, AUDIT_DEL_RULE); + +- /* +- * Because audit_cleanup() might not be called by the test auditd +- * process, it might not be possible to explicitly set it. Anyway, +- * AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd +- * process will exit. +- */ +- return close(audit_fd); ++ err = audit_set_status(audit_fd, AUDIT_STATUS_ENABLED, 0); ++ ++err_close: ++ close(audit_fd); ++ return err; + } + + static int audit_init_with_exe_filter(struct audit_filter *filter) +--- a/tools/testing/selftests/landlock/audit_test.c ++++ b/tools/testing/selftests/landlock/audit_test.c +@@ -849,10 +849,8 @@ FIXTURE_SETUP(audit_exec) + FIXTURE_TEARDOWN(audit_exec) + { + set_cap(_metadata, CAP_AUDIT_CONTROL); +- EXPECT_EQ(0, audit_filter_exe(self->audit_fd, &self->audit_filter, +- AUDIT_DEL_RULE)); ++ EXPECT_EQ(0, audit_cleanup(self->audit_fd, &self->audit_filter)); + clear_cap(_metadata, CAP_AUDIT_CONTROL); +- EXPECT_EQ(0, close(self->audit_fd)); + } + + TEST_F(audit_exec, signal_and_open) diff --git a/queue-7.1/selftests-landlock-filter-dealloc-records-in-audit_count_records.patch b/queue-7.1/selftests-landlock-filter-dealloc-records-in-audit_count_records.patch new file mode 100644 index 0000000000..c12b242839 --- /dev/null +++ b/queue-7.1/selftests-landlock-filter-dealloc-records-in-audit_count_records.patch @@ -0,0 +1,161 @@ +From 26679fad81a471428707d2dd7b0418204c52b7e4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= +Date: Wed, 13 May 2026 12:51:08 +0200 +Subject: selftests/landlock: Filter dealloc records in audit_count_records() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Mickaël Salaün + +commit 26679fad81a471428707d2dd7b0418204c52b7e4 upstream. + +audit_count_records() counts both AUDIT_LANDLOCK_DOMAIN allocation and +deallocation records in records.domain . Domain deallocation is tied to +asynchronous credential freeing via kworker threads +(landlock_put_ruleset_deferred), so the dealloc record can arrive after +the drain in audit_init() and after the preceding audit_match_record() +call. This causes flaky failures in tests that assert an exact +records.domain count: a stale dealloc record from a previous test's +domain inflates the count by one. + +Observed on x86_64 under build configurations that delay the kworker +firing the dealloc callback (e.g. coverage instrumentation): the +audit_layout1 tests in fs_test.c intermittently saw records.domain == 2 +where 1 was expected. The fix is in the shared helper, so those +existing checks become robust without needing a fs_test.c edit. + +Filter audit_count_records() with a regex to skip records containing +deallocation status. The remaining domain records (allocation, emitted +synchronously during landlock_log_denial()) are deterministic. +Deallocation records are already tested explicitly via +matches_log_domain_deallocated() in audit_test.c, which uses its own +domain-ID-based filtering and longer timeout. + +With this filter in place, re-add the records.domain == 0 checks that +were removed in commit 3647a4977fb7 ("selftests/landlock: Drain stale +audit records on init") as a workaround for this race. + +Cc: Günther Noack +Cc: stable@vger.kernel.org +Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()") +Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") +Tested-by: Günther Noack +Link: https://patch.msgid.link/20260513105112.140137-1-mic@digikod.net +Signed-off-by: Mickaël Salaün +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/landlock/audit.h | 39 +++++++---- + tools/testing/selftests/landlock/audit_test.c | 2 + tools/testing/selftests/landlock/ptrace_test.c | 1 + tools/testing/selftests/landlock/scoped_abstract_unix_test.c | 1 + 4 files changed, 30 insertions(+), 13 deletions(-) + +--- a/tools/testing/selftests/landlock/audit.h ++++ b/tools/testing/selftests/landlock/audit.h +@@ -381,18 +381,24 @@ struct audit_records { + }; + + /* +- * WARNING: Do not assert records.domain == 0 without a preceding +- * audit_match_record() call. Domain deallocation records are emitted +- * asynchronously from kworker threads and can arrive after the drain in +- * audit_init(), corrupting the domain count. A preceding audit_match_record() +- * call consumes stale records while scanning, making the assertion safe in +- * practice because stale deallocation records arrive before the expected access +- * records. ++ * Counts remaining audit records by type, skipping domain deallocation records. ++ * Deallocation records are emitted asynchronously from kworker threads after a ++ * previous test's child has exited, so they can arrive after the drain in ++ * audit_init() and after the preceding audit_match_record() call. Allocation ++ * records are emitted synchronously during landlock_log_denial() in the current ++ * test's syscall context, so only those are counted in records->domain. + */ + static int audit_count_records(int audit_fd, struct audit_records *records) + { ++ static const char dealloc_pattern[] = REGEX_LANDLOCK_PREFIX ++ " status=deallocated "; + struct audit_message msg; +- int err; ++ regex_t dealloc_re; ++ int ret, err = 0; ++ ++ ret = regcomp(&dealloc_re, dealloc_pattern, 0); ++ if (ret) ++ return -ENOMEM; + + records->access = 0; + records->domain = 0; +@@ -402,9 +408,8 @@ static int audit_count_records(int audit + err = audit_recv(audit_fd, &msg); + if (err) { + if (err == -EAGAIN) +- return 0; +- else +- return err; ++ err = 0; ++ break; + } + + switch (msg.header.nlmsg_type) { +@@ -412,12 +417,20 @@ static int audit_count_records(int audit + records->access++; + break; + case AUDIT_LANDLOCK_DOMAIN: +- records->domain++; ++ ret = regexec(&dealloc_re, msg.data, 0, NULL, 0); ++ if (ret == REG_NOMATCH) { ++ records->domain++; ++ } else if (ret != 0) { ++ err = -EIO; ++ goto out; ++ } + break; + } + } while (true); + +- return 0; ++out: ++ regfree(&dealloc_re); ++ return err; + } + + static int audit_init(void) +--- a/tools/testing/selftests/landlock/audit_test.c ++++ b/tools/testing/selftests/landlock/audit_test.c +@@ -730,6 +730,7 @@ TEST_F(audit_flags, signal) + } else { + EXPECT_EQ(1, records.access); + } ++ EXPECT_EQ(0, records.domain); + + /* Updates filter rules to match the drop record. */ + set_cap(_metadata, CAP_AUDIT_CONTROL); +@@ -915,6 +916,7 @@ TEST_F(audit_exec, signal_and_open) + /* Tests that there was no denial until now. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); ++ EXPECT_EQ(0, records.domain); + + /* + * Wait for the child to do a first denied action by layer1 and +--- a/tools/testing/selftests/landlock/ptrace_test.c ++++ b/tools/testing/selftests/landlock/ptrace_test.c +@@ -342,6 +342,7 @@ TEST_F(audit, trace) + /* Makes sure there is no superfluous logged records. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); ++ EXPECT_EQ(0, records.domain); + + yama_ptrace_scope = get_yama_ptrace_scope(); + ASSERT_LE(0, yama_ptrace_scope); +--- a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c ++++ b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c +@@ -312,6 +312,7 @@ TEST_F(scoped_audit, connect_to_child) + /* Makes sure there is no superfluous logged records. */ + EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); + EXPECT_EQ(0, records.access); ++ EXPECT_EQ(0, records.domain); + + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); diff --git a/queue-7.1/selftests-landlock-increase-default-audit-socket-timeout.patch b/queue-7.1/selftests-landlock-increase-default-audit-socket-timeout.patch new file mode 100644 index 0000000000..3c1b891ec3 --- /dev/null +++ b/queue-7.1/selftests-landlock-increase-default-audit-socket-timeout.patch @@ -0,0 +1,213 @@ +From d8dfb4c7faa87c3e41a8678f38f136c2c7c036fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= +Date: Wed, 13 May 2026 12:51:09 +0200 +Subject: selftests/landlock: Increase default audit socket timeout +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Mickaël Salaün + +commit d8dfb4c7faa87c3e41a8678f38f136c2c7c036fa upstream. + +matches_log_fs() and other audit_match_record() callers intermittently +return -EAGAIN under heavy debug configs (KASAN, lockdep). The audit +record delivery pipeline is asynchronous: landlock_log_denial() queues +the record to audit_queue, and kauditd_thread dequeues and delivers via +netlink. Under debug configs, kauditd scheduling between +audit_log_end() and netlink_unicast() can exceed a syscall round trip +(more than 1 usec), which was the value of the socket timeout used for +the recvfrom() calls. + +The observed failure [1] is an EAGAIN error code (-11) which means that +the access record had not arrived within the 1 usec timeout of +recvfrom(). The expected record does arrive, but only after +matches_log_fs() has already returned. It is then consumed by a later +audit_count_records() call, making records.access == 1 instead of 0. + +Switch the default socket timeout to the slow value (1 second) so all +audit_match_record() callers wait long enough for kauditd delivery, and +lower it to the fast value (1 usec) only on the two paths that expect no +record: audit_count_records() and the expected_domain_id == 0 probe in +matches_log_domain_deallocated(). audit_init() drains stale records +with the fast timeout (terminating on -EAGAIN once the backlog is empty) +and switches to the patient default before returning. 1 second gives +~10x margin over the observed maximum (~100 ms, while the happy path is +~23 us). + +Rename the timeval constants to reflect their new roles: +- audit_tv_dom_drop (1 second) -> audit_tv_default: default socket + timeout, patient enough for asynchronous kauditd delivery. +- audit_tv_default (1 usec) -> audit_tv_fast: fast timeout for paths + that expect no record (drain, audit_count_records(), probes). + +Invert the conditional in matches_log_domain_deallocated(). Check +setsockopt returns on both the lower and restore paths; preserve the +first error via !err when the restore fails after a prior error so the +actionable return code is not masked by a bookkeeping failure. + +Cc: Günther Noack +Cc: Thomas Weißschuh +Cc: stable@vger.kernel.org +Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()") +Fixes: 6a500b22971c ("selftests/landlock: Add tests for audit flags and domain IDs") +Reported-by: Günther Noack +Closes: https://lore.kernel.org/r/20260402.eb5c4e85f472@gnoack.org [1] +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-lkp/202605111649.a8b30a62-lkp@intel.com +Closes: https://lore.kernel.org/oe-lkp/202604300436.a07fae12-lkp@intel.com +Tested-by: Günther Noack +Link: https://patch.msgid.link/20260513105112.140137-2-mic@digikod.net +Signed-off-by: Mickaël Salaün +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/landlock/audit.h | 80 ++++++++++++++++++++++++------- + 1 file changed, 63 insertions(+), 17 deletions(-) + +--- a/tools/testing/selftests/landlock/audit.h ++++ b/tools/testing/selftests/landlock/audit.h +@@ -45,17 +45,25 @@ struct audit_message { + }; + }; + +-static const struct timeval audit_tv_dom_drop = { ++static const struct timeval audit_tv_default = { + /* +- * Because domain deallocation is tied to asynchronous credential +- * freeing, receiving such event may take some time. In practice, +- * on a small VM, it should not exceed 100k usec, but let's wait up +- * to 1 second to be safe. ++ * Default socket timeout for audit_match_record() callers that expect a ++ * record to arrive. Asynchronous kauditd delivery can exceed 1 usec ++ * under heavy debug configs (KASAN, lockdep), where kauditd_thread ++ * scheduling between audit_log_end() and netlink_unicast() takes longer ++ * than the previous 1 usec timeout. 1 second is a generous ceiling: on ++ * the happy path, kauditd delivers within dozens of usec. + */ + .tv_sec = 1, + }; + +-static const struct timeval audit_tv_default = { ++static const struct timeval audit_tv_fast = { ++ /* ++ * Fast timeout for paths that expect no record (audit_init() drain, ++ * audit_count_records(), probes). Causes audit_recv() to return ++ * -EAGAIN once the socket buffer is empty, naturally terminating the ++ * read loop. ++ */ + .tv_usec = 1, + }; + +@@ -334,8 +342,13 @@ static int __maybe_unused matches_log_do + * Matches a domain deallocation record. When expected_domain_id is non-zero, + * the pattern includes the specific domain ID so that stale deallocation + * records from a previous test (with a different domain ID) are skipped by +- * audit_match_record(), and the socket timeout is temporarily increased to +- * audit_tv_dom_drop to wait for the asynchronous kworker deallocation. ++ * audit_match_record(), waiting for the asynchronous kworker deallocation with ++ * the default patient timeout. ++ * ++ * When expected_domain_id is zero, the caller is probing for any dealloc record ++ * that may or may not arrive. Temporarily lowers the socket timeout to ++ * audit_tv_fast for this probe so it returns promptly when no record is ++ * pending; restores audit_tv_default after. + */ + static int __maybe_unused + matches_log_domain_deallocated(int audit_fd, unsigned int num_denials, +@@ -361,16 +374,21 @@ matches_log_domain_deallocated(int audit + if (log_match_len >= sizeof(log_match)) + return -E2BIG; + +- if (expected_domain_id) +- setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, +- &audit_tv_dom_drop, sizeof(audit_tv_dom_drop)); ++ if (!expected_domain_id) { ++ if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, ++ &audit_tv_fast, sizeof(audit_tv_fast))) ++ return -errno; ++ } + + err = audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match, + domain_id); + +- if (expected_domain_id) +- setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, +- sizeof(audit_tv_default)); ++ if (!expected_domain_id) { ++ if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, ++ &audit_tv_default, sizeof(audit_tv_default)) && ++ !err) ++ err = -errno; ++ } + + return err; + } +@@ -387,6 +405,11 @@ struct audit_records { + * audit_init() and after the preceding audit_match_record() call. Allocation + * records are emitted synchronously during landlock_log_denial() in the current + * test's syscall context, so only those are counted in records->domain. ++ * ++ * Temporarily lowers SO_RCVTIMEO to audit_tv_fast for the read loop: this is a ++ * "no record expected" path that should terminate on the first -EAGAIN. The ++ * default patient timeout is restored on exit for subsequent ++ * audit_match_record() callers. + */ + static int audit_count_records(int audit_fd, struct audit_records *records) + { +@@ -403,6 +426,12 @@ static int audit_count_records(int audit + records->access = 0; + records->domain = 0; + ++ if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_fast, ++ sizeof(audit_tv_fast))) { ++ err = -errno; ++ goto out; ++ } ++ + do { + memset(&msg, 0, sizeof(msg)); + err = audit_recv(audit_fd, &msg); +@@ -429,6 +458,10 @@ static int audit_count_records(int audit + } while (true); + + out: ++ if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, ++ sizeof(audit_tv_default)) && ++ !err) ++ err = -errno; + regfree(&dealloc_re); + return err; + } +@@ -449,9 +482,9 @@ static int audit_init(void) + if (err) + goto err_close; + +- /* Sets a timeout for negative tests. */ +- err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, +- sizeof(audit_tv_default)); ++ /* Uses the fast timeout to drain stale records below. */ ++ err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_fast, ++ sizeof(audit_tv_fast)); + if (err) { + err = -errno; + goto err_close; +@@ -467,6 +500,19 @@ static int audit_init(void) + while (audit_recv(fd, NULL) == 0) + ; + ++ /* ++ * Restores the default timeout for audit_match_record() callers that ++ * expect a record to arrive. Paths that expect no record restore the ++ * fast timeout locally (audit_count_records(), the expected_domain_id ++ * == 0 probe in matches_log_domain_deallocated()). ++ */ ++ err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default, ++ sizeof(audit_tv_default)); ++ if (err) { ++ err = -errno; ++ goto err_close; ++ } ++ + return fd; + + err_close: diff --git a/queue-7.1/series b/queue-7.1/series index 3f686228f6..d60aab4c9e 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -428,3 +428,42 @@ selftests-mm-pagemap_ioctl-use-the-correct-page-size-for-transact_test.patch selftests-mm-fix-ksft_process_madv.sh-test-category.patch nouveau-vmm-fix-another-spt-lpt-race.patch bpf-reject-bpf_map_type_inode_storage-creation-if-bpf-lsm-is-uninitialized.patch +iommu-vt-d-avoid-warning-in-sva-unbind-path.patch +iommu-amd-don-t-split-flush-for-amd_iommu_domain_flush_all.patch +iommufd-use-sizeof-hdr-instead-of-sizeof-hdr-in-veventq-read.patch +iommufd-fix-data_len-byte-count-vs-element-count-mismatch.patch +iommufd-move-vevent-memory-allocation-outside-spinlock.patch +iommufd-set-veventq_depth-upper-bound.patch +iommufd-rewind-header-length-in-done-if-iommufd_veventq_fops_read-fails.patch +iommufd-reject-invalid-read-count-in-iommufd_veventq_fops_read.patch +iommufd-propagate-allocation-failure-in-iommufd_veventq_deliver_fetch.patch +iommufd-reject-invalid-read-count-in-iommufd_fault_fops_read.patch +iommufd-break-the-loop-on-failure-in-iommufd_fault_fops_read.patch +iommufd-avoid-partial-fault-group-delivery-in-iommufd_fault_fops_read.patch +iommufd-set-upper-bounds-on-cache-invalidation-entry_num-and-entry_len.patch +audit-fix-removal-of-dangling-executable-rules.patch +landlock-set-audit_net.sk-for-socket-access-checks.patch +selftests-landlock-explicitly-disable-audit-in-teardowns.patch +landlock-account-all-audit-data-allocations-to-user-space.patch +selftests-landlock-filter-dealloc-records-in-audit_count_records.patch +selftests-landlock-increase-default-audit-socket-timeout.patch +kvm-arm64-nv-avoid-dereferencing-null-vncr-pseudo-tlb.patch +loongarch-kvm-add-missing-slots_lock-for-device-register-unregister.patch +kvm-arm64-bound-used_lrs-when-flushing-the-pkvm-hyp-vcpu.patch +kvm-arm64-clear-__hyp_running_vcpu-when-flushing-the-pkvm-hyp-vcpu.patch +kvm-sev-pin-source-page-for-write-when-adding-cpuid-data-for-snp-guest.patch +kvm-svm-disable-x2avic-rdmsr-interception-for-msrs-kvm-actually-supports.patch +kvm-svm-only-disable-x2avic-wrmsr-interception-for-msrs-that-are-accelerated.patch +kvm-vmx-refresh-guest_pending_dbg_exceptions.bs-on-all-injected-dbs.patch +kvm-sev-don-t-terminate-snp-vms-on-vmgexit-without-a-registered-ghcb.patch +kvm-vmx-handle-bad-values-on-proxied-writes-to-lbr-msrs.patch +kvm-tdx-account-all-non-transient-page-allocations-for-per-td-structures.patch +kvm-x86-ensure-vendor-s-exit-handler-runs-before-fastpath-userspace-exits.patch +kvm-guest_memfd-treat-memslot-binding-offset-size-as-unsigned-values.patch +kvm-x86-add-dedicated-api-for-getting-mask-of-accelerated-x2apic-msrs.patch +kvm-arm64-don-t-leak-pfn-when-kvm_translate_vncr-races-mmu-notifier.patch +udmabuf-fix-dma-direction-mismatch-in-release_udmabuf.patch +dma-buf-udmabuf-skip-redundant-cpu-sync-to-fix-cacheline-eexist-warning.patch +svcrdma-wake-sq-waiters-when-the-transport-closes.patch +revert-svcrdma-use-contiguous-pages-for-rdma-read-sink-buffers.patch +fpga-dfl-afu-validate-dma-mapping-length-in-afu_dma_map_region.patch diff --git a/queue-7.1/svcrdma-wake-sq-waiters-when-the-transport-closes.patch b/queue-7.1/svcrdma-wake-sq-waiters-when-the-transport-closes.patch new file mode 100644 index 0000000000..9267ddfb1e --- /dev/null +++ b/queue-7.1/svcrdma-wake-sq-waiters-when-the-transport-closes.patch @@ -0,0 +1,216 @@ +From e5248a7426030db1e126363f72afdb3b71339a5c Mon Sep 17 00:00:00 2001 +From: Chuck Lever +Date: Fri, 22 May 2026 14:13:57 -0400 +Subject: svcrdma: wake sq waiters when the transport closes + +From: Chuck Lever + +commit e5248a7426030db1e126363f72afdb3b71339a5c upstream. + +Threads parked in svc_rdma_sq_wait() on sc_sq_ticket_wait or +sc_send_wait can hang indefinitely in TASK_UNINTERRUPTIBLE state +across transport teardown, pinning svc_xprt references and +blocking svc_rdma_free(). + +The close path sets XPT_CLOSE before invoking xpo_detach and both +wait_event predicates include an XPT_CLOSE term, but the +predicates are re-evaluated only on wakeup. sc_sq_ticket_wait has +no completion-driven wake path; it is advanced solely by the +chained ticket handoff inside svc_rdma_sq_wait() itself. Without +an explicit wake at close, parked threads never observe +XPT_CLOSE, hold their svc_xprt_get reference forever, and +svc_rdma_free() blocks on xpt_ref dropping to zero. + +Two close entry points reach this transport. Local teardown runs +svc_rdma_detach() from svc_handle_xprt() -> svc_delete_xprt() -> +xpo_detach() on a worker thread. A remote disconnect arrives at +svc_rdma_cma_handler(), which calls svc_xprt_deferred_close(): +that sets XPT_CLOSE and enqueues the transport but does not +access either RDMA waitqueue, so a worker already parked in +svc_rdma_sq_wait() never re-evaluates its predicate. With every +worker parked on this transport, no thread is available to run +the local teardown either, and the wake site there is +unreachable. + +Introduce svc_rdma_xprt_deferred_close(), a thin svcrdma wrapper +that calls svc_xprt_deferred_close() and then wakes both +sc_sq_ticket_wait and sc_send_wait. Convert the svcrdma producers +that called svc_xprt_deferred_close() directly: +svc_rdma_cma_handler(), qp_event_handler(), +svc_rdma_post_send_err(), svc_rdma_wc_send(), the sendto drop +path, the rw completion error paths, and the recvfrom flush and +read-list error paths. + +Wake both waitqueues from svc_rdma_detach() as well. The +synchronous svc_xprt_close() path (backchannel ENOTCONN, device +removal via svc_rdma_xprt_done) reaches detach without flowing +through svc_xprt_deferred_close() and therefore does not invoke +the new helper. + +Fixes: ccc89b9d1ed2 ("svcrdma: Add fair queuing for Send Queue access") +Cc: stable@vger.kernel.org +Assisted-by: kres (claude-opus-4-7) +Signed-off-by: Chris Mason +[ cel: add svc_rdma_xprt_deferred_close() to complete the fix ] +Signed-off-by: Chuck Lever +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/sunrpc/svc_rdma.h | 1 + + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 4 ++-- + net/sunrpc/xprtrdma/svc_rdma_rw.c | 6 +++--- + net/sunrpc/xprtrdma/svc_rdma_sendto.c | 6 +++--- + net/sunrpc/xprtrdma/svc_rdma_transport.c | 30 ++++++++++++++++++++++++++++-- + 5 files changed, 37 insertions(+), 10 deletions(-) + +--- a/include/linux/sunrpc/svc_rdma.h ++++ b/include/linux/sunrpc/svc_rdma.h +@@ -328,6 +328,7 @@ extern int svc_rdma_result_payload(struc + unsigned int length); + + /* svc_rdma_transport.c */ ++extern void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma); + extern struct svc_xprt_class svc_rdma_class; + #ifdef CONFIG_SUNRPC_BACKCHANNEL + extern struct svc_xprt_class svc_rdma_bc_class; +--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c ++++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +@@ -377,7 +377,7 @@ flushed: + trace_svcrdma_wc_recv_err(wc, &ctxt->rc_cid); + dropped: + svc_rdma_recv_ctxt_put(rdma, ctxt); +- svc_xprt_deferred_close(&rdma->sc_xprt); ++ svc_rdma_xprt_deferred_close(rdma); + } + + /** +@@ -1001,7 +1001,7 @@ out_readlist: + if (ret == -EINVAL) + svc_rdma_send_error(rdma_xprt, ctxt, ret); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); +- svc_xprt_deferred_close(xprt); ++ svc_rdma_xprt_deferred_close(rdma_xprt); + return ret; + } + return 0; +--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c ++++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c +@@ -313,7 +313,7 @@ static void svc_rdma_reply_done(struct i + trace_svcrdma_wc_reply_err(wc, &cc->cc_cid); + } + +- svc_xprt_deferred_close(&rdma->sc_xprt); ++ svc_rdma_xprt_deferred_close(rdma); + } + + /** +@@ -345,7 +345,7 @@ static void svc_rdma_write_done(struct i + * some of the outgoing RPC message. Signal the loss + * to the client by closing the connection. + */ +- svc_xprt_deferred_close(&rdma->sc_xprt); ++ svc_rdma_xprt_deferred_close(rdma); + } + + /** +@@ -390,7 +390,7 @@ static void svc_rdma_wc_read_done(struct + */ + svc_rdma_cc_release(rdma, cc, DMA_FROM_DEVICE); + svc_rdma_recv_ctxt_put(rdma, ctxt); +- svc_xprt_deferred_close(&rdma->sc_xprt); ++ svc_rdma_xprt_deferred_close(rdma); + } + + /* +--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c ++++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c +@@ -395,7 +395,7 @@ int svc_rdma_post_send_err(struct svcxpr + int sqecount, int ret) + { + trace_svcrdma_sq_post_err(rdma, cid, ret); +- svc_xprt_deferred_close(&rdma->sc_xprt); ++ svc_rdma_xprt_deferred_close(rdma); + + /* If even one WR was posted, a Send completion will + * return the reserved SQ slots. +@@ -437,7 +437,7 @@ flushed: + else + trace_svcrdma_wc_send_flush(wc, &ctxt->sc_cid); + svc_rdma_send_ctxt_put(rdma, ctxt); +- svc_xprt_deferred_close(&rdma->sc_xprt); ++ svc_rdma_xprt_deferred_close(rdma); + } + + /** +@@ -1158,7 +1158,7 @@ put_ctxt: + svc_rdma_send_ctxt_put(rdma, sctxt); + drop_connection: + trace_svcrdma_send_err(rqstp, ret); +- svc_xprt_deferred_close(&rdma->sc_xprt); ++ svc_rdma_xprt_deferred_close(rdma); + return -ENOTCONN; + } + +--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c ++++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c +@@ -98,10 +98,27 @@ struct svc_xprt_class svc_rdma_class = { + .xcl_ident = XPRT_TRANSPORT_RDMA, + }; + ++/** ++ * svc_rdma_xprt_deferred_close - Close an RDMA transport (deferred) ++ * @rdma: transport to close ++ */ ++void svc_rdma_xprt_deferred_close(struct svcxprt_rdma *rdma) ++{ ++ svc_xprt_deferred_close(&rdma->sc_xprt); ++ ++ /* Release parked sc_sq_ticket_wait and sc_send_wait waiters. ++ * Once XPT_CLOSE is observed each returns -ENOTCONN. ++ */ ++ wake_up_all(&rdma->sc_sq_ticket_wait); ++ wake_up_all(&rdma->sc_send_wait); ++} ++ + /* QP event handler */ + static void qp_event_handler(struct ib_event *event, void *context) + { + struct svc_xprt *xprt = context; ++ struct svcxprt_rdma *rdma = ++ container_of(xprt, struct svcxprt_rdma, sc_xprt); + + trace_svcrdma_qp_error(event, (struct sockaddr *)&xprt->xpt_remote); + switch (event->event) { +@@ -119,7 +136,7 @@ static void qp_event_handler(struct ib_e + case IB_EVENT_QP_ACCESS_ERR: + case IB_EVENT_DEVICE_FATAL: + default: +- svc_xprt_deferred_close(xprt); ++ svc_rdma_xprt_deferred_close(rdma); + break; + } + } +@@ -340,7 +357,7 @@ static int svc_rdma_cma_handler(struct r + svc_xprt_enqueue(xprt); + break; + case RDMA_CM_EVENT_DISCONNECTED: +- svc_xprt_deferred_close(xprt); ++ svc_rdma_xprt_deferred_close(rdma); + break; + default: + break; +@@ -597,6 +614,15 @@ static void svc_rdma_detach(struct svc_x + container_of(xprt, struct svcxprt_rdma, sc_xprt); + + rdma_disconnect(rdma->sc_cm_id); ++ ++ /* ++ * Most close paths go through svc_rdma_xprt_deferred_close(), ++ * which wakes the SQ waitqueues. svc_xprt_close() reaches ++ * detach without that helper, so wake any threads parked in ++ * svc_rdma_sq_wait() here as well. ++ */ ++ wake_up_all(&rdma->sc_sq_ticket_wait); ++ wake_up_all(&rdma->sc_send_wait); + } + + /** diff --git a/queue-7.1/udmabuf-fix-dma-direction-mismatch-in-release_udmabuf.patch b/queue-7.1/udmabuf-fix-dma-direction-mismatch-in-release_udmabuf.patch new file mode 100644 index 0000000000..b3d9145dd4 --- /dev/null +++ b/queue-7.1/udmabuf-fix-dma-direction-mismatch-in-release_udmabuf.patch @@ -0,0 +1,70 @@ +From fb7b1a0ab25a6077d26cb3829e31743972d4f31d Mon Sep 17 00:00:00 2001 +From: Mikhail Gavrilov +Date: Sun, 15 Mar 2026 04:27:22 +0500 +Subject: udmabuf: fix DMA direction mismatch in release_udmabuf() + +From: Mikhail Gavrilov + +commit fb7b1a0ab25a6077d26cb3829e31743972d4f31d upstream. + +begin_cpu_udmabuf() maps the sg_table with the caller-provided direction +(e.g., DMA_TO_DEVICE for a write-only sync), and caches it in ubuf->sg +for reuse. However, release_udmabuf() always unmaps this sg_table with +a hardcoded DMA_BIDIRECTIONAL, regardless of the direction that was +originally used for the mapping. + +With CONFIG_DMA_API_DEBUG=y this produces: + + DMA-API: misc udmabuf: device driver frees DMA memory with different + direction [device address=0x000000044a123000] [size=4096 bytes] + [mapped with DMA_TO_DEVICE] [unmapped with DMA_BIDIRECTIONAL] + +The issue was found during video playback when GStreamer performed a +write-only DMA_BUF_IOCTL_SYNC on a udmabuf. It can be reproduced +with CONFIG_DMA_API_DEBUG=y by creating a udmabuf from a memfd, +performing a write-only sync (DMA_BUF_SYNC_WRITE without +DMA_BUF_SYNC_READ), and closing the file descriptor. + +Fix this by storing the DMA direction used when the sg_table is first +created in begin_cpu_udmabuf(), and passing that same direction to +put_sg_table() in release_udmabuf(). + +Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks") +Cc: stable@vger.kernel.org +Signed-off-by: Mikhail Gavrilov +Reviewed-by: Vivek Kasireddy +Signed-off-by: Vivek Kasireddy +Link: https://patch.msgid.link/20260314232722.15555-1-mikhail.v.gavrilov@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/dma-buf/udmabuf.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/dma-buf/udmabuf.c ++++ b/drivers/dma-buf/udmabuf.c +@@ -40,6 +40,7 @@ struct udmabuf { + struct folio **pinned_folios; + + struct sg_table *sg; ++ enum dma_data_direction sg_dir; + struct miscdevice *device; + pgoff_t *offsets; + }; +@@ -235,7 +236,7 @@ static void release_udmabuf(struct dma_b + struct device *dev = ubuf->device->this_device; + + if (ubuf->sg) +- put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); ++ put_sg_table(dev, ubuf->sg, ubuf->sg_dir); + + deinit_udmabuf(ubuf); + kfree(ubuf); +@@ -253,6 +254,8 @@ static int begin_cpu_udmabuf(struct dma_ + if (IS_ERR(ubuf->sg)) { + ret = PTR_ERR(ubuf->sg); + ubuf->sg = NULL; ++ } else { ++ ubuf->sg_dir = direction; + } + } else { + dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);