From: Greg Kroah-Hartman Date: Mon, 27 Apr 2026 16:55:15 +0000 (-0600) Subject: 7.0-stable patches X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=9bb46571c1af4818432c0bfb91aee205277d3af5;p=thirdparty%2Fkernel%2Fstable-queue.git 7.0-stable patches added patches: arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch arm64-mm-fix-rodata-full-block-mapping-support-for-realm-guests.patch device-property-make-modifications-of-fwnode-flags-thread-safe.patch driver-core-don-t-let-a-device-probe-until-it-s-ready.patch drm-nouveau-fix-nvkm_device-leak-on-aperture-removal-failure.patch drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch firmware-google-framebuffer-do-not-mark-framebuffer-as-busy.patch fs-afs-revert-mmap_prepare-change.patch greybus-gb-beagleplay-bound-bootloader-receive-buffering.patch greybus-gb-beagleplay-fix-sleep-in-atomic-context-in-hdlc_tx_frames.patch ibmasm-fix-heap-over-read-in-ibmasm_send_i2o_message.patch ibmasm-fix-oob-reads-in-command_file_write-due-to-missing-size-checks.patch kbuild-rust-allow-clippy-uninlined_format_args.patch leds-qcom-lpg-check-for-array-overflow-when-selecting-the-high-resolution.patch lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch loongarch-add-spectre-boundry-for-syscall-dispatch-table.patch misc-ibmasm-fix-oob-mmio-read-in-ibmasm_handle_mouse_interrupt.patch mm-fix-deferred-split-queue-races-during-migration.patch mm-migrate-requeue-destination-folio-on-deferred-split-queue.patch mm-prevent-droppable-mappings-from-being-locked.patch rust-dma-remove-dma_attr_no_kernel_mapping-from-public-attrs.patch sysfs-attribute_group-respect-is_visible_const-when-changing-owner.patch --- diff --git a/queue-7.0/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch b/queue-7.0/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch new file mode 100644 index 0000000000..394a2bf891 --- /dev/null +++ b/queue-7.0/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch @@ -0,0 +1,132 @@ +From 48478b9f791376b4b89018d7afdfd06865498f65 Mon Sep 17 00:00:00 2001 +From: Anshuman Khandual +Date: Mon, 9 Mar 2026 02:57:24 +0000 +Subject: arm64/mm: Enable batched TLB flush in unmap_hotplug_range() + +From: Anshuman Khandual + +commit 48478b9f791376b4b89018d7afdfd06865498f65 upstream. + +During a memory hot remove operation, both linear and vmemmap mappings for +the memory range being removed, get unmapped via unmap_hotplug_range() but +mapped pages get freed only for vmemmap mapping. This is just a sequential +operation where each table entry gets cleared, followed by a leaf specific +TLB flush, and then followed by memory free operation when applicable. + +This approach was simple and uniform both for vmemmap and linear mappings. +But linear mapping might contain CONT marked block memory where it becomes +necessary to first clear out all entire in the range before a TLB flush. +This is as per the architecture requirement. Hence batch all TLB flushes +during the table tear down walk and finally do it in unmap_hotplug_range(). + +Prior to this fix, it was hypothetically possible for a speculative access +to a higher address in the contiguous block to fill the TLB with shattered +entries for the entire contiguous range after a lower address had already +been cleared and invalidated. Due to the table entries being shattered, the +subsequent TLB invalidation for the higher address would not then clear the +TLB entries for the lower address, meaning stale TLB entries could persist. + +Besides it also helps in improving the performance via TLBI range operation +along with reduced synchronization instructions. The time spent executing +unmap_hotplug_range() improved 97% measured over a 2GB memory hot removal +in KVM guest. + +This scheme is not applicable during vmemmap mapping tear down where memory +needs to be freed and hence a TLB flush is required after clearing out page +table entry. + +Cc: Will Deacon +Cc: linux-arm-kernel@lists.infradead.org +Cc: linux-kernel@vger.kernel.org +Closes: https://lore.kernel.org/all/aWZYXhrT6D2M-7-N@willie-the-truck/ +Fixes: bbd6ec605c0f ("arm64/mm: Enable memory hot remove") +Cc: stable@vger.kernel.org +Reviewed-by: David Hildenbrand (Arm) +Reviewed-by: Ryan Roberts +Signed-off-by: Ryan Roberts +Signed-off-by: Anshuman Khandual +Signed-off-by: Catalin Marinas +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/mm/mmu.c | 36 ++++++++++++++++++++---------------- + 1 file changed, 20 insertions(+), 16 deletions(-) + +--- a/arch/arm64/mm/mmu.c ++++ b/arch/arm64/mm/mmu.c +@@ -1462,10 +1462,14 @@ static void unmap_hotplug_pte_range(pmd_ + + WARN_ON(!pte_present(pte)); + __pte_clear(&init_mm, addr, ptep); +- flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +- if (free_mapped) ++ if (free_mapped) { ++ /* CONT blocks are not supported in the vmemmap */ ++ WARN_ON(pte_cont(pte)); ++ flush_tlb_kernel_range(addr, addr + PAGE_SIZE); + free_hotplug_page_range(pte_page(pte), + PAGE_SIZE, altmap); ++ } ++ /* unmap_hotplug_range() flushes TLB for !free_mapped */ + } while (addr += PAGE_SIZE, addr < end); + } + +@@ -1486,15 +1490,14 @@ static void unmap_hotplug_pmd_range(pud_ + WARN_ON(!pmd_present(pmd)); + if (pmd_sect(pmd)) { + pmd_clear(pmdp); +- +- /* +- * One TLBI should be sufficient here as the PMD_SIZE +- * range is mapped with a single block entry. +- */ +- flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +- if (free_mapped) ++ if (free_mapped) { ++ /* CONT blocks are not supported in the vmemmap */ ++ WARN_ON(pmd_cont(pmd)); ++ flush_tlb_kernel_range(addr, addr + PMD_SIZE); + free_hotplug_page_range(pmd_page(pmd), + PMD_SIZE, altmap); ++ } ++ /* unmap_hotplug_range() flushes TLB for !free_mapped */ + continue; + } + WARN_ON(!pmd_table(pmd)); +@@ -1519,15 +1522,12 @@ static void unmap_hotplug_pud_range(p4d_ + WARN_ON(!pud_present(pud)); + if (pud_sect(pud)) { + pud_clear(pudp); +- +- /* +- * One TLBI should be sufficient here as the PUD_SIZE +- * range is mapped with a single block entry. +- */ +- flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +- if (free_mapped) ++ if (free_mapped) { ++ flush_tlb_kernel_range(addr, addr + PUD_SIZE); + free_hotplug_page_range(pud_page(pud), + PUD_SIZE, altmap); ++ } ++ /* unmap_hotplug_range() flushes TLB for !free_mapped */ + continue; + } + WARN_ON(!pud_table(pud)); +@@ -1557,6 +1557,7 @@ static void unmap_hotplug_p4d_range(pgd_ + static void unmap_hotplug_range(unsigned long addr, unsigned long end, + bool free_mapped, struct vmem_altmap *altmap) + { ++ unsigned long start = addr; + unsigned long next; + pgd_t *pgdp, pgd; + +@@ -1578,6 +1579,9 @@ static void unmap_hotplug_range(unsigned + WARN_ON(!pgd_present(pgd)); + unmap_hotplug_p4d_range(pgdp, addr, next, free_mapped, altmap); + } while (addr = next, addr < end); ++ ++ if (!free_mapped) ++ flush_tlb_kernel_range(start, end); + } + + static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr, diff --git a/queue-7.0/arm64-mm-fix-rodata-full-block-mapping-support-for-realm-guests.patch b/queue-7.0/arm64-mm-fix-rodata-full-block-mapping-support-for-realm-guests.patch new file mode 100644 index 0000000000..1c78b13309 --- /dev/null +++ b/queue-7.0/arm64-mm-fix-rodata-full-block-mapping-support-for-realm-guests.patch @@ -0,0 +1,177 @@ +From f12b435de2f2bb09ce406467020181ada528844c Mon Sep 17 00:00:00 2001 +From: Ryan Roberts +Date: Mon, 30 Mar 2026 17:17:02 +0100 +Subject: arm64: mm: Fix rodata=full block mapping support for realm guests + +From: Ryan Roberts + +commit f12b435de2f2bb09ce406467020181ada528844c upstream. + +Commit a166563e7ec37 ("arm64: mm: support large block mapping when +rodata=full") enabled the linear map to be mapped by block/cont while +still allowing granular permission changes on BBML2_NOABORT systems by +lazily splitting the live mappings. This mechanism was intended to be +usable by realm guests since they need to dynamically share dma buffers +with the host by "decrypting" them - which for Arm CCA, means marking +them as shared in the page tables. + +However, it turns out that the mechanism was failing for realm guests +because realms need to share their dma buffers (via +__set_memory_enc_dec()) much earlier during boot than +split_kernel_leaf_mapping() was able to handle. The report linked below +showed that GIC's ITS was one such user. But during the investigation I +found other callsites that could not meet the +split_kernel_leaf_mapping() constraints. + +The problem is that we block map the linear map based on the boot CPU +supporting BBML2_NOABORT, then check that all the other CPUs support it +too when finalizing the caps. If they don't, then we stop_machine() and +split to ptes. For safety, split_kernel_leaf_mapping() previously +wouldn't permit splitting until after the caps were finalized. That +ensured that if any secondary cpus were running that didn't support +BBML2_NOABORT, we wouldn't risk breaking them. + +I've fix this problem by reducing the black-out window where we refuse +to split; there are now 2 windows. The first is from T0 until the page +allocator is inititialized. Splitting allocates memory for the page +allocator so it must be in use. The second covers the period between +starting to online the secondary cpus until the system caps are +finalized (this is a very small window). + +All of the problematic callers are calling __set_memory_enc_dec() before +the secondary cpus come online, so this solves the problem. However, one +of these callers, swiotlb_update_mem_attributes(), was trying to split +before the page allocator was initialized. So I have moved this call +from arch_mm_preinit() to mem_init(), which solves the ordering issue. + +I've added warnings and return an error if any attempt is made to split +in the black-out windows. + +Note there are other issues which prevent booting all the way to user +space, which will be fixed in subsequent patches. + +Reported-by: Jinjiang Tu +Closes: https://lore.kernel.org/all/0b2a4ae5-fc51-4d77-b177-b2e9db74f11d@huawei.com/ +Fixes: a166563e7ec3 ("arm64: mm: support large block mapping when rodata=full") +Cc: stable@vger.kernel.org +Reviewed-by: Kevin Brodsky +Signed-off-by: Ryan Roberts +Reviewed-by: Suzuki K Poulose +Tested-by: Suzuki K Poulose +Signed-off-by: Catalin Marinas +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/include/asm/mmu.h | 2 + + arch/arm64/mm/init.c | 9 +++++++- + arch/arm64/mm/mmu.c | 45 ++++++++++++++++++++++++++++++------------- + 3 files changed, 42 insertions(+), 14 deletions(-) + +--- a/arch/arm64/include/asm/mmu.h ++++ b/arch/arm64/include/asm/mmu.h +@@ -112,5 +112,7 @@ void kpti_install_ng_mappings(void); + static inline void kpti_install_ng_mappings(void) {} + #endif + ++extern bool page_alloc_available; ++ + #endif /* !__ASSEMBLER__ */ + #endif +--- a/arch/arm64/mm/init.c ++++ b/arch/arm64/mm/init.c +@@ -350,7 +350,6 @@ void __init arch_mm_preinit(void) + } + + swiotlb_init(swiotlb, flags); +- swiotlb_update_mem_attributes(); + + /* + * Check boundaries twice: Some fundamental inconsistencies can be +@@ -377,6 +376,14 @@ void __init arch_mm_preinit(void) + } + } + ++bool page_alloc_available __ro_after_init; ++ ++void __init mem_init(void) ++{ ++ page_alloc_available = true; ++ swiotlb_update_mem_attributes(); ++} ++ + void free_initmem(void) + { + void *lm_init_begin = lm_alias(__init_begin); +--- a/arch/arm64/mm/mmu.c ++++ b/arch/arm64/mm/mmu.c +@@ -772,30 +772,51 @@ static inline bool force_pte_mapping(voi + } + + static DEFINE_MUTEX(pgtable_split_lock); ++static bool linear_map_requires_bbml2; + + int split_kernel_leaf_mapping(unsigned long start, unsigned long end) + { + int ret; + + /* +- * !BBML2_NOABORT systems should not be trying to change permissions on +- * anything that is not pte-mapped in the first place. Just return early +- * and let the permission change code raise a warning if not already +- * pte-mapped. +- */ +- if (!system_supports_bbml2_noabort()) +- return 0; +- +- /* + * If the region is within a pte-mapped area, there is no need to try to + * split. Additionally, CONFIG_DEBUG_PAGEALLOC and CONFIG_KFENCE may + * change permissions from atomic context so for those cases (which are + * always pte-mapped), we must not go any further because taking the +- * mutex below may sleep. ++ * mutex below may sleep. Do not call force_pte_mapping() here because ++ * it could return a confusing result if called from a secondary cpu ++ * prior to finalizing caps. Instead, linear_map_requires_bbml2 gives us ++ * what we need. + */ +- if (force_pte_mapping() || is_kfence_address((void *)start)) ++ if (!linear_map_requires_bbml2 || is_kfence_address((void *)start)) + return 0; + ++ if (!system_supports_bbml2_noabort()) { ++ /* ++ * !BBML2_NOABORT systems should not be trying to change ++ * permissions on anything that is not pte-mapped in the first ++ * place. Just return early and let the permission change code ++ * raise a warning if not already pte-mapped. ++ */ ++ if (system_capabilities_finalized()) ++ return 0; ++ ++ /* ++ * Boot-time: split_kernel_leaf_mapping_locked() allocates from ++ * page allocator. Can't split until it's available. ++ */ ++ if (WARN_ON(!page_alloc_available)) ++ return -EBUSY; ++ ++ /* ++ * Boot-time: Started secondary cpus but don't know if they ++ * support BBML2_NOABORT yet. Can't allow splitting in this ++ * window in case they don't. ++ */ ++ if (WARN_ON(num_online_cpus() > 1)) ++ return -EBUSY; ++ } ++ + /* + * Ensure start and end are at least page-aligned since this is the + * finest granularity we can split to. +@@ -895,8 +916,6 @@ static int range_split_to_ptes(unsigned + return ret; + } + +-static bool linear_map_requires_bbml2 __initdata; +- + u32 idmap_kpti_bbml2_flag; + + static void __init init_idmap_kpti_bbml2_flag(void) diff --git a/queue-7.0/device-property-make-modifications-of-fwnode-flags-thread-safe.patch b/queue-7.0/device-property-make-modifications-of-fwnode-flags-thread-safe.patch new file mode 100644 index 0000000000..47eb4654ba --- /dev/null +++ b/queue-7.0/device-property-make-modifications-of-fwnode-flags-thread-safe.patch @@ -0,0 +1,312 @@ +From f72e77c33e4b5657af35125e75bab249256030f3 Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +Date: Tue, 17 Mar 2026 09:01:20 -0700 +Subject: device property: Make modifications of fwnode "flags" thread safe + +From: Douglas Anderson + +commit f72e77c33e4b5657af35125e75bab249256030f3 upstream. + +In various places in the kernel, we modify the fwnode "flags" member +by doing either: + fwnode->flags |= SOME_FLAG; + fwnode->flags &= ~SOME_FLAG; + +This type of modification is not thread-safe. If two threads are both +mucking with the flags at the same time then one can clobber the +other. + +While flags are often modified while under the "fwnode_link_lock", +this is not universally true. + +Create some accessor functions for setting, clearing, and testing the +FWNODE flags and move all users to these accessor functions. New +accessor functions use set_bit() and clear_bit(), which are +thread-safe. + +Cc: stable@vger.kernel.org +Fixes: c2c724c868c4 ("driver core: Add fw_devlink_parse_fwtree()") +Reviewed-by: Andy Shevchenko +Acked-by: Mark Brown +Reviewed-by: Wolfram Sang +Signed-off-by: Douglas Anderson +Reviewed-by: Rafael J. Wysocki (Intel) +Reviewed-by: Saravana Kannan +Link: https://patch.msgid.link/20260317090112.v2.1.I0a4d03104ecd5103df3d76f66c8d21b1d15a2e38@changeid +[ Fix fwnode_clear_flag() argument alignment, restore dropped blank + line in fwnode_dev_initialized(), and remove unnecessary parentheses + around fwnode_test_flag() calls. - Danilo ] +Signed-off-by: Danilo Krummrich +Signed-off-by: Greg Kroah-Hartman +--- + drivers/base/core.c | 24 +++++++++---------- + drivers/bus/imx-weim.c | 2 - + drivers/i2c/i2c-core-of.c | 2 - + drivers/net/phy/mdio_bus_provider.c | 4 +-- + drivers/of/base.c | 2 - + drivers/of/dynamic.c | 2 - + drivers/of/platform.c | 2 - + drivers/spi/spi.c | 2 - + include/linux/fwnode.h | 44 +++++++++++++++++++++++++++--------- + 9 files changed, 53 insertions(+), 31 deletions(-) + +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -182,7 +182,7 @@ void fw_devlink_purge_absent_suppliers(s + if (fwnode->dev) + return; + +- fwnode->flags |= FWNODE_FLAG_NOT_DEVICE; ++ fwnode_set_flag(fwnode, FWNODE_FLAG_NOT_DEVICE); + fwnode_links_purge_consumers(fwnode); + + fwnode_for_each_available_child_node(fwnode, child) +@@ -228,7 +228,7 @@ static void __fw_devlink_pickup_dangling + if (fwnode->dev && fwnode->dev->bus) + return; + +- fwnode->flags |= FWNODE_FLAG_NOT_DEVICE; ++ fwnode_set_flag(fwnode, FWNODE_FLAG_NOT_DEVICE); + __fwnode_links_move_consumers(fwnode, new_sup); + + fwnode_for_each_available_child_node(fwnode, child) +@@ -1012,7 +1012,7 @@ static void device_links_missing_supplie + static bool dev_is_best_effort(struct device *dev) + { + return (fw_devlink_best_effort && dev->can_match) || +- (dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT)); ++ (dev->fwnode && fwnode_test_flag(dev->fwnode, FWNODE_FLAG_BEST_EFFORT)); + } + + static struct fwnode_handle *fwnode_links_check_suppliers( +@@ -1723,11 +1723,11 @@ bool fw_devlink_is_strict(void) + + static void fw_devlink_parse_fwnode(struct fwnode_handle *fwnode) + { +- if (fwnode->flags & FWNODE_FLAG_LINKS_ADDED) ++ if (fwnode_test_flag(fwnode, FWNODE_FLAG_LINKS_ADDED)) + return; + + fwnode_call_int_op(fwnode, add_links); +- fwnode->flags |= FWNODE_FLAG_LINKS_ADDED; ++ fwnode_set_flag(fwnode, FWNODE_FLAG_LINKS_ADDED); + } + + static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) +@@ -1885,7 +1885,7 @@ static bool fwnode_init_without_drv(stru + struct device *dev; + bool ret; + +- if (!(fwnode->flags & FWNODE_FLAG_INITIALIZED)) ++ if (!fwnode_test_flag(fwnode, FWNODE_FLAG_INITIALIZED)) + return false; + + dev = get_dev_from_fwnode(fwnode); +@@ -2001,10 +2001,10 @@ static bool __fw_devlink_relax_cycles(st + * We aren't trying to find all cycles. Just a cycle between con and + * sup_handle. + */ +- if (sup_handle->flags & FWNODE_FLAG_VISITED) ++ if (fwnode_test_flag(sup_handle, FWNODE_FLAG_VISITED)) + return false; + +- sup_handle->flags |= FWNODE_FLAG_VISITED; ++ fwnode_set_flag(sup_handle, FWNODE_FLAG_VISITED); + + /* Termination condition. */ + if (sup_handle == con_handle) { +@@ -2074,7 +2074,7 @@ static bool __fw_devlink_relax_cycles(st + } + + out: +- sup_handle->flags &= ~FWNODE_FLAG_VISITED; ++ fwnode_clear_flag(sup_handle, FWNODE_FLAG_VISITED); + put_device(sup_dev); + put_device(con_dev); + put_device(par_dev); +@@ -2127,7 +2127,7 @@ static int fw_devlink_create_devlink(str + * When such a flag is set, we can't create device links where P is the + * supplier of C as that would delay the probe of C. + */ +- if (sup_handle->flags & FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD && ++ if (fwnode_test_flag(sup_handle, FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD) && + fwnode_is_ancestor_of(sup_handle, con->fwnode)) + return -EINVAL; + +@@ -2150,7 +2150,7 @@ static int fw_devlink_create_devlink(str + else + flags = FW_DEVLINK_FLAGS_PERMISSIVE; + +- if (sup_handle->flags & FWNODE_FLAG_NOT_DEVICE) ++ if (fwnode_test_flag(sup_handle, FWNODE_FLAG_NOT_DEVICE)) + sup_dev = fwnode_get_next_parent_dev(sup_handle); + else + sup_dev = get_dev_from_fwnode(sup_handle); +@@ -2162,7 +2162,7 @@ static int fw_devlink_create_devlink(str + * supplier device indefinitely. + */ + if (sup_dev->links.status == DL_DEV_NO_DRIVER && +- sup_handle->flags & FWNODE_FLAG_INITIALIZED) { ++ fwnode_test_flag(sup_handle, FWNODE_FLAG_INITIALIZED)) { + dev_dbg(con, + "Not linking %pfwf - dev might never probe\n", + sup_handle); +--- a/drivers/bus/imx-weim.c ++++ b/drivers/bus/imx-weim.c +@@ -332,7 +332,7 @@ static int of_weim_notify(struct notifie + * fw_devlink doesn't skip adding consumers to this + * device. + */ +- rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE; ++ fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); + if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) { + dev_err(&pdev->dev, + "Failed to create child device '%pOF'\n", +--- a/drivers/i2c/i2c-core-of.c ++++ b/drivers/i2c/i2c-core-of.c +@@ -180,7 +180,7 @@ static int of_i2c_notify(struct notifier + * Clear the flag before adding the device so that fw_devlink + * doesn't skip adding consumers to this device. + */ +- rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE; ++ fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); + client = of_i2c_register_device(adap, rd->dn); + if (IS_ERR(client)) { + dev_err(&adap->dev, "failed to create client for '%pOF'\n", +--- a/drivers/net/phy/mdio_bus_provider.c ++++ b/drivers/net/phy/mdio_bus_provider.c +@@ -294,8 +294,8 @@ int __mdiobus_register(struct mii_bus *b + return -EINVAL; + + if (bus->parent && bus->parent->of_node) +- bus->parent->of_node->fwnode.flags |= +- FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD; ++ fwnode_set_flag(&bus->parent->of_node->fwnode, ++ FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD); + + WARN(bus->state != MDIOBUS_ALLOCATED && + bus->state != MDIOBUS_UNREGISTERED, +--- a/drivers/of/base.c ++++ b/drivers/of/base.c +@@ -1915,7 +1915,7 @@ void of_alias_scan(void * (*dt_alloc)(u6 + if (name) + of_stdout = of_find_node_opts_by_path(name, &of_stdout_options); + if (of_stdout) +- of_stdout->fwnode.flags |= FWNODE_FLAG_BEST_EFFORT; ++ fwnode_set_flag(&of_stdout->fwnode, FWNODE_FLAG_BEST_EFFORT); + } + + if (!of_aliases) +--- a/drivers/of/dynamic.c ++++ b/drivers/of/dynamic.c +@@ -225,7 +225,7 @@ static void __of_attach_node(struct devi + np->sibling = np->parent->child; + np->parent->child = np; + of_node_clear_flag(np, OF_DETACHED); +- np->fwnode.flags |= FWNODE_FLAG_NOT_DEVICE; ++ fwnode_set_flag(&np->fwnode, FWNODE_FLAG_NOT_DEVICE); + + raw_spin_unlock_irqrestore(&devtree_lock, flags); + +--- a/drivers/of/platform.c ++++ b/drivers/of/platform.c +@@ -742,7 +742,7 @@ static int of_platform_notify(struct not + * Clear the flag before adding the device so that fw_devlink + * doesn't skip adding consumers to this device. + */ +- rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE; ++ fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); + /* pdev_parent may be NULL when no bus platform device */ + pdev_parent = of_find_device_by_node(parent); + pdev = of_platform_device_create(rd->dn, NULL, +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -4943,7 +4943,7 @@ static int of_spi_notify(struct notifier + * Clear the flag before adding the device so that fw_devlink + * doesn't skip adding consumers to this device. + */ +- rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE; ++ fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); + spi = of_register_spi_device(ctlr, rd->dn); + put_device(&ctlr->dev); + +--- a/include/linux/fwnode.h ++++ b/include/linux/fwnode.h +@@ -15,6 +15,7 @@ + #define _LINUX_FWNODE_H_ + + #include ++#include + #include + #include + #include +@@ -42,12 +43,12 @@ struct device; + * suppliers. Only enforce ordering with suppliers that have + * drivers. + */ +-#define FWNODE_FLAG_LINKS_ADDED BIT(0) +-#define FWNODE_FLAG_NOT_DEVICE BIT(1) +-#define FWNODE_FLAG_INITIALIZED BIT(2) +-#define FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD BIT(3) +-#define FWNODE_FLAG_BEST_EFFORT BIT(4) +-#define FWNODE_FLAG_VISITED BIT(5) ++#define FWNODE_FLAG_LINKS_ADDED 0 ++#define FWNODE_FLAG_NOT_DEVICE 1 ++#define FWNODE_FLAG_INITIALIZED 2 ++#define FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD 3 ++#define FWNODE_FLAG_BEST_EFFORT 4 ++#define FWNODE_FLAG_VISITED 5 + + struct fwnode_handle { + struct fwnode_handle *secondary; +@@ -57,7 +58,7 @@ struct fwnode_handle { + struct device *dev; + struct list_head suppliers; + struct list_head consumers; +- u8 flags; ++ unsigned long flags; + }; + + /* +@@ -212,16 +213,37 @@ static inline void fwnode_init(struct fw + INIT_LIST_HEAD(&fwnode->suppliers); + } + ++static inline void fwnode_set_flag(struct fwnode_handle *fwnode, ++ unsigned int bit) ++{ ++ set_bit(bit, &fwnode->flags); ++} ++ ++static inline void fwnode_clear_flag(struct fwnode_handle *fwnode, ++ unsigned int bit) ++{ ++ clear_bit(bit, &fwnode->flags); ++} ++ ++static inline void fwnode_assign_flag(struct fwnode_handle *fwnode, ++ unsigned int bit, bool value) ++{ ++ assign_bit(bit, &fwnode->flags, value); ++} ++ ++static inline bool fwnode_test_flag(struct fwnode_handle *fwnode, ++ unsigned int bit) ++{ ++ return test_bit(bit, &fwnode->flags); ++} ++ + static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode, + bool initialized) + { + if (IS_ERR_OR_NULL(fwnode)) + return; + +- if (initialized) +- fwnode->flags |= FWNODE_FLAG_INITIALIZED; +- else +- fwnode->flags &= ~FWNODE_FLAG_INITIALIZED; ++ fwnode_assign_flag(fwnode, FWNODE_FLAG_INITIALIZED, initialized); + } + + int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup, diff --git a/queue-7.0/driver-core-don-t-let-a-device-probe-until-it-s-ready.patch b/queue-7.0/driver-core-don-t-let-a-device-probe-until-it-s-ready.patch new file mode 100644 index 0000000000..09f5e7f46a --- /dev/null +++ b/queue-7.0/driver-core-don-t-let-a-device-probe-until-it-s-ready.patch @@ -0,0 +1,214 @@ +From a2225b6e834a838ae3c93709760edc0a169eb2f2 Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +Date: Mon, 6 Apr 2026 16:22:54 -0700 +Subject: driver core: Don't let a device probe until it's ready + +From: Douglas Anderson + +commit a2225b6e834a838ae3c93709760edc0a169eb2f2 upstream. + +The moment we link a "struct device" into the list of devices for the +bus, it's possible probe can happen. This is because another thread +can load the driver at any time and that can cause the device to +probe. This has been seen in practice with a stack crawl that looks +like this [1]: + + really_probe() + __driver_probe_device() + driver_probe_device() + __driver_attach() + bus_for_each_dev() + driver_attach() + bus_add_driver() + driver_register() + __platform_driver_register() + init_module() [some module] + do_one_initcall() + do_init_module() + load_module() + __arm64_sys_finit_module() + invoke_syscall() + +As a result of the above, it was seen that device_links_driver_bound() +could be called for the device before "dev->fwnode->dev" was +assigned. This prevented __fw_devlink_pickup_dangling_consumers() from +being called which meant that other devices waiting on our driver's +sub-nodes were stuck deferring forever. + +It's believed that this problem is showing up suddenly for two +reasons: +1. Android has recently (last ~1 year) implemented an optimization to + the order it loads modules [2]. When devices opt-in to this faster + loading, modules are loaded one-after-the-other very quickly. This + is unlike how other distributions do it. The reproduction of this + problem has only been seen on devices that opt-in to Android's + "parallel module loading". +2. Android devices typically opt-in to fw_devlink, and the most + noticeable issue is the NULL "dev->fwnode->dev" in + device_links_driver_bound(). fw_devlink is somewhat new code and + also not in use by all Linux devices. + +Even though the specific symptom where "dev->fwnode->dev" wasn't +assigned could be fixed by moving that assignment higher in +device_add(), other parts of device_add() (like the call to +device_pm_add()) are also important to run before probe. Only moving +the "dev->fwnode->dev" assignment would likely fix the current +symptoms but lead to difficult-to-debug problems in the future. + +Fix the problem by preventing probe until device_add() has run far +enough that the device is ready to probe. If somehow we end up trying +to probe before we're allowed, __driver_probe_device() will return +-EPROBE_DEFER which will make certain the device is noticed. + +In the race condition that was seen with Android's faster module +loading, we will temporarily add the device to the deferred list and +then take it off immediately when device_add() probes the device. + +Instead of adding another flag to the bitfields already in "struct +device", instead add a new "flags" field and use that. This allows us +to freely change the bit from different thread without worrying about +corrupting nearby bits (and means threads changing other bit won't +corrupt us). + +[1] Captured on a machine running a downstream 6.6 kernel +[2] https://cs.android.com/android/platform/superproject/main/+/main:system/core/libmodprobe/libmodprobe.cpp?q=LoadModulesParallel + +Cc: stable@vger.kernel.org +Fixes: 2023c610dc54 ("Driver core: add new device to bus's list before probing") +Reviewed-by: Alan Stern +Reviewed-by: Rafael J. Wysocki (Intel) +Reviewed-by: Danilo Krummrich +Acked-by: Greg Kroah-Hartman +Acked-by: Marek Szyprowski +Signed-off-by: Douglas Anderson +Link: https://patch.msgid.link/20260406162231.v5.1.Id750b0fbcc94f23ed04b7aecabcead688d0d8c17@changeid +Signed-off-by: Danilo Krummrich +Signed-off-by: Greg Kroah-Hartman +--- + drivers/base/core.c | 15 +++++++++++++++ + drivers/base/dd.c | 20 ++++++++++++++++++++ + include/linux/device.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 79 insertions(+) + +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -3687,6 +3687,21 @@ int device_add(struct device *dev) + fw_devlink_link_device(dev); + } + ++ /* ++ * The moment the device was linked into the bus's "klist_devices" in ++ * bus_add_device() then it's possible that probe could have been ++ * attempted in a different thread via userspace loading a driver ++ * matching the device. "ready_to_probe" being unset would have ++ * blocked those attempts. Now that all of the above initialization has ++ * happened, unblock probe. If probe happens through another thread ++ * after this point but before bus_probe_device() runs then it's fine. ++ * bus_probe_device() -> device_initial_probe() -> __device_attach() ++ * will notice (under device_lock) that the device is already bound. ++ */ ++ device_lock(dev); ++ dev_set_ready_to_probe(dev); ++ device_unlock(dev); ++ + bus_probe_device(dev); + + /* +--- a/drivers/base/dd.c ++++ b/drivers/base/dd.c +@@ -848,6 +848,26 @@ static int __driver_probe_device(const s + if (dev->driver) + return -EBUSY; + ++ /* ++ * In device_add(), the "struct device" gets linked into the subsystem's ++ * list of devices and broadcast to userspace (via uevent) before we're ++ * quite ready to probe. Those open pathways to driver probe before ++ * we've finished enough of device_add() to reliably support probe. ++ * Detect this and tell other pathways to try again later. device_add() ++ * itself will also try to probe immediately after setting ++ * "ready_to_probe". ++ */ ++ if (!dev_ready_to_probe(dev)) ++ return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n"); ++ ++ /* ++ * Set can_match = true after calling dev_ready_to_probe(), so ++ * driver_deferred_probe_add() won't actually add the device to the ++ * deferred probe list when dev_ready_to_probe() returns false. ++ * ++ * When dev_ready_to_probe() returns false, it means that device_add() ++ * will do another probe() attempt for us. ++ */ + dev->can_match = true; + dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n", + drv->bus->name, __func__, drv->name); +--- a/include/linux/device.h ++++ b/include/linux/device.h +@@ -459,6 +459,21 @@ struct device_physical_location { + }; + + /** ++ * enum struct_device_flags - Flags in struct device ++ * ++ * Each flag should have a set of accessor functions created via ++ * __create_dev_flag_accessors() for each access. ++ * ++ * @DEV_FLAG_READY_TO_PROBE: If set then device_add() has finished enough ++ * initialization that probe could be called. ++ */ ++enum struct_device_flags { ++ DEV_FLAG_READY_TO_PROBE = 0, ++ ++ DEV_FLAG_COUNT ++}; ++ ++/** + * struct device - The basic device structure + * @parent: The device's "parent" device, the device to which it is attached. + * In most cases, a parent device is some sort of bus or host +@@ -553,6 +568,7 @@ struct device_physical_location { + * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers. + * @dma_iommu: Device is using default IOMMU implementation for DMA and + * doesn't rely on dma_ops structure. ++ * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify. + * + * At the lowest level, every device in a Linux system is represented by an + * instance of struct device. The device structure contains the information +@@ -675,8 +691,36 @@ struct device { + #ifdef CONFIG_IOMMU_DMA + bool dma_iommu:1; + #endif ++ ++ DECLARE_BITMAP(flags, DEV_FLAG_COUNT); + }; + ++#define __create_dev_flag_accessors(accessor_name, flag_name) \ ++static inline bool dev_##accessor_name(const struct device *dev) \ ++{ \ ++ return test_bit(flag_name, dev->flags); \ ++} \ ++static inline void dev_set_##accessor_name(struct device *dev) \ ++{ \ ++ set_bit(flag_name, dev->flags); \ ++} \ ++static inline void dev_clear_##accessor_name(struct device *dev) \ ++{ \ ++ clear_bit(flag_name, dev->flags); \ ++} \ ++static inline void dev_assign_##accessor_name(struct device *dev, bool value) \ ++{ \ ++ assign_bit(flag_name, dev->flags, value); \ ++} \ ++static inline bool dev_test_and_set_##accessor_name(struct device *dev) \ ++{ \ ++ return test_and_set_bit(flag_name, dev->flags); \ ++} ++ ++__create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE); ++ ++#undef __create_dev_flag_accessors ++ + /** + * struct device_link - Device link representation. + * @supplier: The device on the supplier end of the link. diff --git a/queue-7.0/drm-nouveau-fix-nvkm_device-leak-on-aperture-removal-failure.patch b/queue-7.0/drm-nouveau-fix-nvkm_device-leak-on-aperture-removal-failure.patch new file mode 100644 index 0000000000..7b2e7d8694 --- /dev/null +++ b/queue-7.0/drm-nouveau-fix-nvkm_device-leak-on-aperture-removal-failure.patch @@ -0,0 +1,40 @@ +From 6597ff1d8de3f583be169587efeafd8af134e138 Mon Sep 17 00:00:00 2001 +From: David Carlier +Date: Sat, 11 Apr 2026 07:29:38 +0100 +Subject: drm/nouveau: fix nvkm_device leak on aperture removal failure + +From: David Carlier + +commit 6597ff1d8de3f583be169587efeafd8af134e138 upstream. + +When aperture_remove_conflicting_pci_devices() fails during probe, the +error path returns directly without unwinding the nvkm_device that was +just allocated by nvkm_device_pci_new(). This leaks both the device +wrapper and the pci_enable_device() reference taken inside it. + +Jump to the existing fail_nvkm label so nvkm_device_del() runs and +balances both. The leak was introduced when the intermediate +nvkm_device_del() between detection and aperture removal was dropped +in favor of creating the pci device once. + +Fixes: c0bfe34330b5 ("drm/nouveau: create pci device once") +Cc: stable@vger.kernel.org +Signed-off-by: David Carlier +Link: https://patch.msgid.link/20260411062938.22925-1-devnexen@gmail.com +Signed-off-by: Danilo Krummrich +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/nouveau/nouveau_drm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/nouveau/nouveau_drm.c ++++ b/drivers/gpu/drm/nouveau/nouveau_drm.c +@@ -874,7 +874,7 @@ static int nouveau_drm_probe(struct pci_ + /* Remove conflicting drivers (vesafb, efifb etc). */ + ret = aperture_remove_conflicting_pci_devices(pdev, driver_pci.name); + if (ret) +- return ret; ++ goto fail_nvkm; + + pci_set_master(pdev); + diff --git a/queue-7.0/drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch b/queue-7.0/drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch new file mode 100644 index 0000000000..dbff604981 --- /dev/null +++ b/queue-7.0/drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch @@ -0,0 +1,49 @@ +From 2fc87d37be1b730a149b035f9375fdb8cc5333a5 Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Mon, 20 Apr 2026 21:16:09 +0200 +Subject: drm/nouveau: fix u32 overflow in pushbuf reloc bounds check + +From: Greg Kroah-Hartman + +commit 2fc87d37be1b730a149b035f9375fdb8cc5333a5 upstream. + +nouveau_gem_pushbuf_reloc_apply() validates each relocation with + + if (r->reloc_bo_offset + 4 > nvbo->bo.base.size) + +but reloc_bo_offset is __u32 (uapi/drm/nouveau_drm.h) and the integer +literal 4 promotes to unsigned int, so the addition is performed in 32 +bits and wraps before the comparison against the size_t bo size. + +Cast to u64 so the addition happens in 64-bit arithmetic. + +Cc: Lyude Paul +Cc: Danilo Krummrich +Cc: Maarten Lankhorst +Cc: Maxime Ripard +Cc: Thomas Zimmermann +Cc: David Airlie +Cc: Simona Vetter +Reported-by: Anthropic +Cc: stable +Assisted-by: gkh_clanker_t1000 +Fixes: a1606a9596e5 ("drm/nouveau: new gem pushbuf interface, bump to 0.0.16") +Signed-off-by: Greg Kroah-Hartman +[ Add Fixes: tag. - Danilo ] +Signed-off-by: Danilo Krummrich +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/nouveau/nouveau_gem.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/nouveau/nouveau_gem.c ++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c +@@ -686,7 +686,7 @@ nouveau_gem_pushbuf_reloc_apply(struct n + } + nvbo = (void *)(unsigned long)bo[r->reloc_bo_index].user_priv; + +- if (unlikely(r->reloc_bo_offset + 4 > ++ if (unlikely((u64)r->reloc_bo_offset + 4 > + nvbo->bo.base.size)) { + NV_PRINTK(err, cli, "reloc outside of bo\n"); + ret = -EINVAL; diff --git a/queue-7.0/firmware-google-framebuffer-do-not-mark-framebuffer-as-busy.patch b/queue-7.0/firmware-google-framebuffer-do-not-mark-framebuffer-as-busy.patch new file mode 100644 index 0000000000..b2bc47b8cd --- /dev/null +++ b/queue-7.0/firmware-google-framebuffer-do-not-mark-framebuffer-as-busy.patch @@ -0,0 +1,47 @@ +From f3850d399de3b6142b02315227ef9e772ed0c302 Mon Sep 17 00:00:00 2001 +From: Thomas Zimmermann +Date: Tue, 17 Feb 2026 16:56:12 +0100 +Subject: firmware: google: framebuffer: Do not mark framebuffer as busy + +From: Thomas Zimmermann + +commit f3850d399de3b6142b02315227ef9e772ed0c302 upstream. + +Remove the flag IORESOURCE_BUSY flag from coreboot's framebuffer +resource. It prevents simpledrm from successfully requesting the +range for its own use; resulting in errors such as + +[ 2.775430] simple-framebuffer simple-framebuffer.0: [drm] could not acquire memory region [mem 0x80000000-0x80407fff flags 0x80000200] + +As with other uses of simple-framebuffer, the simple-framebuffer +device should only declare it's I/O resources, but not actively use +them. + +Signed-off-by: Thomas Zimmermann +Fixes: 851b4c14532d ("firmware: coreboot: Add coreboot framebuffer driver") +Acked-by: Tzung-Bi Shih +Acked-by: Julius Werner +Cc: Samuel Holland +Cc: Greg Kroah-Hartman +Cc: Tzung-Bi Shih +Cc: Brian Norris +Cc: Julius Werner +Cc: chrome-platform@lists.linux.dev +Cc: # v4.18+ +Link: https://patch.msgid.link/20260217155836.96267-3-tzimmermann@suse.de +Signed-off-by: Greg Kroah-Hartman +--- + drivers/firmware/google/framebuffer-coreboot.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/firmware/google/framebuffer-coreboot.c ++++ b/drivers/firmware/google/framebuffer-coreboot.c +@@ -67,7 +67,7 @@ static int framebuffer_probe(struct core + return -ENODEV; + + memset(&res, 0, sizeof(res)); +- res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ res.flags = IORESOURCE_MEM; + res.name = "Coreboot Framebuffer"; + res.start = fb->physical_address; + length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line); diff --git a/queue-7.0/fs-afs-revert-mmap_prepare-change.patch b/queue-7.0/fs-afs-revert-mmap_prepare-change.patch new file mode 100644 index 0000000000..d8cfcc3e05 --- /dev/null +++ b/queue-7.0/fs-afs-revert-mmap_prepare-change.patch @@ -0,0 +1,102 @@ +From fbfc6578eaca12daa0c09df1e9ba7f2c657b49da Mon Sep 17 00:00:00 2001 +From: "Lorenzo Stoakes (Oracle)" +Date: Fri, 20 Mar 2026 22:39:35 +0000 +Subject: fs: afs: revert mmap_prepare() change + +From: Lorenzo Stoakes (Oracle) + +commit fbfc6578eaca12daa0c09df1e9ba7f2c657b49da upstream. + +Partially reverts commit 9d5403b1036c ("fs: convert most other +generic_file_*mmap() users to .mmap_prepare()"). + +This is because the .mmap invocation establishes a refcount, but +.mmap_prepare is called at a point where a merge or an allocation failure +might happen after the call, which would leak the refcount increment. + +Functionality is being added to permit the use of .mmap_prepare in this +case, but in the interim, we need to fix this. + +Link: https://lkml.kernel.org/r/08804c94e39d9102a3a8fbd12385e8aa079ba1d3.1774045440.git.ljs@kernel.org +Fixes: 9d5403b1036c ("fs: convert most other generic_file_*mmap() users to .mmap_prepare()") +Signed-off-by: Lorenzo Stoakes (Oracle) +Acked-by: Vlastimil Babka (SUSE) +Cc: Alexander Shishkin +Cc: Alexandre Torgue +Cc: Al Viro +Cc: Arnd Bergmann +Cc: Bodo Stroesser +Cc: Christian Brauner +Cc: Clemens Ladisch +Cc: David Hildenbrand +Cc: David Howells +Cc: Dexuan Cui +Cc: Greg Kroah-Hartman +Cc: Haiyang Zhang +Cc: Jan Kara +Cc: Jann Horn +Cc: Jonathan Corbet +Cc: K. Y. Srinivasan +Cc: Liam Howlett +Cc: Long Li +Cc: Marc Dionne +Cc: "Martin K. Petersen" +Cc: Maxime Coquelin +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Miquel Raynal +Cc: Pedro Falcato +Cc: Richard Weinberger +Cc: Ryan Roberts +Cc: Suren Baghdasaryan +Cc: Vignesh Raghavendra +Cc: Vlastimil Babka (SUSE) +Cc: Wei Liu +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + fs/afs/file.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/fs/afs/file.c ++++ b/fs/afs/file.c +@@ -19,7 +19,7 @@ + #include + #include "internal.h" + +-static int afs_file_mmap_prepare(struct vm_area_desc *desc); ++static int afs_file_mmap(struct file *file, struct vm_area_struct *vma); + + static ssize_t afs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter); + static ssize_t afs_file_splice_read(struct file *in, loff_t *ppos, +@@ -35,7 +35,7 @@ const struct file_operations afs_file_op + .llseek = generic_file_llseek, + .read_iter = afs_file_read_iter, + .write_iter = netfs_file_write_iter, +- .mmap_prepare = afs_file_mmap_prepare, ++ .mmap = afs_file_mmap, + .splice_read = afs_file_splice_read, + .splice_write = iter_file_splice_write, + .fsync = afs_fsync, +@@ -492,16 +492,16 @@ static void afs_drop_open_mmap(struct af + /* + * Handle setting up a memory mapping on an AFS file. + */ +-static int afs_file_mmap_prepare(struct vm_area_desc *desc) ++static int afs_file_mmap(struct file *file, struct vm_area_struct *vma) + { +- struct afs_vnode *vnode = AFS_FS_I(file_inode(desc->file)); ++ struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); + int ret; + + afs_add_open_mmap(vnode); + +- ret = generic_file_mmap_prepare(desc); ++ ret = generic_file_mmap(file, vma); + if (ret == 0) +- desc->vm_ops = &afs_vm_ops; ++ vma->vm_ops = &afs_vm_ops; + else + afs_drop_open_mmap(vnode); + return ret; diff --git a/queue-7.0/greybus-gb-beagleplay-bound-bootloader-receive-buffering.patch b/queue-7.0/greybus-gb-beagleplay-bound-bootloader-receive-buffering.patch new file mode 100644 index 0000000000..0a041fb8d7 --- /dev/null +++ b/queue-7.0/greybus-gb-beagleplay-bound-bootloader-receive-buffering.patch @@ -0,0 +1,44 @@ +From 1214bf28965ceaf584fb20d357731264dd2e10e1 Mon Sep 17 00:00:00 2001 +From: Pengpeng Hou +Date: Thu, 2 Apr 2026 13:40:16 +0800 +Subject: greybus: gb-beagleplay: bound bootloader receive buffering + +From: Pengpeng Hou + +commit 1214bf28965ceaf584fb20d357731264dd2e10e1 upstream. + +cc1352_bootloader_rx() appends each serdev chunk into the fixed +rx_buffer before parsing bootloader packets. The helper can keep +leftover bytes between callbacks and may receive multiple packets in one +callback, so a single count value is not constrained by one packet +length. + +Check that the incoming chunk fits in the remaining receive buffer space +before memcpy(). If it does not, drop the staged data and consume the +bytes instead of overflowing rx_buffer. + +Fixes: 0cf7befa3ea2 ("greybus: gb-beagleplay: Add firmware upload API") +Cc: stable +Signed-off-by: Pengpeng Hou +Link: https://patch.msgid.link/20260402054016.38587-1-pengpeng@iscas.ac.cn +Signed-off-by: Greg Kroah-Hartman +--- + drivers/greybus/gb-beagleplay.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/greybus/gb-beagleplay.c ++++ b/drivers/greybus/gb-beagleplay.c +@@ -535,6 +535,13 @@ static size_t cc1352_bootloader_rx(struc + int ret; + size_t off = 0; + ++ if (count > sizeof(bg->rx_buffer) - bg->rx_buffer_len) { ++ dev_warn(&bg->sd->dev, ++ "dropping oversized bootloader receive chunk"); ++ bg->rx_buffer_len = 0; ++ return count; ++ } ++ + memcpy(bg->rx_buffer + bg->rx_buffer_len, data, count); + bg->rx_buffer_len += count; + diff --git a/queue-7.0/greybus-gb-beagleplay-fix-sleep-in-atomic-context-in-hdlc_tx_frames.patch b/queue-7.0/greybus-gb-beagleplay-fix-sleep-in-atomic-context-in-hdlc_tx_frames.patch new file mode 100644 index 0000000000..d37d9f922a --- /dev/null +++ b/queue-7.0/greybus-gb-beagleplay-fix-sleep-in-atomic-context-in-hdlc_tx_frames.patch @@ -0,0 +1,180 @@ +From 6b526dca0966f2370835765019a54319b78fca8d Mon Sep 17 00:00:00 2001 +From: Weigang He +Date: Mon, 30 Mar 2026 12:08:00 +0000 +Subject: greybus: gb-beagleplay: fix sleep in atomic context in hdlc_tx_frames() + +From: Weigang He + +commit 6b526dca0966f2370835765019a54319b78fca8d upstream. + +hdlc_append() calls usleep_range() to wait for circular buffer space, +but it is called with tx_producer_lock (a spinlock) held via +hdlc_tx_frames() -> hdlc_append_tx_frame()/hdlc_append_tx_u8()/etc. +Sleeping while holding a spinlock is illegal and can trigger +"BUG: scheduling while atomic". + +Fix this by moving the buffer-space wait out of hdlc_append() and into +hdlc_tx_frames(), before the spinlock is acquired. The new flow: + + 1. Pre-calculate the worst-case encoded frame length. + 2. Wait (with sleep) outside the lock until enough space is available, + kicking the TX consumer work to drain the buffer. + 3. Acquire the spinlock, re-verify space, and write the entire frame + atomically. + +This ensures that sleeping only happens without any lock held, and +that frames are either fully enqueued or not written at all. + +This bug is found by CodeQL static analysis tool (interprocedural +sleep-in-atomic query) and my code review. + +Fixes: ec558bbfea67 ("greybus: Add BeaglePlay Linux Driver") +Cc: stable +Cc: Ayush Singh +Cc: Johan Hovold +Cc: Alex Elder +Cc: Greg Kroah-Hartman +Signed-off-by: Weigang He +Link: https://patch.msgid.link/20260330120801.981506-1-geoffreyhe2@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/greybus/gb-beagleplay.c | 107 +++++++++++++++++++++++++++++++++------- + 1 file changed, 90 insertions(+), 17 deletions(-) + +--- a/drivers/greybus/gb-beagleplay.c ++++ b/drivers/greybus/gb-beagleplay.c +@@ -242,30 +242,26 @@ static void hdlc_write(struct gb_beaglep + } + + /** +- * hdlc_append() - Queue HDLC data for sending. ++ * hdlc_append() - Queue a single HDLC byte for sending. + * @bg: beagleplay greybus driver + * @value: hdlc byte to transmit + * +- * Assumes that producer lock as been acquired. ++ * Caller must hold tx_producer_lock and must have ensured sufficient ++ * space in the circular buffer before calling (see hdlc_tx_frames()). + */ + static void hdlc_append(struct gb_beagleplay *bg, u8 value) + { +- int tail, head = bg->tx_circ_buf.head; ++ int head = bg->tx_circ_buf.head; ++ int tail = READ_ONCE(bg->tx_circ_buf.tail); + +- while (true) { +- tail = READ_ONCE(bg->tx_circ_buf.tail); +- +- if (CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) >= 1) { +- bg->tx_circ_buf.buf[head] = value; +- +- /* Finish producing HDLC byte */ +- smp_store_release(&bg->tx_circ_buf.head, +- (head + 1) & (TX_CIRC_BUF_SIZE - 1)); +- return; +- } +- dev_warn(&bg->sd->dev, "Tx circ buf full"); +- usleep_range(3000, 5000); +- } ++ lockdep_assert_held(&bg->tx_producer_lock); ++ if (WARN_ON_ONCE(CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) < 1)) ++ return; ++ ++ bg->tx_circ_buf.buf[head] = value; ++ /* Ensure buffer write is visible before advancing head. */ ++ smp_store_release(&bg->tx_circ_buf.head, ++ (head + 1) & (TX_CIRC_BUF_SIZE - 1)); + } + + static void hdlc_append_escaped(struct gb_beagleplay *bg, u8 value) +@@ -313,13 +309,90 @@ static void hdlc_transmit(struct work_st + spin_unlock_bh(&bg->tx_consumer_lock); + } + ++/** ++ * hdlc_encoded_length() - Calculate worst-case encoded length of an HDLC frame. ++ * @payloads: array of payload buffers ++ * @count: number of payloads ++ * ++ * Returns the maximum number of bytes needed in the circular buffer. ++ */ ++static size_t hdlc_encoded_length(const struct hdlc_payload payloads[], ++ size_t count) ++{ ++ size_t i, payload_len = 0; ++ ++ for (i = 0; i < count; i++) ++ payload_len += payloads[i].len; ++ ++ /* ++ * Worst case: every data byte needs escaping (doubles in size). ++ * data bytes = address(1) + control(1) + payload + crc(2) ++ * framing = opening flag(1) + closing flag(1) ++ */ ++ return 2 + (1 + 1 + payload_len + 2) * 2; ++} ++ ++#define HDLC_TX_BUF_WAIT_RETRIES 500 ++#define HDLC_TX_BUF_WAIT_US_MIN 3000 ++#define HDLC_TX_BUF_WAIT_US_MAX 5000 ++ ++/** ++ * hdlc_tx_frames() - Encode and queue an HDLC frame for transmission. ++ * @bg: beagleplay greybus driver ++ * @address: HDLC address field ++ * @control: HDLC control field ++ * @payloads: array of payload buffers ++ * @count: number of payloads ++ * ++ * Sleeps outside the spinlock until enough circular-buffer space is ++ * available, then verifies space under the lock and writes the entire ++ * frame atomically. Either a complete frame is enqueued or nothing is ++ * written, avoiding both sleeping in atomic context and partial frames. ++ */ + static void hdlc_tx_frames(struct gb_beagleplay *bg, u8 address, u8 control, + const struct hdlc_payload payloads[], size_t count) + { ++ size_t needed = hdlc_encoded_length(payloads, count); ++ int retries = HDLC_TX_BUF_WAIT_RETRIES; + size_t i; ++ int head, tail; ++ ++ /* Wait outside the lock for sufficient buffer space. */ ++ while (retries--) { ++ /* Pairs with smp_store_release() in hdlc_append(). */ ++ head = smp_load_acquire(&bg->tx_circ_buf.head); ++ tail = READ_ONCE(bg->tx_circ_buf.tail); ++ ++ if (CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) >= needed) ++ break; ++ ++ /* Kick the consumer and sleep — no lock held. */ ++ schedule_work(&bg->tx_work); ++ usleep_range(HDLC_TX_BUF_WAIT_US_MIN, HDLC_TX_BUF_WAIT_US_MAX); ++ } ++ ++ if (retries < 0) { ++ dev_warn_ratelimited(&bg->sd->dev, ++ "Tx circ buf full, dropping frame\n"); ++ return; ++ } + + spin_lock(&bg->tx_producer_lock); + ++ /* ++ * Re-check under the lock. Should not fail since ++ * tx_producer_lock serialises all producers and the ++ * consumer only frees space, but guard against it. ++ */ ++ head = bg->tx_circ_buf.head; ++ tail = READ_ONCE(bg->tx_circ_buf.tail); ++ if (unlikely(CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) < needed)) { ++ spin_unlock(&bg->tx_producer_lock); ++ dev_warn_ratelimited(&bg->sd->dev, ++ "Tx circ buf space lost, dropping frame\n"); ++ return; ++ } ++ + hdlc_append_tx_frame(bg); + hdlc_append_tx_u8(bg, address); + hdlc_append_tx_u8(bg, control); diff --git a/queue-7.0/ibmasm-fix-heap-over-read-in-ibmasm_send_i2o_message.patch b/queue-7.0/ibmasm-fix-heap-over-read-in-ibmasm_send_i2o_message.patch new file mode 100644 index 0000000000..5cd0f2d07c --- /dev/null +++ b/queue-7.0/ibmasm-fix-heap-over-read-in-ibmasm_send_i2o_message.patch @@ -0,0 +1,72 @@ +From 9aad71144fa3682cca3837a06c8623016790e7ec Mon Sep 17 00:00:00 2001 +From: Tyllis Xu +Date: Sat, 14 Mar 2026 11:58:05 -0500 +Subject: ibmasm: fix heap over-read in ibmasm_send_i2o_message() + +From: Tyllis Xu + +commit 9aad71144fa3682cca3837a06c8623016790e7ec upstream. + +The ibmasm_send_i2o_message() function uses get_dot_command_size() to +compute the byte count for memcpy_toio(), but this value is derived from +user-controlled fields in the dot_command_header (command_size: u8, +data_size: u16) and is never validated against the actual allocation size. +A root user can write a small buffer with inflated header fields, causing +memcpy_toio() to read up to ~65 KB past the end of the allocation into +adjacent kernel heap, which is then forwarded to the service processor +over MMIO. + +Silently clamping the copy size is not sufficient: if the header fields +claim a larger size than the buffer, the SP receives a dot command whose +own header is inconsistent with the I2O message length, which can cause +the SP to desynchronize. Reject such commands outright by returning +failure. + +Validate command_size before calling get_mfa_inbound() to avoid leaking +an I2O message frame: reading INBOUND_QUEUE_PORT dequeues a hardware +frame from the controller's free pool, and returning without a +corresponding set_mfa_inbound() call would permanently exhaust it. + +Additionally, clamp command_size to I2O_COMMAND_SIZE before the +memcpy_toio() so the MMIO write stays within the I2O message frame, +consistent with the clamping already performed by outgoing_message_size() +for the header field. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Reported-by: Yuhao Jiang +Cc: stable@vger.kernel.org +Signed-off-by: Tyllis Xu +Link: https://patch.msgid.link/20260314165805.548293-1-LivelyCarpet87@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/misc/ibmasm/lowlevel.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +--- a/drivers/misc/ibmasm/lowlevel.c ++++ b/drivers/misc/ibmasm/lowlevel.c +@@ -19,17 +19,21 @@ static struct i2o_header header = I2O_HE + int ibmasm_send_i2o_message(struct service_processor *sp) + { + u32 mfa; +- unsigned int command_size; ++ size_t command_size; + struct i2o_message *message; + struct command *command = sp->current_command; + ++ command_size = get_dot_command_size(command->buffer); ++ if (command_size > command->buffer_size) ++ return 1; ++ if (command_size > I2O_COMMAND_SIZE) ++ command_size = I2O_COMMAND_SIZE; ++ + mfa = get_mfa_inbound(sp->base_address); + if (!mfa) + return 1; + +- command_size = get_dot_command_size(command->buffer); +- header.message_size = outgoing_message_size(command_size); +- ++ header.message_size = outgoing_message_size((unsigned int)command_size); + message = get_i2o_message(sp->base_address, mfa); + + memcpy_toio(&message->header, &header, sizeof(struct i2o_header)); diff --git a/queue-7.0/ibmasm-fix-oob-reads-in-command_file_write-due-to-missing-size-checks.patch b/queue-7.0/ibmasm-fix-oob-reads-in-command_file_write-due-to-missing-size-checks.patch new file mode 100644 index 0000000000..fdf6a0b5d1 --- /dev/null +++ b/queue-7.0/ibmasm-fix-oob-reads-in-command_file_write-due-to-missing-size-checks.patch @@ -0,0 +1,59 @@ +From 0eb09f737428e482a32a2e31e5e223f2b35a71d3 Mon Sep 17 00:00:00 2001 +From: Tyllis Xu +Date: Sat, 14 Mar 2026 11:53:54 -0500 +Subject: ibmasm: fix OOB reads in command_file_write due to missing size checks + +From: Tyllis Xu + +commit 0eb09f737428e482a32a2e31e5e223f2b35a71d3 upstream. + +The command_file_write() handler allocates a kernel buffer of exactly +count bytes and copies user data into it, but does not validate the +buffer against the dot command protocol before passing it to +get_dot_command_size() and get_dot_command_timeout(). + +Since both the allocation size (count) and the header fields (command_size, +data_size) are independently user-controlled, an attacker can cause +get_dot_command_size() to return a value exceeding the allocation, +triggering OOB reads in get_dot_command_timeout() and an out-of-bounds +memcpy_toio() that leaks kernel heap memory to the service processor. + +Fix with two guards: reject writes smaller than sizeof(struct +dot_command_header) before allocation, then after copying user data +reject commands where the buffer is smaller than the total size declared +by the header (sizeof(header) + command_size + data_size). This ensures +all subsequent header and payload field accesses stay within the buffer. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Reported-by: Yuhao Jiang +Cc: stable@vger.kernel.org +Signed-off-by: Tyllis Xu +Link: https://patch.msgid.link/20260314165355.548119-1-LivelyCarpet87@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/misc/ibmasm/ibmasmfs.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/misc/ibmasm/ibmasmfs.c ++++ b/drivers/misc/ibmasm/ibmasmfs.c +@@ -303,6 +303,8 @@ static ssize_t command_file_write(struct + return -EINVAL; + if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE) + return 0; ++ if (count < sizeof(struct dot_command_header)) ++ return -EINVAL; + if (*offset != 0) + return 0; + +@@ -319,6 +321,11 @@ static ssize_t command_file_write(struct + return -EFAULT; + } + ++ if (count < get_dot_command_size(cmd->buffer)) { ++ command_put(cmd); ++ return -EINVAL; ++ } ++ + spin_lock_irqsave(&command_data->sp->lock, flags); + if (command_data->command) { + spin_unlock_irqrestore(&command_data->sp->lock, flags); diff --git a/queue-7.0/kbuild-rust-allow-clippy-uninlined_format_args.patch b/queue-7.0/kbuild-rust-allow-clippy-uninlined_format_args.patch new file mode 100644 index 0000000000..36ea9db870 --- /dev/null +++ b/queue-7.0/kbuild-rust-allow-clippy-uninlined_format_args.patch @@ -0,0 +1,72 @@ +From 10eea3c147141c90cf409b8df56d245c9d7f88d9 Mon Sep 17 00:00:00 2001 +From: Miguel Ojeda +Date: Tue, 31 Mar 2026 22:58:48 +0200 +Subject: kbuild: rust: allow `clippy::uninlined_format_args` + +From: Miguel Ojeda + +commit 10eea3c147141c90cf409b8df56d245c9d7f88d9 upstream. + +Clippy in Rust 1.88.0 (only) reports [1]: + + warning: variables can be used directly in the `format!` string + --> rust/macros/module.rs:112:23 + | + 112 | let content = format!("{param}:{content}", param = param, content = content); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args + = note: `-W clippy::uninlined-format-args` implied by `-W clippy::all` + = help: to override `-W clippy::all` add `#[allow(clippy::uninlined_format_args)]` + help: change this to + | + 112 - let content = format!("{param}:{content}", param = param, content = content); + 112 + let content = format!("{param}:{content}"); + + warning: variables can be used directly in the `format!` string + --> rust/macros/module.rs:198:14 + | + 198 | t => panic!("Unsupported parameter type {}", t), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args + = note: `-W clippy::uninlined-format-args` implied by `-W clippy::all` + = help: to override `-W clippy::all` add `#[allow(clippy::uninlined_format_args)]` + help: change this to + | + 198 - t => panic!("Unsupported parameter type {}", t), + 198 + t => panic!("Unsupported parameter type {t}"), + | + +The reason it only triggers in that version is that the lint was moved +from `pedantic` to `style` in Rust 1.88.0 and then back to `pedantic` +in Rust 1.89.0 [2][3]. + +In the first case, the suggestion is fair and a pure simplification, thus +we will clean it up separately. + +To keep the behavior the same across all versions, and since the lint +does not work for all macros (e.g. custom ones like `pr_info!`), disable +it globally. + +Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). +Link: https://lore.kernel.org/rust-for-linux/CANiq72=drAtf3y_DZ-2o4jb6Az9J3Yj4QYwWnbRui4sm4AJD3Q@mail.gmail.com/ [1] +Link: https://github.com/rust-lang/rust-clippy/pull/15287 [2] +Link: https://github.com/rust-lang/rust-clippy/issues/15151 [3] +Link: https://patch.msgid.link/20260331205849.498295-1-ojeda@kernel.org +Signed-off-by: Miguel Ojeda +Signed-off-by: Greg Kroah-Hartman +--- + Makefile | 1 + + 1 file changed, 1 insertion(+) + +--- a/Makefile ++++ b/Makefile +@@ -495,6 +495,7 @@ export rust_common_flags := --edition=20 + -Wclippy::ptr_cast_constness \ + -Wclippy::ref_as_ptr \ + -Wclippy::undocumented_unsafe_blocks \ ++ -Aclippy::uninlined_format_args \ + -Wclippy::unnecessary_safety_comment \ + -Wclippy::unnecessary_safety_doc \ + -Wrustdoc::missing_crate_level_docs \ diff --git a/queue-7.0/leds-qcom-lpg-check-for-array-overflow-when-selecting-the-high-resolution.patch b/queue-7.0/leds-qcom-lpg-check-for-array-overflow-when-selecting-the-high-resolution.patch new file mode 100644 index 0000000000..ac32b2dd82 --- /dev/null +++ b/queue-7.0/leds-qcom-lpg-check-for-array-overflow-when-selecting-the-high-resolution.patch @@ -0,0 +1,40 @@ +From d45963a93c1495e9f1338fde91d0ebba8fd22474 Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Thu, 19 Feb 2026 15:34:35 +0100 +Subject: leds: qcom-lpg: Check for array overflow when selecting the high resolution + +From: Greg Kroah-Hartman + +commit d45963a93c1495e9f1338fde91d0ebba8fd22474 upstream. + +When selecting the high resolution values from the array, FIELD_GET() is +used to pull from a 3 bit register, yet the array being indexed has only +5 values in it. Odds are the hardware is sane, but just to be safe, +properly check before just overflowing and reading random data and then +setting up chip values based on that. + +Cc: stable +Signed-off-by: Greg Kroah-Hartman +Link: https://patch.msgid.link/2026021934-nearby-playroom-036b@gregkh +Signed-off-by: Lee Jones +Signed-off-by: Greg Kroah-Hartman +--- + drivers/leds/rgb/leds-qcom-lpg.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/leds/rgb/leds-qcom-lpg.c ++++ b/drivers/leds/rgb/leds-qcom-lpg.c +@@ -1273,7 +1273,12 @@ static int lpg_pwm_get_state(struct pwm_ + return ret; + + if (chan->subtype == LPG_SUBTYPE_HI_RES_PWM) { +- refclk = lpg_clk_rates_hi_res[FIELD_GET(PWM_CLK_SELECT_HI_RES_MASK, val)]; ++ unsigned int clk_idx = FIELD_GET(PWM_CLK_SELECT_HI_RES_MASK, val); ++ ++ if (clk_idx >= ARRAY_SIZE(lpg_clk_rates_hi_res)) ++ return -EINVAL; ++ ++ refclk = lpg_clk_rates_hi_res[clk_idx]; + resolution = lpg_pwm_resolution_hi_res[FIELD_GET(PWM_SIZE_HI_RES_MASK, val)]; + } else { + refclk = lpg_clk_rates[FIELD_GET(PWM_CLK_SELECT_MASK, val)]; diff --git a/queue-7.0/lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch b/queue-7.0/lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch new file mode 100644 index 0000000000..102d88158d --- /dev/null +++ b/queue-7.0/lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch @@ -0,0 +1,192 @@ +From 744dd97752ef1076a8d8672bb0d8aa2c7abc1144 Mon Sep 17 00:00:00 2001 +From: Alistair Popple +Date: Tue, 31 Mar 2026 17:34:43 +1100 +Subject: lib: test_hmm: evict device pages on file close to avoid use-after-free + +From: Alistair Popple + +commit 744dd97752ef1076a8d8672bb0d8aa2c7abc1144 upstream. + +Patch series "Minor hmm_test fixes and cleanups". + +Two bugfixes a cleanup for the HMM kernel selftests. These were mostly +reported by Zenghui Yu with special thanks to Lorenzo for analysing and +pointing out the problems. + + +This patch (of 3): + +When dmirror_fops_release() is called it frees the dmirror struct but +doesn't migrate device private pages back to system memory first. This +leaves those pages with a dangling zone_device_data pointer to the freed +dmirror. + +If a subsequent fault occurs on those pages (eg. during coredump) the +dmirror_devmem_fault() callback dereferences the stale pointer causing a +kernel panic. This was reported [1] when running mm/ksft_hmm.sh on arm64, +where a test failure triggered SIGABRT and the resulting coredump walked +the VMAs faulting in the stale device private pages. + +Fix this by calling dmirror_device_evict_chunk() for each devmem chunk in +dmirror_fops_release() to migrate all device private pages back to system +memory before freeing the dmirror struct. The function is moved earlier +in the file to avoid a forward declaration. + +Link: https://lore.kernel.org/20260331063445.3551404-1-apopple@nvidia.com +Link: https://lore.kernel.org/20260331063445.3551404-2-apopple@nvidia.com +Fixes: b2ef9f5a5cb3 ("mm/hmm/test: add selftest driver for HMM") +Signed-off-by: Alistair Popple +Reported-by: Zenghui Yu +Closes: https://lore.kernel.org/linux-mm/8bd0396a-8997-4d2e-a13f-5aac033083d7@linux.dev/ +Reviewed-by: Balbir Singh +Tested-by: Zenghui Yu +Cc: David Hildenbrand +Cc: Jason Gunthorpe +Cc: Leon Romanovsky +Cc: Liam Howlett +Cc: Lorenzo Stoakes (Oracle) +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Suren Baghdasaryan +Cc: Zenghui Yu +Cc: Matthew Brost +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + lib/test_hmm.c | 112 +++++++++++++++++++++++++++++++-------------------------- + 1 file changed, 62 insertions(+), 50 deletions(-) + +--- a/lib/test_hmm.c ++++ b/lib/test_hmm.c +@@ -185,11 +185,73 @@ static int dmirror_fops_open(struct inod + return 0; + } + ++static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk) ++{ ++ unsigned long start_pfn = chunk->pagemap.range.start >> PAGE_SHIFT; ++ unsigned long end_pfn = chunk->pagemap.range.end >> PAGE_SHIFT; ++ unsigned long npages = end_pfn - start_pfn + 1; ++ unsigned long i; ++ unsigned long *src_pfns; ++ unsigned long *dst_pfns; ++ unsigned int order = 0; ++ ++ src_pfns = kvcalloc(npages, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); ++ dst_pfns = kvcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); ++ ++ migrate_device_range(src_pfns, start_pfn, npages); ++ for (i = 0; i < npages; i++) { ++ struct page *dpage, *spage; ++ ++ spage = migrate_pfn_to_page(src_pfns[i]); ++ if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE)) ++ continue; ++ ++ if (WARN_ON(!is_device_private_page(spage) && ++ !is_device_coherent_page(spage))) ++ continue; ++ ++ order = folio_order(page_folio(spage)); ++ spage = BACKING_PAGE(spage); ++ if (src_pfns[i] & MIGRATE_PFN_COMPOUND) { ++ dpage = folio_page(folio_alloc(GFP_HIGHUSER_MOVABLE, ++ order), 0); ++ } else { ++ dpage = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NOFAIL); ++ order = 0; ++ } ++ ++ /* TODO Support splitting here */ ++ lock_page(dpage); ++ dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)); ++ if (src_pfns[i] & MIGRATE_PFN_WRITE) ++ dst_pfns[i] |= MIGRATE_PFN_WRITE; ++ if (order) ++ dst_pfns[i] |= MIGRATE_PFN_COMPOUND; ++ folio_copy(page_folio(dpage), page_folio(spage)); ++ } ++ migrate_device_pages(src_pfns, dst_pfns, npages); ++ migrate_device_finalize(src_pfns, dst_pfns, npages); ++ kvfree(src_pfns); ++ kvfree(dst_pfns); ++} ++ + static int dmirror_fops_release(struct inode *inode, struct file *filp) + { + struct dmirror *dmirror = filp->private_data; ++ struct dmirror_device *mdevice = dmirror->mdevice; ++ int i; + + mmu_interval_notifier_remove(&dmirror->notifier); ++ ++ if (mdevice->devmem_chunks) { ++ for (i = 0; i < mdevice->devmem_count; i++) { ++ struct dmirror_chunk *devmem = ++ mdevice->devmem_chunks[i]; ++ ++ dmirror_device_evict_chunk(devmem); ++ } ++ } ++ + xa_destroy(&dmirror->pt); + kfree(dmirror); + return 0; +@@ -1377,56 +1439,6 @@ static int dmirror_snapshot(struct dmirr + return ret; + } + +-static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk) +-{ +- unsigned long start_pfn = chunk->pagemap.range.start >> PAGE_SHIFT; +- unsigned long end_pfn = chunk->pagemap.range.end >> PAGE_SHIFT; +- unsigned long npages = end_pfn - start_pfn + 1; +- unsigned long i; +- unsigned long *src_pfns; +- unsigned long *dst_pfns; +- unsigned int order = 0; +- +- src_pfns = kvcalloc(npages, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); +- dst_pfns = kvcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); +- +- migrate_device_range(src_pfns, start_pfn, npages); +- for (i = 0; i < npages; i++) { +- struct page *dpage, *spage; +- +- spage = migrate_pfn_to_page(src_pfns[i]); +- if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE)) +- continue; +- +- if (WARN_ON(!is_device_private_page(spage) && +- !is_device_coherent_page(spage))) +- continue; +- +- order = folio_order(page_folio(spage)); +- spage = BACKING_PAGE(spage); +- if (src_pfns[i] & MIGRATE_PFN_COMPOUND) { +- dpage = folio_page(folio_alloc(GFP_HIGHUSER_MOVABLE, +- order), 0); +- } else { +- dpage = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NOFAIL); +- order = 0; +- } +- +- /* TODO Support splitting here */ +- lock_page(dpage); +- dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)); +- if (src_pfns[i] & MIGRATE_PFN_WRITE) +- dst_pfns[i] |= MIGRATE_PFN_WRITE; +- if (order) +- dst_pfns[i] |= MIGRATE_PFN_COMPOUND; +- folio_copy(page_folio(dpage), page_folio(spage)); +- } +- migrate_device_pages(src_pfns, dst_pfns, npages); +- migrate_device_finalize(src_pfns, dst_pfns, npages); +- kvfree(src_pfns); +- kvfree(dst_pfns); +-} +- + /* Removes free pages from the free list so they can't be re-allocated */ + static void dmirror_remove_free_pages(struct dmirror_chunk *devmem) + { diff --git a/queue-7.0/loongarch-add-spectre-boundry-for-syscall-dispatch-table.patch b/queue-7.0/loongarch-add-spectre-boundry-for-syscall-dispatch-table.patch new file mode 100644 index 0000000000..d1bcc58cc0 --- /dev/null +++ b/queue-7.0/loongarch-add-spectre-boundry-for-syscall-dispatch-table.patch @@ -0,0 +1,41 @@ +From 0c965d2784fbbd7f8e3b96d875c9cfdf7c00da3d Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Wed, 22 Apr 2026 15:45:12 +0800 +Subject: LoongArch: Add spectre boundry for syscall dispatch table + +From: Greg Kroah-Hartman + +commit 0c965d2784fbbd7f8e3b96d875c9cfdf7c00da3d upstream. + +The LoongArch syscall number is directly controlled by userspace, but +does not have a array_index_nospec() boundry to prevent access past the +syscall function pointer tables. + +Cc: stable@vger.kernel.org +Assisted-by: gkh_clanker_2000 +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Huacai Chen +Signed-off-by: Greg Kroah-Hartman +--- + arch/loongarch/kernel/syscall.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/arch/loongarch/kernel/syscall.c ++++ b/arch/loongarch/kernel/syscall.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -74,7 +75,7 @@ void noinstr __no_stack_protector do_sys + add_random_kstack_offset(); + + if (nr < NR_syscalls) { +- syscall_fn = sys_call_table[nr]; ++ syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)]; + regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6], + regs->regs[7], regs->regs[8], regs->regs[9]); + } diff --git a/queue-7.0/misc-ibmasm-fix-oob-mmio-read-in-ibmasm_handle_mouse_interrupt.patch b/queue-7.0/misc-ibmasm-fix-oob-mmio-read-in-ibmasm_handle_mouse_interrupt.patch new file mode 100644 index 0000000000..e612ad028b --- /dev/null +++ b/queue-7.0/misc-ibmasm-fix-oob-mmio-read-in-ibmasm_handle_mouse_interrupt.patch @@ -0,0 +1,62 @@ +From 4b6e6ead556734bdc14024c5f837132b1e7a4b84 Mon Sep 17 00:00:00 2001 +From: Tyllis Xu +Date: Sun, 8 Mar 2026 00:21:08 -0600 +Subject: misc: ibmasm: fix OOB MMIO read in ibmasm_handle_mouse_interrupt() + +From: Tyllis Xu + +commit 4b6e6ead556734bdc14024c5f837132b1e7a4b84 upstream. + +ibmasm_handle_mouse_interrupt() performs an out-of-bounds MMIO read +when the queue reader or writer index from hardware exceeds +REMOTE_QUEUE_SIZE (60). + +A compromised service processor can trigger this by writing an +out-of-range value to the reader or writer MMIO register before +asserting an interrupt. Since writer is re-read from hardware on +every loop iteration, it can also be set to an out-of-range value +after the loop has already started. + +The root cause is that get_queue_reader() and get_queue_writer() return +raw readl() values that are passed directly into get_queue_entry(), +which computes: + + queue_begin + reader * sizeof(struct remote_input) + +with no bounds check. This unchecked MMIO address is then passed to +memcpy_fromio(), reading 8 bytes from unintended device registers. +For sufficiently large values the address falls outside the PCI BAR +mapping entirely, triggering a machine check exception. + +Fix by checking both indices against REMOTE_QUEUE_SIZE at the top of +the loop body, before any call to get_queue_entry(). On an out-of-range +value, reset the reader register to 0 via set_queue_reader() before +breaking, so that normal queue operation can resume if the corrupted +hardware state is transient. + +Reported-by: Yuhao Jiang +Fixes: 278d72ae8803 ("[PATCH] ibmasm driver: redesign handling of remote control events") +Cc: stable@vger.kernel.org +Cc: ychen@northwestern.edu +Signed-off-by: Tyllis Xu +Link: https://patch.msgid.link/20260308062108.258940-1-LivelyCarpet87@gmail.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Greg Kroah-Hartman +--- + drivers/misc/ibmasm/remote.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/misc/ibmasm/remote.c ++++ b/drivers/misc/ibmasm/remote.c +@@ -177,6 +177,11 @@ void ibmasm_handle_mouse_interrupt(struc + writer = get_queue_writer(sp); + + while (reader != writer) { ++ if (reader >= REMOTE_QUEUE_SIZE || writer >= REMOTE_QUEUE_SIZE) { ++ set_queue_reader(sp, 0); ++ break; ++ } ++ + memcpy_fromio(&input, get_queue_entry(sp, reader), + sizeof(struct remote_input)); + diff --git a/queue-7.0/mm-fix-deferred-split-queue-races-during-migration.patch b/queue-7.0/mm-fix-deferred-split-queue-races-during-migration.patch new file mode 100644 index 0000000000..7c16891822 --- /dev/null +++ b/queue-7.0/mm-fix-deferred-split-queue-races-during-migration.patch @@ -0,0 +1,130 @@ +From 3bac01168982ec3e3bf87efdc1807c7933590a85 Mon Sep 17 00:00:00 2001 +From: Lance Yang +Date: Wed, 1 Apr 2026 21:10:32 +0800 +Subject: mm: fix deferred split queue races during migration + +From: Lance Yang + +commit 3bac01168982ec3e3bf87efdc1807c7933590a85 upstream. + +migrate_folio_move() records the deferred split queue state from src and +replays it on dst. Replaying it after remove_migration_ptes(src, dst, 0) +makes dst visible before it is requeued, so a concurrent rmap-removal path +can mark dst partially mapped and trip the WARN in deferred_split_folio(). + +Move the requeue before remove_migration_ptes() so dst is back on the +deferred split queue before it becomes visible again. + +Because migration still holds dst locked at that point, teach +deferred_split_scan() to requeue a folio when folio_trylock() fails. +Otherwise a fully mapped underused folio can be dequeued by the shrinker +and silently lost from split_queue. + +[ziy@nvidia.com: move the comment] + Link: https://lkml.kernel.org/r/FB71A764-0F10-4E5A-B4A0-BA4C7F138408@nvidia.com +Link: https://syzkaller.appspot.com/bug?extid=a7067a757858ac8eb085 +Link: https://lkml.kernel.org/r/20260401131032.13011-1-lance.yang@linux.dev +Fixes: 8a8ca142a488 ("mm: migrate: requeue destination folio on deferred split queue") +Signed-off-by: Lance Yang +Signed-off-by: Zi Yan +Reported-by: syzbot+a7067a757858ac8eb085@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/linux-mm/69ccb65b.050a0220.183828.003a.GAE@google.com/ +Suggested-by: David Hildenbrand (Arm) +Acked-by: David Hildenbrand (Arm) +Acked-by: Zi Yan +Cc: Alistair Popple +Cc: Baolin Wang +Cc: Barry Song +Cc: Byungchul Park +Cc: David Hildenbrand +Cc: Deepanshu Kartikey +Cc: Dev Jain +Cc: Gregory Price +Cc: "Huang, Ying" +Cc: Joshua Hahn +Cc: Lance Yang +Cc: Liam Howlett +Cc: Lorenzo Stoakes (Oracle) +Cc: Matthew Brost +Cc: Nico Pache +Cc: Rakie Kim +Cc: Ryan Roberts +Cc: Wei Yang +Cc: Ying Huang +Cc: Usama Arif +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + mm/huge_memory.c | 15 ++++++++++----- + mm/migrate.c | 18 +++++++++--------- + 2 files changed, 19 insertions(+), 14 deletions(-) + +--- a/mm/huge_memory.c ++++ b/mm/huge_memory.c +@@ -4456,7 +4456,7 @@ retry: + goto next; + } + if (!folio_trylock(folio)) +- goto next; ++ goto requeue; + if (!split_folio(folio)) { + did_split = true; + if (underused) +@@ -4465,13 +4465,18 @@ retry: + } + folio_unlock(folio); + next: ++ /* ++ * If thp_underused() returns false, or if split_folio() ++ * succeeds, or if split_folio() fails in the case it was ++ * underused, then consider it used and don't add it back to ++ * split_queue. ++ */ + if (did_split || !folio_test_partially_mapped(folio)) + continue; ++requeue: + /* +- * Only add back to the queue if folio is partially mapped. +- * If thp_underused returns false, or if split_folio fails +- * in the case it was underused, then consider it used and +- * don't add it back to split_queue. ++ * Add back partially mapped folios, or underused folios that ++ * we could not lock this round. + */ + fqueue = folio_split_queue_lock_irqsave(folio, &flags); + if (list_empty(&folio->_deferred_list)) { +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -1384,6 +1384,15 @@ static int migrate_folio_move(free_folio + goto out; + + /* ++ * Requeue the destination folio on the deferred split queue if ++ * the source was on the queue. The source is unqueued in ++ * __folio_migrate_mapping(), so we recorded the state from ++ * before move_to_new_folio(). ++ */ ++ if (src_deferred_split) ++ deferred_split_folio(dst, src_partially_mapped); ++ ++ /* + * When successful, push dst to LRU immediately: so that if it + * turns out to be an mlocked page, remove_migration_ptes() will + * automatically build up the correct dst->mlock_count for it. +@@ -1399,15 +1408,6 @@ static int migrate_folio_move(free_folio + if (old_page_state & PAGE_WAS_MAPPED) + remove_migration_ptes(src, dst, 0); + +- /* +- * Requeue the destination folio on the deferred split queue if +- * the source was on the queue. The source is unqueued in +- * __folio_migrate_mapping(), so we recorded the state from +- * before move_to_new_folio(). +- */ +- if (src_deferred_split) +- deferred_split_folio(dst, src_partially_mapped); +- + out_unlock_both: + folio_unlock(dst); + folio_set_owner_migrate_reason(dst, reason); diff --git a/queue-7.0/mm-migrate-requeue-destination-folio-on-deferred-split-queue.patch b/queue-7.0/mm-migrate-requeue-destination-folio-on-deferred-split-queue.patch new file mode 100644 index 0000000000..35adfea9d5 --- /dev/null +++ b/queue-7.0/mm-migrate-requeue-destination-folio-on-deferred-split-queue.patch @@ -0,0 +1,97 @@ +From a2e0c0668a3486f96b86c50e02872c8e94fd4f9c Mon Sep 17 00:00:00 2001 +From: Usama Arif +Date: Thu, 12 Mar 2026 03:47:23 -0700 +Subject: mm: migrate: requeue destination folio on deferred split queue + +From: Usama Arif + +commit a2e0c0668a3486f96b86c50e02872c8e94fd4f9c upstream. + +During folio migration, __folio_migrate_mapping() removes the source folio +from the deferred split queue, but the destination folio is never +re-queued. This causes underutilized THPs to escape the shrinker after +NUMA migration, since they silently drop off the deferred split list. + +Fix this by recording whether the source folio was on the deferred split +queue and its partially mapped state before move_to_new_folio() unqueues +it, and re-queuing the destination folio after a successful migration if +it was. + +By the time migrate_folio_move() runs, partially mapped folios without a +pin have already been split by migrate_pages_batch(). So only two cases +remain on the deferred list at this point: + 1. Partially mapped folios with a pin (split failed). + 2. Fully mapped but potentially underused folios. The recorded + partially_mapped state is forwarded to deferred_split_folio() so that + the destination folio is correctly re-queued in both cases. + +Because THPs are removed from the deferred_list, THP shinker cannot +split the underutilized THPs in time. As a result, users will show +less free memory than before. + +Link: https://lkml.kernel.org/r/20260312104723.1351321-1-usama.arif@linux.dev +Fixes: dafff3f4c850 ("mm: split underused THPs") +Signed-off-by: Usama Arif +Reported-by: Johannes Weiner +Acked-by: Johannes Weiner +Acked-by: Zi Yan +Acked-by: David Hildenbrand (Arm) +Acked-by: SeongJae Park +Reviewed-by: Wei Yang +Cc: Alistair Popple +Cc: Byungchul Park +Cc: Gregory Price +Cc: "Huang, Ying" +Cc: Joshua Hahn +Cc: Matthew Brost +Cc: Matthew Wilcox (Oracle) +Cc: Nico Pache +Cc: Rakie Kim +Cc: Ying Huang +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + mm/migrate.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -1358,6 +1358,8 @@ static int migrate_folio_move(free_folio + int rc; + int old_page_state = 0; + struct anon_vma *anon_vma = NULL; ++ bool src_deferred_split = false; ++ bool src_partially_mapped = false; + struct list_head *prev; + + __migrate_folio_extract(dst, &old_page_state, &anon_vma); +@@ -1371,6 +1373,12 @@ static int migrate_folio_move(free_folio + goto out_unlock_both; + } + ++ if (folio_order(src) > 1 && ++ !data_race(list_empty(&src->_deferred_list))) { ++ src_deferred_split = true; ++ src_partially_mapped = folio_test_partially_mapped(src); ++ } ++ + rc = move_to_new_folio(dst, src, mode); + if (rc) + goto out; +@@ -1391,6 +1399,15 @@ static int migrate_folio_move(free_folio + if (old_page_state & PAGE_WAS_MAPPED) + remove_migration_ptes(src, dst, 0); + ++ /* ++ * Requeue the destination folio on the deferred split queue if ++ * the source was on the queue. The source is unqueued in ++ * __folio_migrate_mapping(), so we recorded the state from ++ * before move_to_new_folio(). ++ */ ++ if (src_deferred_split) ++ deferred_split_folio(dst, src_partially_mapped); ++ + out_unlock_both: + folio_unlock(dst); + folio_set_owner_migrate_reason(dst, reason); diff --git a/queue-7.0/mm-prevent-droppable-mappings-from-being-locked.patch b/queue-7.0/mm-prevent-droppable-mappings-from-being-locked.patch new file mode 100644 index 0000000000..c664879ee1 --- /dev/null +++ b/queue-7.0/mm-prevent-droppable-mappings-from-being-locked.patch @@ -0,0 +1,123 @@ +From d239462787b072c78eb19fc1f155c3d411256282 Mon Sep 17 00:00:00 2001 +From: Anthony Yznaga +Date: Tue, 10 Mar 2026 08:58:20 -0700 +Subject: mm: prevent droppable mappings from being locked + +From: Anthony Yznaga + +commit d239462787b072c78eb19fc1f155c3d411256282 upstream. + +Droppable mappings must not be lockable. There is a check for VMAs with +VM_DROPPABLE set in mlock_fixup() along with checks for other types of +unlockable VMAs which ensures this when calling mlock()/mlock2(). + +For mlockall(MCL_FUTURE), the check for unlockable VMAs is different. In +apply_mlockall_flags(), if the flags parameter has MCL_FUTURE set, the +current task's mm's default VMA flag field mm->def_flags has VM_LOCKED +applied to it. VM_LOCKONFAULT is also applied if MCL_ONFAULT is also set. +When these flags are set as default in this manner they are cleared in +__mmap_complete() for new mappings that do not support mlock. A check for +VM_DROPPABLE in __mmap_complete() is missing resulting in droppable +mappings created with VM_LOCKED set. To fix this and reduce that chance +of similar bugs in the future, introduce and use vma_supports_mlock(). + +Link: https://lkml.kernel.org/r/20260310155821.17869-1-anthony.yznaga@oracle.com +Fixes: 9651fcedf7b9 ("mm: add MAP_DROPPABLE for designating always lazily freeable mappings") +Signed-off-by: Anthony Yznaga +Suggested-by: David Hildenbrand +Acked-by: David Hildenbrand (Arm) +Reviewed-by: Pedro Falcato +Reviewed-by: Lorenzo Stoakes (Oracle) +Tested-by: Lorenzo Stoakes (Oracle) +Cc: Jann Horn +Cc: Jason A. Donenfeld +Cc: Liam Howlett +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Shuah Khan +Cc: Suren Baghdasaryan +Cc: Vlastimil Babka +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/hugetlb_inline.h | 2 +- + mm/internal.h | 10 ++++++++++ + mm/mlock.c | 10 ++++++---- + mm/vma.c | 4 +--- + tools/testing/vma/include/stubs.h | 5 +++++ + 5 files changed, 23 insertions(+), 8 deletions(-) + +--- a/include/linux/hugetlb_inline.h ++++ b/include/linux/hugetlb_inline.h +@@ -30,7 +30,7 @@ static inline bool is_vma_hugetlb_flags( + + #endif + +-static inline bool is_vm_hugetlb_page(struct vm_area_struct *vma) ++static inline bool is_vm_hugetlb_page(const struct vm_area_struct *vma) + { + return is_vm_hugetlb_flags(vma->vm_flags); + } +--- a/mm/internal.h ++++ b/mm/internal.h +@@ -1217,6 +1217,16 @@ static inline struct file *maybe_unlock_ + } + return fpin; + } ++ ++static inline bool vma_supports_mlock(const struct vm_area_struct *vma) ++{ ++ if (vma->vm_flags & (VM_SPECIAL | VM_DROPPABLE)) ++ return false; ++ if (vma_is_dax(vma) || is_vm_hugetlb_page(vma)) ++ return false; ++ return vma != get_gate_vma(current->mm); ++} ++ + #else /* !CONFIG_MMU */ + static inline void unmap_mapping_folio(struct folio *folio) { } + static inline void mlock_new_folio(struct folio *folio) { } +--- a/mm/mlock.c ++++ b/mm/mlock.c +@@ -472,10 +472,12 @@ static int mlock_fixup(struct vma_iterat + int ret = 0; + vm_flags_t oldflags = vma->vm_flags; + +- if (newflags == oldflags || (oldflags & VM_SPECIAL) || +- is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) || +- vma_is_dax(vma) || vma_is_secretmem(vma) || (oldflags & VM_DROPPABLE)) +- /* don't set VM_LOCKED or VM_LOCKONFAULT and don't count */ ++ if (newflags == oldflags || vma_is_secretmem(vma) || ++ !vma_supports_mlock(vma)) ++ /* ++ * Don't set VM_LOCKED or VM_LOCKONFAULT and don't count. ++ * For secretmem, don't allow the memory to be unlocked. ++ */ + goto out; + + vma = vma_modify_flags(vmi, *prev, vma, start, end, &newflags); +--- a/mm/vma.c ++++ b/mm/vma.c +@@ -2589,9 +2589,7 @@ static void __mmap_complete(struct mmap_ + + vm_stat_account(mm, vma->vm_flags, map->pglen); + if (vm_flags & VM_LOCKED) { +- if ((vm_flags & VM_SPECIAL) || vma_is_dax(vma) || +- is_vm_hugetlb_page(vma) || +- vma == get_gate_vma(mm)) ++ if (!vma_supports_mlock(vma)) + vm_flags_clear(vma, VM_LOCKED_MASK); + else + mm->locked_vm += map->pglen; +--- a/tools/testing/vma/include/stubs.h ++++ b/tools/testing/vma/include/stubs.h +@@ -426,3 +426,8 @@ static inline void vma_adjust_trans_huge + } + + static inline void hugetlb_split(struct vm_area_struct *, unsigned long) {} ++ ++static inline bool vma_supports_mlock(const struct vm_area_struct *vma) ++{ ++ return false; ++} diff --git a/queue-7.0/rust-dma-remove-dma_attr_no_kernel_mapping-from-public-attrs.patch b/queue-7.0/rust-dma-remove-dma_attr_no_kernel_mapping-from-public-attrs.patch new file mode 100644 index 0000000000..66697292f7 --- /dev/null +++ b/queue-7.0/rust-dma-remove-dma_attr_no_kernel_mapping-from-public-attrs.patch @@ -0,0 +1,50 @@ +From 18fb5f1f0289b8217c0c43d54d12bccc201dd640 Mon Sep 17 00:00:00 2001 +From: Danilo Krummrich +Date: Sat, 21 Mar 2026 18:27:46 +0100 +Subject: rust: dma: remove DMA_ATTR_NO_KERNEL_MAPPING from public attrs + +From: Danilo Krummrich + +commit 18fb5f1f0289b8217c0c43d54d12bccc201dd640 upstream. + +When DMA_ATTR_NO_KERNEL_MAPPING is passed to dma_alloc_attrs(), the +returned CPU address is not a pointer to the allocated memory but an +opaque handle (e.g. struct page *). + +Coherent (or CoherentAllocation respectively) stores this value as +NonNull and exposes methods that dereference it and even modify its +contents. + +Remove the flag from the public attrs module such that drivers cannot +pass it to Coherent (or CoherentAllocation respectively) in the +first place. + +Instead DMA_ATTR_NO_KERNEL_MAPPING can be supported with an additional +opaque type (e.g. CoherentHandle) which does not provide access to the +allocated memory. + +Cc: stable@vger.kernel.org +Fixes: ad2907b4e308 ("rust: add dma coherent allocator abstraction") +Signed-off-by: Danilo Krummrich +Reviewed-by: Alice Ryhl +Reviewed-by: Alexandre Courbot +Reviewed-by: Gary Guo +Link: https://patch.msgid.link/20260321172749.592387-1-dakr@kernel.org +Signed-off-by: Alexandre Courbot +Signed-off-by: Greg Kroah-Hartman +--- + rust/kernel/dma.rs | 3 --- + 1 file changed, 3 deletions(-) + +--- a/rust/kernel/dma.rs ++++ b/rust/kernel/dma.rs +@@ -250,9 +250,6 @@ pub mod attrs { + /// Specifies that writes to the mapping may be buffered to improve performance. + pub const DMA_ATTR_WRITE_COMBINE: Attrs = Attrs(bindings::DMA_ATTR_WRITE_COMBINE); + +- /// Lets the platform to avoid creating a kernel virtual mapping for the allocated buffer. +- pub const DMA_ATTR_NO_KERNEL_MAPPING: Attrs = Attrs(bindings::DMA_ATTR_NO_KERNEL_MAPPING); +- + /// Allows platform code to skip synchronization of the CPU cache for the given buffer assuming + /// that it has been already transferred to 'device' domain. + pub const DMA_ATTR_SKIP_CPU_SYNC: Attrs = Attrs(bindings::DMA_ATTR_SKIP_CPU_SYNC); diff --git a/queue-7.0/series b/queue-7.0/series index bf25601f84..dcd9c855a1 100644 --- a/queue-7.0/series +++ b/queue-7.0/series @@ -5,3 +5,25 @@ usb-xhci-make-usb_host_endpoint.hcpriv-survive-endpoint_disable.patch usb-chipidea-otg-not-wait-vbus-drop-if-use-role_switch.patch usb-chipidea-core-allow-ci_irq_handler-handle-both-id-and-vbus-change.patch alsa-usb-audio-evaluate-packsize-caps-at-the-right-place.patch +loongarch-add-spectre-boundry-for-syscall-dispatch-table.patch +drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch +leds-qcom-lpg-check-for-array-overflow-when-selecting-the-high-resolution.patch +greybus-gb-beagleplay-bound-bootloader-receive-buffering.patch +greybus-gb-beagleplay-fix-sleep-in-atomic-context-in-hdlc_tx_frames.patch +misc-ibmasm-fix-oob-mmio-read-in-ibmasm_handle_mouse_interrupt.patch +ibmasm-fix-oob-reads-in-command_file_write-due-to-missing-size-checks.patch +ibmasm-fix-heap-over-read-in-ibmasm_send_i2o_message.patch +sysfs-attribute_group-respect-is_visible_const-when-changing-owner.patch +driver-core-don-t-let-a-device-probe-until-it-s-ready.patch +device-property-make-modifications-of-fwnode-flags-thread-safe.patch +drm-nouveau-fix-nvkm_device-leak-on-aperture-removal-failure.patch +rust-dma-remove-dma_attr_no_kernel_mapping-from-public-attrs.patch +kbuild-rust-allow-clippy-uninlined_format_args.patch +fs-afs-revert-mmap_prepare-change.patch +firmware-google-framebuffer-do-not-mark-framebuffer-as-busy.patch +lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch +arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch +arm64-mm-fix-rodata-full-block-mapping-support-for-realm-guests.patch +mm-migrate-requeue-destination-folio-on-deferred-split-queue.patch +mm-prevent-droppable-mappings-from-being-locked.patch +mm-fix-deferred-split-queue-races-during-migration.patch diff --git a/queue-7.0/sysfs-attribute_group-respect-is_visible_const-when-changing-owner.patch b/queue-7.0/sysfs-attribute_group-respect-is_visible_const-when-changing-owner.patch new file mode 100644 index 0000000000..3c28e93c44 --- /dev/null +++ b/queue-7.0/sysfs-attribute_group-respect-is_visible_const-when-changing-owner.patch @@ -0,0 +1,47 @@ +From 9ce4a8c07b28cdd70f6ca38b60bf688c27dbbfb9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= +Date: Fri, 3 Apr 2026 18:31:02 +0200 +Subject: sysfs: attribute_group: Respect is_visible_const() when changing owner +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Thomas Weißschuh + +commit 9ce4a8c07b28cdd70f6ca38b60bf688c27dbbfb9 upstream. + +The call to grp->is_visible in sysfs_group_attrs_change_owner() was +missed when support for is_visible_const() was added. + +Check for both is_visible variants there too. + +Fixes: 7dd9fdb4939b ("sysfs: attribute_group: enable const variants of is_visible()") +Cc: stable@vger.kernel.org +Reported-by: Michael Kelley +Closes: https://lore.kernel.org/lkml/SN6PR02MB4157D5F04608E4E3C21AB56ED45EA@SN6PR02MB4157.namprd02.prod.outlook.com/ +Link: https://sashiko.dev/#/patchset/20260403-sysfs-const-hv-v2-0-8932ab8d41db%40weissschuh.net +Signed-off-by: Thomas Weißschuh +Reviewed-by: Michael Kelley +Link: https://patch.msgid.link/20260403-sysfs-is_visible_const-fix-v1-1-f87f26071d2c@weissschuh.net +Signed-off-by: Danilo Krummrich +Signed-off-by: Greg Kroah-Hartman +--- + fs/sysfs/group.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/fs/sysfs/group.c ++++ b/fs/sysfs/group.c +@@ -517,8 +517,11 @@ static int sysfs_group_attrs_change_owne + struct attribute *const *attr; + + for (i = 0, attr = grp->attrs; *attr; i++, attr++) { +- if (grp->is_visible) { +- mode = grp->is_visible(kobj, *attr, i); ++ if (grp->is_visible || grp->is_visible_const) { ++ if (grp->is_visible) ++ mode = grp->is_visible(kobj, *attr, i); ++ else ++ mode = grp->is_visible_const(kobj, *attr, i); + if (mode & SYSFS_GROUP_INVISIBLE) + break; + if (!mode)