]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
7.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 20 Jul 2026 14:35:30 +0000 (16:35 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 20 Jul 2026 14:35:30 +0000 (16:35 +0200)
added patches:
9p-skip-nlink-update-in-cacheless-mode-to-fix-warn_on.patch
fs-ntfs3-add-depth-limit-to-indx_find_buffer-to-prevent-stack-overflow.patch
fs-ntfs3-bound-attr_off-in-updateresidentvalue-against-data_off.patch
fs-ntfs3-bound-copy_lcns-dp-page_lcns-index-in-analysis-pass.patch
fs-ntfs3-bound-deleteindexentryallocation-memmove-length.patch
fs-ntfs3-bound-ntfs_de-view.data_off-in-updaterecorddata-root-allocation.patch
fs-ntfs3-fix-syncing-wrong-inode-on-dirsync-cross-directory-rename.patch
fs-ntfs3-validate-lcns_follow-in-log_replay-conversion.patch
fs-proc-fix-kpf_ksm-reported-for-all-anonymous-pages.patch
fs-proc-task_mmu-do-not-warn-on-seeing-non-migration-pmd-entry.patch
fs-proc-task_mmu-fix-hugetlb-self-deadlock-in-pagemap_scan_pte_hole.patch
fs-proc-task_mmu-fix-make_uffd_wp_huge_pte-prot-update-race.patch
fs-proc-task_mmu-use-huge_page_size-in-pagemap_scan_hugetlb_entry.patch
kcov-use-write_once-for-selftest-mode-stores.patch
kho-make-sure-scratch-size-is-always-aligned-by-cma_min_alignment_bytes.patch
landlock-fix-landlock_scope_signal-bypass-on-the-sigio-path.patch
mips-dec-ensure-32-bit-stack-location-for-o32-prom_printf.patch
mips-ip22-gio-fix-device-reference-leak-in-probe.patch
mips-ip22-gio-fix-gio-device-memory-leak.patch
mips-ip22-gio-fix-kfree-of-static-object.patch
mips-sched-fix-cpumask_offstack-memory-corruption.patch
mm-damon-core-always-put-unsuccessfully-committed-target-pids.patch
mm-damon-core-make-charge_addr_from-aware-of-end-address-exclusivity.patch
mm-damon-sysfs-schemes-fix-dir-put-orders-in-access_pattern_add_dirs.patch
mm-damon-sysfs-schemes-put-stats-for-scheme_add_dirs-internal-error.patch
mm-huge_memory-preserve-pmd_swp_uffd_wp-on-device-private-pmd-downgrade.patch
mm-hugetlb-fix-hugetlb-cgroup-rsvd-charge-uncharge-mismatch.patch
mm-memory_hotplug-fix-incorrect-altmap-passing-in-error-path.patch
mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch
mm-mm_init-fix-uninitialized-struct-pages-for-zone_device.patch
mm-sparse-vmemmap-fix-vmemmap-accounting-underflow.patch
mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch
mtd-rawnand-fix-condition-in-nand_select_target.patch
mtd-rawnand-pl353-fix-probe-resource-allocation.patch
mtd-slram-remove-failed-entries-from-the-device-list.patch
net-9p-fix-infinite-loop-in-p9_client_rpc-on-fatal-signal.patch
ntfs-add-bounds-check-before-accessing-ea-entries.patch
ntfs-add-wq_percpu-to-alloc_workqueue-users.patch
ntfs-avoid-heap-allocation-for-free-cluster-readahead-state.patch
ntfs-avoid-self-deadlock-during-inode-eviction.patch
ntfs-centalize-index_root-header-validation.patch
ntfs-detect-mapping-pairs-lcn-accumulator-overflow.patch
ntfs-do-not-replace-volume-name-after-lookup-errors.patch
ntfs-fail-attrlist-updates-when-the-superblock-is-inactive.patch
ntfs-fix-hole-runlist-memory-leak-in-insert-range-error-path.patch
ntfs-fix-incorrect-size-of-symbolic-link.patch
ntfs-fix-mrec_lock-abba-deadlock-in-rename.patch
ntfs-fix-off-by-one-in-mapping-pairs-decoding-bounds-checks.patch
ntfs-fix-warn_on-for-resident-attribute-in-ntfs_map_runlist_nolock.patch
ntfs-free-volume-wide-resources-on-fill_super-failure.patch
ntfs-grow-index-root-value-before-reparent-header-update.patch
ntfs-make-system-files-immutable-to-prevent-corruption.patch
ntfs-not-change-0-byte-data-attribute-to-non-resident.patch
ntfs-only-alias-volume-upcase-to-default-on-exact-match.patch
ntfs-reinit-search-context-before-volume-information-lookup.patch
ntfs-reject-non-resident-records-for-resident-only-attributes.patch
ntfs-sanitize-mft-references-returned-from-ntfs_lookup_inode_by_name.patch
ntfs-skip-extent-mft-records-in-writeback-to-prevent-deadlock.patch
ntfs-update-index-root-allocated-size-before-shrink.patch
ntfs-validate-attribute-values-on-lookup.patch
ntfs-validate-index-block-header-more-strictly.patch
ntfs-validate-index-entries-on-reading.patch
ntfs-validate-resident-index-root-values-on-lookup.patch
ntfs-validate-resident-volume-name-values-on-lookup.patch
ntfs3-bound-to_move-in-indx_insert_into_root-before-hdr_insert_head.patch
ntfs3-cap-restart_table-free-chain-walker-at-rt-used.patch
ntfs3-fix-out-of-bounds-read-in-decompress_lznt.patch
ntfs3-validate-split-point-offset-in-indx_insert_into_buffer.patch
ocfs2-add-journal-null-check-in-ocfs2_checkpoint_inode.patch
ocfs2-avoid-moving-extents-to-occupied-clusters.patch
ocfs2-fix-null-h_transaction-deref-in-ocfs2_assure_trans_credits.patch
ocfs2-fix-ubsan-array-index-out-of-bounds-in-ocfs2_sum_rightmost_rec.patch
ocfs2-reject-dinodes-whose-i_rdev-disagrees-with-the-file-type.patch
ocfs2-reject-dinodes-with-non-canonical-i_mode-type.patch
ocfs2-reject-non-inline-dinodes-with-i_size-and-zero-i_clusters.patch
ocfs2-use-kzalloc-for-quota-recovery-bitmap-allocation.patch
openrisc-add-full-instruction-cache-invalidate-functions.patch
power-supply-bq257xx-fix-vsysmin-clamping-logic.patch
power-supply-charger-manager-fix-refcount-leak-in-is_full_charged.patch
power-supply-cpcap-battery-fix-missing-nvmem_device_put-causing-reference-leak.patch
power-supply-max17042-fix-of-node-reference-imbalance.patch
powerpc-dt_cpu_ftrs-set-cpu_ftr_p11_pvr-for-power11-and-later-processors.patch
proc-only-bump-parent-nlink-when-registering-directories.patch
remoteproc-qcom-fix-leak-when-custom-dump_segments-addition-fails.patch
remoteproc-xlnx-check-remote-core-state.patch
riscv-cacheinfo-fix-node-reference-leak-in-populate_cache_leaves.patch
scsi-sas-skip-opt_sectors-when-dma-reports-no-real-optimization-hint.patch
scsi-smartpqi-use-shost_to_hba-in-pqi_scan_finished.patch
selftests-landlock-test-scope_signal-on-the-sigio-fowner-pgid-path.patch

90 files changed:
queue-7.1/9p-skip-nlink-update-in-cacheless-mode-to-fix-warn_on.patch [new file with mode: 0644]
queue-7.1/fs-ntfs3-add-depth-limit-to-indx_find_buffer-to-prevent-stack-overflow.patch [new file with mode: 0644]
queue-7.1/fs-ntfs3-bound-attr_off-in-updateresidentvalue-against-data_off.patch [new file with mode: 0644]
queue-7.1/fs-ntfs3-bound-copy_lcns-dp-page_lcns-index-in-analysis-pass.patch [new file with mode: 0644]
queue-7.1/fs-ntfs3-bound-deleteindexentryallocation-memmove-length.patch [new file with mode: 0644]
queue-7.1/fs-ntfs3-bound-ntfs_de-view.data_off-in-updaterecorddata-root-allocation.patch [new file with mode: 0644]
queue-7.1/fs-ntfs3-fix-syncing-wrong-inode-on-dirsync-cross-directory-rename.patch [new file with mode: 0644]
queue-7.1/fs-ntfs3-validate-lcns_follow-in-log_replay-conversion.patch [new file with mode: 0644]
queue-7.1/fs-proc-fix-kpf_ksm-reported-for-all-anonymous-pages.patch [new file with mode: 0644]
queue-7.1/fs-proc-task_mmu-do-not-warn-on-seeing-non-migration-pmd-entry.patch [new file with mode: 0644]
queue-7.1/fs-proc-task_mmu-fix-hugetlb-self-deadlock-in-pagemap_scan_pte_hole.patch [new file with mode: 0644]
queue-7.1/fs-proc-task_mmu-fix-make_uffd_wp_huge_pte-prot-update-race.patch [new file with mode: 0644]
queue-7.1/fs-proc-task_mmu-use-huge_page_size-in-pagemap_scan_hugetlb_entry.patch [new file with mode: 0644]
queue-7.1/kcov-use-write_once-for-selftest-mode-stores.patch [new file with mode: 0644]
queue-7.1/kho-make-sure-scratch-size-is-always-aligned-by-cma_min_alignment_bytes.patch [new file with mode: 0644]
queue-7.1/landlock-fix-landlock_scope_signal-bypass-on-the-sigio-path.patch [new file with mode: 0644]
queue-7.1/mips-dec-ensure-32-bit-stack-location-for-o32-prom_printf.patch [new file with mode: 0644]
queue-7.1/mips-ip22-gio-fix-device-reference-leak-in-probe.patch [new file with mode: 0644]
queue-7.1/mips-ip22-gio-fix-gio-device-memory-leak.patch [new file with mode: 0644]
queue-7.1/mips-ip22-gio-fix-kfree-of-static-object.patch [new file with mode: 0644]
queue-7.1/mips-sched-fix-cpumask_offstack-memory-corruption.patch [new file with mode: 0644]
queue-7.1/mm-damon-core-always-put-unsuccessfully-committed-target-pids.patch [new file with mode: 0644]
queue-7.1/mm-damon-core-make-charge_addr_from-aware-of-end-address-exclusivity.patch [new file with mode: 0644]
queue-7.1/mm-damon-sysfs-schemes-fix-dir-put-orders-in-access_pattern_add_dirs.patch [new file with mode: 0644]
queue-7.1/mm-damon-sysfs-schemes-put-stats-for-scheme_add_dirs-internal-error.patch [new file with mode: 0644]
queue-7.1/mm-huge_memory-preserve-pmd_swp_uffd_wp-on-device-private-pmd-downgrade.patch [new file with mode: 0644]
queue-7.1/mm-hugetlb-fix-hugetlb-cgroup-rsvd-charge-uncharge-mismatch.patch [new file with mode: 0644]
queue-7.1/mm-memory_hotplug-fix-incorrect-altmap-passing-in-error-path.patch [new file with mode: 0644]
queue-7.1/mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch [new file with mode: 0644]
queue-7.1/mm-mm_init-fix-uninitialized-struct-pages-for-zone_device.patch [new file with mode: 0644]
queue-7.1/mm-sparse-vmemmap-fix-vmemmap-accounting-underflow.patch [new file with mode: 0644]
queue-7.1/mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch [new file with mode: 0644]
queue-7.1/mtd-rawnand-fix-condition-in-nand_select_target.patch [new file with mode: 0644]
queue-7.1/mtd-rawnand-pl353-fix-probe-resource-allocation.patch [new file with mode: 0644]
queue-7.1/mtd-slram-remove-failed-entries-from-the-device-list.patch [new file with mode: 0644]
queue-7.1/net-9p-fix-infinite-loop-in-p9_client_rpc-on-fatal-signal.patch [new file with mode: 0644]
queue-7.1/ntfs-add-bounds-check-before-accessing-ea-entries.patch [new file with mode: 0644]
queue-7.1/ntfs-add-wq_percpu-to-alloc_workqueue-users.patch [new file with mode: 0644]
queue-7.1/ntfs-avoid-heap-allocation-for-free-cluster-readahead-state.patch [new file with mode: 0644]
queue-7.1/ntfs-avoid-self-deadlock-during-inode-eviction.patch [new file with mode: 0644]
queue-7.1/ntfs-centalize-index_root-header-validation.patch [new file with mode: 0644]
queue-7.1/ntfs-detect-mapping-pairs-lcn-accumulator-overflow.patch [new file with mode: 0644]
queue-7.1/ntfs-do-not-replace-volume-name-after-lookup-errors.patch [new file with mode: 0644]
queue-7.1/ntfs-fail-attrlist-updates-when-the-superblock-is-inactive.patch [new file with mode: 0644]
queue-7.1/ntfs-fix-hole-runlist-memory-leak-in-insert-range-error-path.patch [new file with mode: 0644]
queue-7.1/ntfs-fix-incorrect-size-of-symbolic-link.patch [new file with mode: 0644]
queue-7.1/ntfs-fix-mrec_lock-abba-deadlock-in-rename.patch [new file with mode: 0644]
queue-7.1/ntfs-fix-off-by-one-in-mapping-pairs-decoding-bounds-checks.patch [new file with mode: 0644]
queue-7.1/ntfs-fix-warn_on-for-resident-attribute-in-ntfs_map_runlist_nolock.patch [new file with mode: 0644]
queue-7.1/ntfs-free-volume-wide-resources-on-fill_super-failure.patch [new file with mode: 0644]
queue-7.1/ntfs-grow-index-root-value-before-reparent-header-update.patch [new file with mode: 0644]
queue-7.1/ntfs-make-system-files-immutable-to-prevent-corruption.patch [new file with mode: 0644]
queue-7.1/ntfs-not-change-0-byte-data-attribute-to-non-resident.patch [new file with mode: 0644]
queue-7.1/ntfs-only-alias-volume-upcase-to-default-on-exact-match.patch [new file with mode: 0644]
queue-7.1/ntfs-reinit-search-context-before-volume-information-lookup.patch [new file with mode: 0644]
queue-7.1/ntfs-reject-non-resident-records-for-resident-only-attributes.patch [new file with mode: 0644]
queue-7.1/ntfs-sanitize-mft-references-returned-from-ntfs_lookup_inode_by_name.patch [new file with mode: 0644]
queue-7.1/ntfs-skip-extent-mft-records-in-writeback-to-prevent-deadlock.patch [new file with mode: 0644]
queue-7.1/ntfs-update-index-root-allocated-size-before-shrink.patch [new file with mode: 0644]
queue-7.1/ntfs-validate-attribute-values-on-lookup.patch [new file with mode: 0644]
queue-7.1/ntfs-validate-index-block-header-more-strictly.patch [new file with mode: 0644]
queue-7.1/ntfs-validate-index-entries-on-reading.patch [new file with mode: 0644]
queue-7.1/ntfs-validate-resident-index-root-values-on-lookup.patch [new file with mode: 0644]
queue-7.1/ntfs-validate-resident-volume-name-values-on-lookup.patch [new file with mode: 0644]
queue-7.1/ntfs3-bound-to_move-in-indx_insert_into_root-before-hdr_insert_head.patch [new file with mode: 0644]
queue-7.1/ntfs3-cap-restart_table-free-chain-walker-at-rt-used.patch [new file with mode: 0644]
queue-7.1/ntfs3-fix-out-of-bounds-read-in-decompress_lznt.patch [new file with mode: 0644]
queue-7.1/ntfs3-validate-split-point-offset-in-indx_insert_into_buffer.patch [new file with mode: 0644]
queue-7.1/ocfs2-add-journal-null-check-in-ocfs2_checkpoint_inode.patch [new file with mode: 0644]
queue-7.1/ocfs2-avoid-moving-extents-to-occupied-clusters.patch [new file with mode: 0644]
queue-7.1/ocfs2-fix-null-h_transaction-deref-in-ocfs2_assure_trans_credits.patch [new file with mode: 0644]
queue-7.1/ocfs2-fix-ubsan-array-index-out-of-bounds-in-ocfs2_sum_rightmost_rec.patch [new file with mode: 0644]
queue-7.1/ocfs2-reject-dinodes-whose-i_rdev-disagrees-with-the-file-type.patch [new file with mode: 0644]
queue-7.1/ocfs2-reject-dinodes-with-non-canonical-i_mode-type.patch [new file with mode: 0644]
queue-7.1/ocfs2-reject-non-inline-dinodes-with-i_size-and-zero-i_clusters.patch [new file with mode: 0644]
queue-7.1/ocfs2-use-kzalloc-for-quota-recovery-bitmap-allocation.patch [new file with mode: 0644]
queue-7.1/openrisc-add-full-instruction-cache-invalidate-functions.patch [new file with mode: 0644]
queue-7.1/power-supply-bq257xx-fix-vsysmin-clamping-logic.patch [new file with mode: 0644]
queue-7.1/power-supply-charger-manager-fix-refcount-leak-in-is_full_charged.patch [new file with mode: 0644]
queue-7.1/power-supply-cpcap-battery-fix-missing-nvmem_device_put-causing-reference-leak.patch [new file with mode: 0644]
queue-7.1/power-supply-max17042-fix-of-node-reference-imbalance.patch [new file with mode: 0644]
queue-7.1/powerpc-dt_cpu_ftrs-set-cpu_ftr_p11_pvr-for-power11-and-later-processors.patch [new file with mode: 0644]
queue-7.1/proc-only-bump-parent-nlink-when-registering-directories.patch [new file with mode: 0644]
queue-7.1/remoteproc-qcom-fix-leak-when-custom-dump_segments-addition-fails.patch [new file with mode: 0644]
queue-7.1/remoteproc-xlnx-check-remote-core-state.patch [new file with mode: 0644]
queue-7.1/riscv-cacheinfo-fix-node-reference-leak-in-populate_cache_leaves.patch [new file with mode: 0644]
queue-7.1/scsi-sas-skip-opt_sectors-when-dma-reports-no-real-optimization-hint.patch [new file with mode: 0644]
queue-7.1/scsi-smartpqi-use-shost_to_hba-in-pqi_scan_finished.patch [new file with mode: 0644]
queue-7.1/selftests-landlock-test-scope_signal-on-the-sigio-fowner-pgid-path.patch [new file with mode: 0644]
queue-7.1/series

diff --git a/queue-7.1/9p-skip-nlink-update-in-cacheless-mode-to-fix-warn_on.patch b/queue-7.1/9p-skip-nlink-update-in-cacheless-mode-to-fix-warn_on.patch
new file mode 100644 (file)
index 0000000..eefeca7
--- /dev/null
@@ -0,0 +1,71 @@
+From 574aa0b4799470ac814479f1138d19efe6262255 Mon Sep 17 00:00:00 2001
+From: Breno Leitao <leitao@debian.org>
+Date: Tue, 21 Apr 2026 02:41:09 -0700
+Subject: 9p: skip nlink update in cacheless mode to fix WARN_ON
+
+From: Breno Leitao <leitao@debian.org>
+
+commit 574aa0b4799470ac814479f1138d19efe6262255 upstream.
+
+v9fs_dec_count() unconditionally calls drop_nlink() on regular files,
+even when the inode's nlink is already zero. In cacheless mode the
+client refetches inode metadata from the server (the source of truth)
+on every operation, so by the time v9fs_remove() returns, the locally
+cached nlink may already reflect the post-unlink value:
+
+  1. Client initiates unlink, server processes it and sets nlink to 0
+  2. Client refetches inode metadata (nlink=0) before unlink returns
+  3. Client's v9fs_remove() completes successfully
+  4. Client calls v9fs_dec_count() which calls drop_nlink() on nlink=0
+
+This race is easily triggered under heavy unlink workloads, such as
+stress-ng's unlink stressor, producing the following warning:
+
+  WARNING: fs/inode.c:417 at drop_nlink+0x4c/0xc8
+  Call trace:
+   drop_nlink+0x4c/0xc8
+   v9fs_remove+0x1e0/0x250 [9p]
+   v9fs_vfs_unlink+0x20/0x38 [9p]
+   vfs_unlink+0x13c/0x258
+   ...
+
+In cacheless mode the server is authoritative and the inode is on its
+way out, so locally adjusting nlink buys nothing. Skip v9fs_dec_count()
+entirely when neither CACHE_META nor CACHE_LOOSE is set, which both
+avoids the warning and removes a class of nlink races (two concurrent
+unlinkers observing nlink > 0 and both calling drop_nlink()) that an
+nlink == 0 guard alone would only narrow rather than close.
+
+Fixes: ac89b2ef9b55 ("9p: don't maintain dir i_nlink if the exported fs doesn't either")
+Cc: stable@vger.kernel.org
+Suggested-by: Dominique Martinet <asmadeus@codewreck.org>
+Signed-off-by: Breno Leitao <leitao@debian.org>
+Message-ID: <20260421-9p-v2-1-48762d294fad@debian.org>
+Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/9p/vfs_inode.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/fs/9p/vfs_inode.c
++++ b/fs/9p/vfs_inode.c
+@@ -488,10 +488,19 @@ static int v9fs_at_to_dotl_flags(int fla
+  * - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more
+  *   than EXT4_LINK_MAX (65000) links.
+  *
++ * In cacheless mode the server is the source of truth for nlink and the
++ * inode is going away immediately, so locally adjusting i_nlink buys
++ * nothing and races with concurrent metadata fetches that may already
++ * have observed the post-unlink value (nlink == 0).
++ *
+  * @inode: inode whose nlink is being dropped
+  */
+ static void v9fs_dec_count(struct inode *inode)
+ {
++      struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
++
++      if (!(v9ses->cache & (CACHE_META | CACHE_LOOSE)))
++              return;
+       if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2)
+               drop_nlink(inode);
+ }
diff --git a/queue-7.1/fs-ntfs3-add-depth-limit-to-indx_find_buffer-to-prevent-stack-overflow.patch b/queue-7.1/fs-ntfs3-add-depth-limit-to-indx_find_buffer-to-prevent-stack-overflow.patch
new file mode 100644 (file)
index 0000000..08244a8
--- /dev/null
@@ -0,0 +1,81 @@
+From 1ebd684b8f627f75bc3e03f8b2ad8400fd1f02cd Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Mon, 13 Apr 2026 09:31:17 -0400
+Subject: fs/ntfs3: add depth limit to indx_find_buffer to prevent stack overflow
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 1ebd684b8f627f75bc3e03f8b2ad8400fd1f02cd upstream.
+
+indx_find_buffer() recursively descends the B+ tree index with no depth
+limit.  A crafted NTFS image with circular index node references causes
+unbounded recursion, overflowing the kernel stack and panicking the
+system.
+
+This is reachable by mounting a malicious NTFS filesystem (e.g. from a
+USB drive via desktop automount) and deleting a file whose index entry
+triggers the rebalancing fallback path in indx_delete_entry().
+
+Add a depth parameter and bail out with -EINVAL when it reaches the
+fnd->nodes array bound, matching the constraint already enforced by
+fnd_push() in indx_find().
+
+The related function indx_find() was previously patched for a similar
+infinite-loop issue (commit 1732053c8a6b), but indx_find_buffer() was
+missed.
+
+Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-6
+Assisted-by: Codex:gpt-5-4
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/index.c |   15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+--- a/fs/ntfs3/index.c
++++ b/fs/ntfs3/index.c
+@@ -2022,13 +2022,21 @@ out1:
+ static struct indx_node *indx_find_buffer(struct ntfs_index *indx,
+                                         struct ntfs_inode *ni,
+                                         const struct INDEX_ROOT *root,
+-                                        __le64 vbn, struct indx_node *n)
++                                        __le64 vbn, struct indx_node *n,
++                                        int depth)
+ {
+       int err;
+       const struct NTFS_DE *e;
+       struct indx_node *r;
+       const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr;
++      /*
++       * Limit recursion depth to prevent stack overflow from crafted
++       * images.  Use the same bound as the fnd->nodes array (20).
++       */
++      if (depth > ARRAY_SIZE(((struct ntfs_fnd *)NULL)->nodes))
++              return ERR_PTR(-EINVAL);
++
+       /* Step 1: Scan one level. */
+       for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) {
+               if (!e)
+@@ -2049,7 +2057,8 @@ static struct indx_node *indx_find_buffe
+                       if (err)
+                               return ERR_PTR(err);
+-                      r = indx_find_buffer(indx, ni, root, vbn, n);
++                      r = indx_find_buffer(indx, ni, root, vbn, n,
++                                           depth + 1);
+                       if (r)
+                               return r;
+               }
+@@ -2462,7 +2471,7 @@ int indx_delete_entry(struct ntfs_index
+               fnd_clear(fnd);
+-              in = indx_find_buffer(indx, ni, root, sub_vbn, NULL);
++              in = indx_find_buffer(indx, ni, root, sub_vbn, NULL, 0);
+               if (IS_ERR(in)) {
+                       err = PTR_ERR(in);
+                       goto out;
diff --git a/queue-7.1/fs-ntfs3-bound-attr_off-in-updateresidentvalue-against-data_off.patch b/queue-7.1/fs-ntfs3-bound-attr_off-in-updateresidentvalue-against-data_off.patch
new file mode 100644 (file)
index 0000000..32ca99b
--- /dev/null
@@ -0,0 +1,64 @@
+From d1570c48f49a693974d000251030370ee2e83539 Mon Sep 17 00:00:00 2001
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Date: Tue, 2 Jun 2026 15:15:47 +0200
+Subject: fs/ntfs3: bound attr_off in UpdateResidentValue against data_off
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+commit d1570c48f49a693974d000251030370ee2e83539 upstream.
+
+In do_action()'s UpdateResidentValue case (fslog.c:3307),
+lrh->attr_off and lrh->redo_len come from the on-disk LRH.
+When they satisfy aoff + dlen < attr->res.data_off, the
+assignment
+
+       attr->res.data_size = cpu_to_le32(aoff + dlen - data_off);
+
+underflows to ~4 GiB (e.g. 0xFFFFFFF9 when aoff=0x10, dlen=1,
+data_off=0x18).  Subsequent code that reads attr->res.data_size
+to walk the resident attribute payload would then read up to
+4 GiB past the 1024-byte MFT record allocation.
+
+The existing mi_enum_attr() defense in fs/ntfs3/record.c:287
+catches the corrupted data_size on the next attribute walk
+and fails the mount, but only on the path that walks all
+attributes.  A read site that picks an attribute by name and
+reads its data_size without re-validating is not covered.
+Validate aoff against data_off and asize at the source.
+
+Reproduced under UML+KASAN on mainline 8d90b09e6741 via
+pr_warn-only probe: with aoff=0x10 and data_off=0x18, the
+post-assignment data_size is 0xfffffff9 (mount then fails
+at -22 from mi_enum_attr).
+
+Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+[almaz.alexandrovich@paragon-software.com: clang-formatted the changes]
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/fslog.c |   11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/fs/ntfs3/fslog.c
++++ b/fs/ntfs3/fslog.c
+@@ -3325,6 +3325,17 @@ skip_load_parent:
+               nsize = ALIGN(nsize, 8);
+               data_off = le16_to_cpu(attr->res.data_off);
++              /*
++               * aoff comes from the on-disk lrh->attr_off.  Forbid
++               * writes that begin below the resident attribute's
++               * data_off (which would overwrite the resident header),
++               * and forbid aoff + dlen < data_off, which would make
++               * the data_size assignment below underflow to ~4 GiB.
++               */
++              if (aoff < data_off || aoff + dlen < data_off ||
++                  aoff + dlen > asize)
++                      goto dirty_vol;
++
+               if (nsize < asize) {
+                       memmove(Add2Ptr(attr, aoff), data, dlen);
+                       data = NULL; // To skip below memmove().
diff --git a/queue-7.1/fs-ntfs3-bound-copy_lcns-dp-page_lcns-index-in-analysis-pass.patch b/queue-7.1/fs-ntfs3-bound-copy_lcns-dp-page_lcns-index-in-analysis-pass.patch
new file mode 100644 (file)
index 0000000..25614b7
--- /dev/null
@@ -0,0 +1,116 @@
+From 5e7b598660cfa8e5af172cf4c65cffc126333307 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Fri, 15 May 2026 12:34:05 -0400
+Subject: fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 5e7b598660cfa8e5af172cf4c65cffc126333307 upstream.
+
+In log_replay()'s analysis pass, after find_dp() returns a
+valid DIR_PAGE_ENTRY for the (target_attr, target_vcn) tuple,
+the copy_lcns block walks lrh->lcns_follow further entries:
+
+       t16 = le16_to_cpu(lrh->lcns_follow);
+       for (i = 0; i < t16; i++) {
+           size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
+                               le64_to_cpu(dp->vcn));
+           dp->page_lcns[j + i] = lrh->page_lcns[i];
+       }
+
+find_dp() only validates that target_vcn falls within
+[dp->vcn, dp->vcn + dp->lcns_follow), i.e., that the FIRST
+cluster is covered.  The walk through the further entries is
+not bounded against dp->lcns_follow.  For a malformed LRH
+where target_vcn = dp->vcn + dp->lcns_follow - 1 and
+lrh->lcns_follow > 1, the i > 0 writes overflow the dp's
+allocated page_lcns[] array.
+
+Add the missing j + lrh->lcns_follow <= dp->lcns_follow guard.
+
+Reproduced under UML+KASAN on mainline 8d90b09e6741 as a
+slab-out-of-bounds write of size 8 from log_replay+0x68d4 on
+the mount path.
+
+This is distinct from Pavitra Jha's 2026-05-02 patch
+("fs/ntfs3: validate lcns_follow in log_replay conversion",
+<20260502154252.164586-1-jhapavitra98@gmail.com>) which
+addresses the separate version-0 dirty-page-table conversion
+path's memmove(&dp->vcn, ...) call.  The two fixes are
+complementary; both should land.
+
+Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+[almaz.alexandrovich@paragon-software.com: clang-formatted the changes,
+fixed conflicts]
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/fslog.c |   46 +++++++++++++++++++++++++++++-----------------
+ 1 file changed, 29 insertions(+), 17 deletions(-)
+
+--- a/fs/ntfs3/fslog.c
++++ b/fs/ntfs3/fslog.c
+@@ -3369,8 +3369,8 @@ move_data:
+               if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn),
+                                       attr_run(attr),
+-                                      le32_to_cpu(attr->size) - 
+-                                              le16_to_cpu(attr->nres.run_off),        
++                                      le32_to_cpu(attr->size) -
++                                              le16_to_cpu(attr->nres.run_off),
+                                       &t64)) {
+                       goto dirty_vol;
+               }
+@@ -4579,22 +4579,34 @@ copy_lcns:
+                * whole routine a loop, case Lcns do not fit below.
+                */
+               t16 = le16_to_cpu(lrh->lcns_follow);
+-                t32 = le32_to_cpu(dp->lcns_follow);
+-                if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) {
+-                        err = -EINVAL;
+-                        goto out;
+-                }
+-
+-                for (i = 0; i < t16; i++) {
+-                        size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
+-                                            le64_to_cpu(dp->vcn));
+-                        if (j >= t32 || i >= t32 - j) {
+-                                err = -EINVAL;
+-                                goto out;
+-                        }
+-                        dp->page_lcns[j + i] = lrh->page_lcns[i];
+-                }
++              t32 = le32_to_cpu(dp->lcns_follow);
++              if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) {
++                      err = -EINVAL;
++                      goto out;
++              }
++
++              /*
++         * find_dp() only validates that target_vcn is the first
++         * cluster covered by dp.  The walk through lrh->lcns_follow
++         * further entries must stay within the allocated
++         * dp->page_lcns[] array, which is sized by dp->lcns_follow.
++         */
++              if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 >
++                  le32_to_cpu(dp->lcns_follow)) {
++                      err = -EINVAL;
++                      log->set_dirty = true;
++                      goto out;
++              }
++              for (i = 0; i < t16; i++) {
++                      size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
++                                          le64_to_cpu(dp->vcn));
++                      if (j >= t32 || i >= t32 - j) {
++                              err = -EINVAL;
++                              goto out;
++                      }
++                      dp->page_lcns[j + i] = lrh->page_lcns[i];
++              }
+               goto next_log_record_analyze;
+       case DeleteDirtyClusters: {
diff --git a/queue-7.1/fs-ntfs3-bound-deleteindexentryallocation-memmove-length.patch b/queue-7.1/fs-ntfs3-bound-deleteindexentryallocation-memmove-length.patch
new file mode 100644 (file)
index 0000000..29c5f03
--- /dev/null
@@ -0,0 +1,74 @@
+From fc4626bb3656362de8b0ecd56605d47a19ec3518 Mon Sep 17 00:00:00 2001
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Date: Tue, 2 Jun 2026 15:21:03 +0200
+Subject: fs/ntfs3: bound DeleteIndexEntryAllocation memmove length
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+commit fc4626bb3656362de8b0ecd56605d47a19ec3518 upstream.
+
+In do_action()'s DeleteIndexEntryAllocation case, e->size comes
+from an on-disk INDEX_BUFFER entry.  When e->size makes
+e + e->size point past hdr + hdr->used,
+PtrOffset(e1, Add2Ptr(hdr, used)) returns a negative ptrdiff_t
+that is silently cast to a quasi-infinite size_t when passed
+to memmove().  The memmove then walks past the destination
+buffer.
+
+The sibling DeleteIndexEntryRoot case at fslog.c:3540-3543
+already carries the corresponding guard:
+
+       if (PtrOffset(e1, Add2Ptr(hdr, used)) < esize ||
+           Add2Ptr(e, esize) > Add2Ptr(lrh, rec_len) ||
+           used + esize > le32_to_cpu(hdr->total)) {
+               goto dirty_vol;
+       }
+
+Apply the same shape to the allocation-path case.  Also reject
+esize == 0: memmove(e, e, ...) is a no-op and leaves
+hdr->used unchanged, hiding a malformed entry from the
+existing check_index_header() walk.
+
+Reproduced under UML+KASAN on mainline 8d90b09e6741 by
+mounting a crafted NTFS image: the unguarded memmove takes a
+length of 0xffffffffffffff00 and the kernel oopses in
+memmove+0x81/0x1a0 on the do_action+0x36a2 frame.
+
+Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+[almaz.alexandrovich@paragon-software.com: clang-formatted the changes]
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/fslog.c |   16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+--- a/fs/ntfs3/fslog.c
++++ b/fs/ntfs3/fslog.c
+@@ -3573,9 +3573,23 @@ move_data:
+               }
+               e1 = Add2Ptr(e, esize);
+-              nsize = esize;
+               used = le32_to_cpu(hdr->used);
++              /*
++               * Reject crafted entries whose e->size makes e + esize
++               * point past the INDEX_HDR's used boundary.  Without this,
++               * PtrOffset(e1, hdr + used) underflows to a quasi-infinite
++               * size_t when fed to the memmove() below.
++               *
++               * Also reject esize == 0: memmove(e, e, ...) is a no-op and
++               * leaves hdr->used unchanged, masking the crafted entry.
++               */
++              if (!esize || Add2Ptr(e, esize) > Add2Ptr(hdr, used) ||
++                  PtrOffset(e1, Add2Ptr(hdr, used)) < esize)
++                      goto dirty_vol;
++
++              nsize = esize;
++
+               memmove(e, e1, PtrOffset(e1, Add2Ptr(hdr, used)));
+               hdr->used = cpu_to_le32(used - nsize);
diff --git a/queue-7.1/fs-ntfs3-bound-ntfs_de-view.data_off-in-updaterecorddata-root-allocation.patch b/queue-7.1/fs-ntfs3-bound-ntfs_de-view.data_off-in-updaterecorddata-root-allocation.patch
new file mode 100644 (file)
index 0000000..c067f62
--- /dev/null
@@ -0,0 +1,80 @@
+From 3e127829e57f5190f612412ece4541cb96d5ec7a Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Tue, 19 May 2026 05:51:35 -0400
+Subject: fs/ntfs3: bound NTFS_DE view.data_off in UpdateRecordData{Root,Allocation}
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 3e127829e57f5190f612412ece4541cb96d5ec7a upstream.
+
+In do_action()'s UpdateRecordDataRoot (fslog.c:3489) and
+UpdateRecordDataAllocation (fslog.c:3697) cases, the memmove
+destination is `Add2Ptr(e, le16_to_cpu(e->view.data_off))`,
+where e->view.data_off comes from an on-disk NTFS_DE inside
+an INDEX_ROOT or INDEX_BUFFER.  Neither case validates
+view.data_off + dlen against e->size; the existing
+check_if_index_root / check_if_alloc_index helpers walk the
+entry chain and validate the entry's offset, but not its
+internal view fields.
+
+The neighbouring read sites (e.g., fs/ntfs3/index.c when
+iterating view entries) check view.data_off + view.data_size
+<= e->size.  Apply the same bound at the two memmove sites.
+
+Reproduced under UML+KASAN on mainline 8d90b09e6741 via
+pr_warn-only probe instrumentation: with view.data_off forced
+to 0xFFFC, the memmove writes 32 bytes past the end of the
+NTFS_DE.
+
+This is similar in shape to Pavitra Jha's 2026-05-02 patch
+"fs/ntfs3: prevent oob in case UpdateRecordDataRoot"
+(<20260502105008.21827-1-jhapavitra98@gmail.com>) which
+proposes calling ntfs3_bad_de_range(); that helper does not
+exist in mainline.  This patch uses inline checks.
+
+Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
+Cc: stable@vger.kernel.org
+Reported-by: Pavitra Jha <jhapavitra98@gmail.com>
+Closes: https://lore.kernel.org/ntfs3/20260502105008.21827-1-jhapavitra98@gmail.com/
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/fslog.c |   18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/fs/ntfs3/fslog.c
++++ b/fs/ntfs3/fslog.c
+@@ -3511,6 +3511,18 @@ move_data:
+               e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
++              /*
++               * e->view.data_off and dlen come from the on-disk
++               * INDEX_ROOT entry / LRH.  The neighbouring read sites
++               * (e.g. fs/ntfs3/index.c) check that
++               * view.data_off + view.data_size <= e->size; mirror that
++               * bound here so the memmove cannot reach past the entry.
++               */
++              if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) ||
++                  le16_to_cpu(e->view.data_off) + dlen >
++                          le16_to_cpu(e->size))
++                      goto dirty_vol;
++
+               memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen);
+               mi->dirty = true;
+@@ -3717,6 +3729,12 @@ move_data:
+                       goto dirty_vol;
+               }
++              /* See UpdateRecordDataRoot for the rationale. */
++              if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) ||
++                  le16_to_cpu(e->view.data_off) + dlen >
++                          le16_to_cpu(e->size))
++                      goto dirty_vol;
++
+               memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen);
+               a_dirty = true;
diff --git a/queue-7.1/fs-ntfs3-fix-syncing-wrong-inode-on-dirsync-cross-directory-rename.patch b/queue-7.1/fs-ntfs3-fix-syncing-wrong-inode-on-dirsync-cross-directory-rename.patch
new file mode 100644 (file)
index 0000000..c5306a5
--- /dev/null
@@ -0,0 +1,45 @@
+From 932fa0c1496286e39e14e27194c1ee7c2181629f Mon Sep 17 00:00:00 2001
+From: Zhan Xusheng <zhanxusheng1024@gmail.com>
+Date: Wed, 6 May 2026 15:55:54 +0800
+Subject: fs/ntfs3: fix syncing wrong inode on DIRSYNC cross-directory rename
+
+From: Zhan Xusheng <zhanxusheng1024@gmail.com>
+
+commit 932fa0c1496286e39e14e27194c1ee7c2181629f upstream.
+
+In ntfs3_rename(), when IS_DIRSYNC(new_dir) is true, the code syncs
+the renamed file inode instead of the target directory new_dir:
+    if (IS_DIRSYNC(new_dir))
+        ntfs_sync_inode(inode);      /* should be new_dir */
+
+DIRSYNC requires that directory metadata changes are written to disk
+synchronously.  Since new_dir was modified (a new directory entry was
+added), it is new_dir that must be synced to satisfy the guarantee,
+not the renamed file itself.
+
+This bug has existed since the initial ntfs3 implementation and was
+carried through the refactoring in commit 78ab59fee07f
+("fs/ntfs3: Rework file operations").
+
+Fix by syncing new_dir instead of inode.
+
+Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation")
+Cc: stable@vger.kernel.org
+Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/namei.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/ntfs3/namei.c
++++ b/fs/ntfs3/namei.c
+@@ -340,7 +340,7 @@ static int ntfs_rename(struct mnt_idmap
+                       ntfs_sync_inode(dir);
+               if (IS_DIRSYNC(new_dir))
+-                      ntfs_sync_inode(inode);
++                      ntfs_sync_inode(new_dir);
+       }
+       if (dir_ni != new_dir_ni)
diff --git a/queue-7.1/fs-ntfs3-validate-lcns_follow-in-log_replay-conversion.patch b/queue-7.1/fs-ntfs3-validate-lcns_follow-in-log_replay-conversion.patch
new file mode 100644 (file)
index 0000000..601117e
--- /dev/null
@@ -0,0 +1,76 @@
+From 6a4c53a2e26a865565bd6a460961e8d6fcb32329 Mon Sep 17 00:00:00 2001
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Date: Mon, 1 Jun 2026 10:57:56 +0200
+Subject: fs/ntfs3: validate lcns_follow in log_replay conversion
+
+From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+
+commit 6a4c53a2e26a865565bd6a460961e8d6fcb32329 upstream.
+
+log_replay() converts DIR_PAGE_ENTRY_32 records into DIR_PAGE_ENTRY
+records when replaying version 0 restart tables.
+
+During this conversion, the memmove() length is derived directly from
+the on-disk lcns_follow field:
+
+       memmove(&dp->vcn, &dp0->vcn_low,
+               2 * sizeof(u64) +
+                               le32_to_cpu(dp->lcns_follow) * sizeof(u64));
+
+check_rstbl() validates restart table structure, but does not constrain
+per-entry lcns_follow values relative to the entry size. A malformed
+filesystem image can provide an oversized lcns_follow value, causing
+the conversion memmove() to access memory beyond the bounds of the
+allocated restart table buffer.
+
+The same field is later used to bound iteration over page_lcns[],
+so validating lcns_follow during conversion also prevents downstream
+out-of-bounds access from the same malformed metadata.
+
+Compute the maximum valid lcns_follow from the already-validated
+restart table entry size and reject entries that exceed this bound.
+Reuse the existing t16/t32 scratch variables already declared in
+log_replay() to avoid introducing new declarations.
+
+Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
+Cc: stable@vger.kernel.org
+Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
+[almaz.alexandrovich@paragon-software.com: fixed the conflicts]
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/fslog.c |   19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+--- a/fs/ntfs3/fslog.c
++++ b/fs/ntfs3/fslog.c
+@@ -4262,13 +4262,26 @@ check_dirty_page_table:
+       if (rst->major_ver)
+               goto end_conv_1; /* reduce tab pressure. */
++      t16 = le16_to_cpu(dptbl->size);
++      if (t16 < sizeof(struct DIR_PAGE_ENTRY)) {
++              log->set_dirty = true;
++              goto out;
++      }
++
++      t32 = (t16 - sizeof(struct DIR_PAGE_ENTRY)) / sizeof(u64);
++
+       dp = NULL;
+       while ((dp = enum_rstbl(dptbl, dp))) {
+               struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp;
+-              // NOTE: Danger. Check for of boundary.
++              u32 lcns = le32_to_cpu(dp->lcns_follow);
++
++              if (lcns > t32) {
++                      log->set_dirty = true;
++                      goto out;
++              }
++
+               memmove(&dp->vcn, &dp0->vcn_low,
+-                      2 * sizeof(u64) +
+-                              le32_to_cpu(dp->lcns_follow) * sizeof(u64));
++                      2 * sizeof(u64) + lcns * sizeof(u64));
+       }
+ end_conv_1:
diff --git a/queue-7.1/fs-proc-fix-kpf_ksm-reported-for-all-anonymous-pages.patch b/queue-7.1/fs-proc-fix-kpf_ksm-reported-for-all-anonymous-pages.patch
new file mode 100644 (file)
index 0000000..1e0dbaa
--- /dev/null
@@ -0,0 +1,52 @@
+From 81401cebfc1598306b0a981b5f9ee5b58c1aac52 Mon Sep 17 00:00:00 2001
+From: Jinjiang Tu <tujinjiang@huawei.com>
+Date: Fri, 26 Jun 2026 09:32:52 +0800
+Subject: fs/proc: fix KPF_KSM reported for all anonymous pages
+
+From: Jinjiang Tu <tujinjiang@huawei.com>
+
+commit 81401cebfc1598306b0a981b5f9ee5b58c1aac52 upstream.
+
+Reading /proc/kpageflags for any anonymous page returns KPF_KSM set, even
+when KSM is not in use.  As a result, tools misclassify all anonymous
+pages as KSM merged.
+
+In stable_page_flags(), if the page is anonymous, then use (mapping &
+FOLIO_MAPPING_KSM) check to identify if the anonymous page is KSM page.
+However, FOLIO_MAPPING_KSM is FOLIO_MAPPING_ANON | FOLIO_MAPPING_ANON_KSM,
+(mapping & FOLIO_MAPPING_KSM) check returns true for all anonymous pages.
+
+To fix it, use FOLIO_MAPPING_ANON_KSM instead.
+
+Link: https://lore.kernel.org/20260629033122.774318-1-tujinjiang@huawei.com
+Link: https://lore.kernel.org/20260626013252.2846774-1-tujinjiang@huawei.com
+Fixes: dee3d0bef2b0 ("proc: rewrite stable_page_flags()")
+Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Acked-by: Zi Yan <ziy@nvidia.com>
+Reviewed-by: Xu Xin <xu.xin16@zte.com.cn>
+Cc: Chengming Zhou <chengming.zhou@linux.dev>
+Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
+Cc: Luiz Capitulino <luizcap@redhat.com>
+Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
+Cc: Miaohe Lin <linmiaohe@huawei.com>
+Cc: Nanyong Sun <sunnanyong@huawei.com>
+Cc: Svetly Todorov <svetly.todorov@memverge.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/page.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/proc/page.c
++++ b/fs/proc/page.c
+@@ -173,7 +173,7 @@ u64 stable_page_flags(const struct page
+               u |= 1 << KPF_MMAP;
+       if (is_anon) {
+               u |= 1 << KPF_ANON;
+-              if (mapping & FOLIO_MAPPING_KSM)
++              if ((mapping & FOLIO_MAPPING_FLAGS) == FOLIO_MAPPING_KSM)
+                       u |= 1 << KPF_KSM;
+       }
diff --git a/queue-7.1/fs-proc-task_mmu-do-not-warn-on-seeing-non-migration-pmd-entry.patch b/queue-7.1/fs-proc-task_mmu-do-not-warn-on-seeing-non-migration-pmd-entry.patch
new file mode 100644 (file)
index 0000000..fe65ff6
--- /dev/null
@@ -0,0 +1,52 @@
+From cd1fc0e3c1f67c0c31dfc215e5d9b771133dedc0 Mon Sep 17 00:00:00 2001
+From: Dev Jain <dev.jain@arm.com>
+Date: Thu, 4 Jun 2026 05:53:05 +0000
+Subject: fs/proc/task_mmu: do not warn on seeing non-migration pmd entry
+
+From: Dev Jain <dev.jain@arm.com>
+
+commit cd1fc0e3c1f67c0c31dfc215e5d9b771133dedc0 upstream.
+
+Patch series "mm/hmm: A fix and a selftest", v3.
+
+Patch 1 fixes a stale warning present from the time when only migration
+softleaf entries were supported at the PMD level.
+
+Patch 2 adds some code into hmm-tests.c which exercises the pagemap path
+for PMD device-private entries.
+
+
+This patch (of 2):
+
+pagemap_pmd_range_thp() warns if a non-present PMD is not a migration
+entry.  This became false once device-private entries at the PMD level
+were added.
+
+Therefore, remove the stale migration-only assertion.
+
+Link: https://lore.kernel.org/20260604055308.1947679-1-dev.jain@arm.com
+Link: https://lore.kernel.org/20260604055308.1947679-2-dev.jain@arm.com
+Fixes: a30b48bf1b24 ("mm/migrate_device: implement THP migration of zone device pages")
+Signed-off-by: Dev Jain <dev.jain@arm.com>
+Reviewed-by: Balbir Singh <balbirs@nvidia.com>
+Reviewed-by: Lorenzo Stoakes <ljs@kernel.org>
+Tested-by: Lorenzo Stoakes <ljs@kernel.org>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Reviewed-by: Oscar Salvador (SUSE) <osalvador@kernel.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/task_mmu.c |    1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/fs/proc/task_mmu.c
++++ b/fs/proc/task_mmu.c
+@@ -2042,7 +2042,6 @@ static int pagemap_pmd_range_thp(pmd_t *
+                       flags |= PM_SOFT_DIRTY;
+               if (pmd_swp_uffd_wp(pmd))
+                       flags |= PM_UFFD_WP;
+-              VM_WARN_ON_ONCE(!pmd_is_migration_entry(pmd));
+               page = softleaf_to_page(entry);
+       }
diff --git a/queue-7.1/fs-proc-task_mmu-fix-hugetlb-self-deadlock-in-pagemap_scan_pte_hole.patch b/queue-7.1/fs-proc-task_mmu-fix-hugetlb-self-deadlock-in-pagemap_scan_pte_hole.patch
new file mode 100644 (file)
index 0000000..fce4d01
--- /dev/null
@@ -0,0 +1,140 @@
+From e92d92bbafb264dc0518d52b846a3c07ed8d523f Mon Sep 17 00:00:00 2001
+From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
+Date: Fri, 29 May 2026 18:23:27 +0100
+Subject: fs/proc/task_mmu: fix hugetlb self-deadlock in pagemap_scan_pte_hole()
+
+From: Kiryl Shutsemau (Meta) <kas@kernel.org>
+
+commit e92d92bbafb264dc0518d52b846a3c07ed8d523f upstream.
+
+A PAGEMAP_SCAN ioctl requesting PM_SCAN_WP_MATCHING on a hugetlb VMA hangs
+the calling thread, unkillably, as soon as the scan reaches an unpopulated
+part of the range:
+
+  do_pagemap_scan()
+    walk_page_range()
+      walk_hugetlb_range()
+        hugetlb_vma_lock_read()           # take the vma lock for read ...
+        pagemap_scan_pte_hole()           # ... ->pte_hole() for a hole
+          uffd_wp_range()
+            change_protection()
+              hugetlb_change_protection()
+                hugetlb_vma_lock_write()  # ... and block taking it for write
+
+walk_hugetlb_range() holds the hugetlb vma lock for read across the whole
+walk.  A present entry goes to ->hugetlb_entry(); an unpopulated one goes
+to ->pte_hole(), i.e.  pagemap_scan_pte_hole().  To write-protect the hole
+that handler calls uffd_wp_range(), which on a hugetlb VMA reaches
+hugetlb_change_protection() and takes the same vma lock for write.  The
+thread then blocks in down_write() waiting for the read lock it is itself
+holding.
+
+The populated path avoids this: pagemap_scan_hugetlb_entry()
+write-protects the entry inline under the page-table lock and never enters
+hugetlb_change_protection().
+
+Do the same for holes.  Fault in the page table and install the uffd-wp
+marker directly with make_uffd_wp_huge_pte() under the page-table lock,
+rather than routing through uffd_wp_range().  That is the same sequence
+hugetlb_change_protection() runs for an unpopulated entry, minus the vma
+write lock -- which is safe to skip because PMD sharing is disabled on
+uffd-wp VMAs (hugetlb_unshare_all_pmds() runs at registration), leaving
+nothing for that lock to serialise against.
+
+Link: https://lore.kernel.org/20260529172331.356655-4-kas@kernel.org
+Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs")
+Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
+Reported-by: Sashiko AI review <sashiko-bot@kernel.org>
+Assisted-by: Claude:claude-opus-4-8
+Cc: David Hildenbrand <david@kernel.org>
+Cc: Lorenzo Stoakes <ljs@kernel.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Peter Xu <peterx@redhat.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: Balbir Singh <balbirs@nvidia.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/task_mmu.c |   59 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 58 insertions(+), 1 deletion(-)
+
+--- a/fs/proc/task_mmu.c
++++ b/fs/proc/task_mmu.c
+@@ -2890,8 +2890,62 @@ out_unlock:
+       return ret;
+ }
++
++/*
++ * Write-protect the unpopulated hugetlb entries covering [addr, end) by
++ * installing uffd-wp markers inline, exactly as pagemap_scan_hugetlb_entry()
++ * does for populated entries.
++ *
++ * walk_hugetlb_range() currently calls ->pte_hole() once per huge page, so the
++ * loop normally runs a single iteration; it is written to cover the full range
++ * in case the walker ever coalesces adjacent holes.
++ *
++ * The obvious route -- uffd_wp_range() -> hugetlb_change_protection() --
++ * cannot be used here: it takes hugetlb_vma_lock_write(), but the page-table
++ * walker (walk_hugetlb_range()) already holds hugetlb_vma_lock_read() on the
++ * same VMA, so the scanning thread would deadlock against itself. PMD sharing
++ * is disabled on uffd-wp VMAs (hugetlb_unshare_all_pmds() at registration), so
++ * the vma lock guards nothing that matters for these entries anyway.
++ */
++static int pagemap_scan_hugetlb_hole_wp(struct vm_area_struct *vma,
++                                      unsigned long addr, unsigned long end)
++{
++      struct hstate *h = hstate_vma(vma);
++      unsigned long psize = huge_page_size(h);
++      struct mm_struct *mm = vma->vm_mm;
++      spinlock_t *ptl;
++      pte_t *ptep;
++      pte_t pte;
++
++      for (addr = ALIGN_DOWN(addr, psize); addr < end; addr += psize) {
++              ptep = huge_pte_alloc(mm, vma, addr, psize);
++              if (!ptep)
++                      return -ENOMEM;
++
++              i_mmap_lock_write(vma->vm_file->f_mapping);
++              ptl = huge_pte_lock(h, mm, ptep);
++              pte = huge_ptep_get(mm, addr, ptep);
++              make_uffd_wp_huge_pte(vma, addr, ptep, pte);
++              /*
++               * A none entry has no cached translation, so installing the
++               * marker needs no TLB flush. Flush only if a fault populated
++               * the entry between huge_pte_alloc() and the page table lock.
++               */
++              if (!huge_pte_none(pte))
++                      flush_hugetlb_tlb_range(vma, addr, addr + psize);
++              spin_unlock(ptl);
++              i_mmap_unlock_write(vma->vm_file->f_mapping);
++      }
++
++      return 0;
++}
+ #else
+ #define pagemap_scan_hugetlb_entry NULL
++static int pagemap_scan_hugetlb_hole_wp(struct vm_area_struct *vma,
++                                      unsigned long addr, unsigned long end)
++{
++      return 0;
++}
+ #endif
+ static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end,
+@@ -2911,7 +2965,10 @@ static int pagemap_scan_pte_hole(unsigne
+       if (~p->arg.flags & PM_SCAN_WP_MATCHING)
+               return ret;
+-      err = uffd_wp_range(vma, addr, end - addr, true);
++      if (is_vm_hugetlb_page(vma))
++              err = pagemap_scan_hugetlb_hole_wp(vma, addr, end);
++      else
++              err = uffd_wp_range(vma, addr, end - addr, true);
+       if (err < 0)
+               ret = err;
diff --git a/queue-7.1/fs-proc-task_mmu-fix-make_uffd_wp_huge_pte-prot-update-race.patch b/queue-7.1/fs-proc-task_mmu-fix-make_uffd_wp_huge_pte-prot-update-race.patch
new file mode 100644 (file)
index 0000000..a9f77f6
--- /dev/null
@@ -0,0 +1,112 @@
+From 04718f7c9290f95385f0dd328758753dc1c36dec Mon Sep 17 00:00:00 2001
+From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
+Date: Fri, 29 May 2026 18:23:25 +0100
+Subject: fs/proc/task_mmu: fix make_uffd_wp_huge_pte() prot-update race
+
+From: Kiryl Shutsemau (Meta) <kas@kernel.org>
+
+commit 04718f7c9290f95385f0dd328758753dc1c36dec upstream.
+
+Patch series "userfaultfd/pagemap: pre-existing fixes".
+
+These are pre-existing bug fixes that were carried at the front of the
+userfaultfd RWP working-set-tracking series up to v5 [1].  Per review
+feedback that fixes should not sit in the middle of a feature series, they
+are split out and sent on their own; the RWP series is reposted rebased on
+top of this.
+
+All six were flagged by the Sashiko AI review of the RWP series and carry
+Reported-by: Sashiko AI review <sashiko-bot@kernel.org>.  They are
+independent of RWP, apply to mm-new directly, and carry Cc: stable@.
+
+  1: fs/proc/task_mmu: a missing huge_ptep_modify_prot_start() in
+     make_uffd_wp_huge_pte() can lose hardware Dirty/Accessed updates
+     when PAGEMAP_SCAN write-protects a hugetlb PTE.
+
+  2: fs/proc/task_mmu: pagemap_scan_hugetlb_entry() compares the range
+     against HPAGE_SIZE rather than the hstate page size, so it never
+     write-protects gigantic hugetlb pages.
+
+  3: fs/proc/task_mmu: PAGEMAP_SCAN with PM_SCAN_WP_MATCHING over an
+     unpopulated hugetlb range self-deadlocks -- pagemap_scan_pte_hole()
+     calls uffd_wp_range() while walk_hugetlb_range() holds the hugetlb
+     vma lock for read, and hugetlb_change_protection() then takes it
+     for write. Install the marker inline instead.
+
+  4: mm/huge_memory: change_non_present_huge_pmd() drops pmd_swp_uffd_wp
+     on a device-private PMD permission downgrade, silently losing the
+     uffd-wp marker.
+
+  5: userfaultfd: must_wait() applies pte_write() to a locklessly read
+     PTE without checking pte_present(), so swap/migration entries
+     decode random offset bits and a thread can stay parked on a stale
+     fault.
+
+  6: userfaultfd: __VMA_UFFD_FLAGS feeds VMA_UFFD_MINOR_BIT (41) to
+     mk_vma_flags() unconditionally, an out-of-bounds write into the
+     single-word vma_flags_t on 32-bit. Build the mask from config-gated
+     per-mode masks so an unavailable bit is never materialised.
+
+
+This patch (of 6):
+
+make_uffd_wp_huge_pte() arms the UFFD_WP bit on a present HugeTLB PTE by
+calling huge_ptep_modify_prot_commit() with a ptent snapshot that was
+fetched without the corresponding huge_ptep_modify_prot_start().  The
+start helper is what atomically clears the entry so the kernel-owned
+snapshot stays consistent until the commit; without it, the hardware may
+set Dirty or Accessed in the live PTE between the original read and the
+commit, and huge_ptep_modify_prot_commit() (whose generic implementation
+just calls set_huge_pte_at()) then writes the stale snapshot back over the
+live hardware bits, losing the update.
+
+The non-hugetlb sibling make_uffd_wp_pte() does this correctly via
+ptep_modify_prot_start() / ptep_modify_prot_commit().  Mirror that pattern
+for the present-PTE branch.  The migration case stays as-is -- migration
+entries are non-present, so there's no hardware update to race against.
+
+Link: https://lore.kernel.org/20260529172331.356655-1-kas@kernel.org
+Link: https://lore.kernel.org/20260529172331.356655-2-kas@kernel.org
+Link: https://lore.kernel.org/all/20260526130509.2748441-1-kirill@shutemov.name/ [1]
+Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs")
+Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
+Reported-by: Sashiko AI review <sashiko-bot@kernel.org>
+Reviewed-by: Lorenzo Stoakes <ljs@kernel.org>
+Reviewed-by: Dev Jain <dev.jain@arm.com>
+Cc: David Hildenbrand <david@kernel.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Peter Xu <peterx@redhat.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: Balbir Singh <balbirs@nvidia.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/task_mmu.c |   12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/fs/proc/task_mmu.c
++++ b/fs/proc/task_mmu.c
+@@ -2523,12 +2523,16 @@ static void make_uffd_wp_huge_pte(struct
+       if (softleaf_is_hwpoison(entry) || softleaf_is_marker(entry))
+               return;
+-      if (softleaf_is_migration(entry))
++      if (softleaf_is_migration(entry)) {
+               set_huge_pte_at(vma->vm_mm, addr, ptep,
+                               pte_swp_mkuffd_wp(ptent), psize);
+-      else
+-              huge_ptep_modify_prot_commit(vma, addr, ptep, ptent,
+-                                           huge_pte_mkuffd_wp(ptent));
++      } else {
++              pte_t old_pte, new_pte;
++
++              old_pte = huge_ptep_modify_prot_start(vma, addr, ptep);
++              new_pte = huge_pte_mkuffd_wp(old_pte);
++              huge_ptep_modify_prot_commit(vma, addr, ptep, old_pte, new_pte);
++      }
+ }
+ #endif /* CONFIG_HUGETLB_PAGE */
diff --git a/queue-7.1/fs-proc-task_mmu-use-huge_page_size-in-pagemap_scan_hugetlb_entry.patch b/queue-7.1/fs-proc-task_mmu-use-huge_page_size-in-pagemap_scan_hugetlb_entry.patch
new file mode 100644 (file)
index 0000000..cf37d85
--- /dev/null
@@ -0,0 +1,46 @@
+From 1b074e3270e1c061c829150c742eb83bad4dddd1 Mon Sep 17 00:00:00 2001
+From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
+Date: Fri, 29 May 2026 18:23:26 +0100
+Subject: fs/proc/task_mmu: use huge_page_size() in pagemap_scan_hugetlb_entry()
+
+From: Kiryl Shutsemau (Meta) <kas@kernel.org>
+
+commit 1b074e3270e1c061c829150c742eb83bad4dddd1 upstream.
+
+The partial-page check compares against HPAGE_SIZE (PMD_SIZE), which is
+wrong for gigantic hugetlb hstates (e.g.  1G).  The walker hands the
+callback a huge_page_size()-sized range, never start + HPAGE_SIZE, so the
+comparison always declares it partial and aborts the WP.  Compare against
+the actual hstate's page size.
+
+Link: https://lore.kernel.org/20260529172331.356655-3-kas@kernel.org
+Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs")
+Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
+Reported-by: Sashiko AI review <sashiko-bot@kernel.org>
+Reviewed-by: Lorenzo Stoakes <ljs@kernel.org>
+Reviewed-by: Dev Jain <dev.jain@arm.com>
+Cc: David Hildenbrand <david@kernel.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Peter Xu <peterx@redhat.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: Balbir Singh <balbirs@nvidia.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/task_mmu.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/proc/task_mmu.c
++++ b/fs/proc/task_mmu.c
+@@ -2873,7 +2873,7 @@ static int pagemap_scan_hugetlb_entry(pt
+       if (~categories & PAGE_IS_WRITTEN)
+               goto out_unlock;
+-      if (end != start + HPAGE_SIZE) {
++      if (end != start + huge_page_size(hstate_vma(vma))) {
+               /* Partial HugeTLB page WP isn't possible. */
+               pagemap_scan_backout_range(p, start, end);
+               p->arg.walk_end = start;
diff --git a/queue-7.1/kcov-use-write_once-for-selftest-mode-stores.patch b/queue-7.1/kcov-use-write_once-for-selftest-mode-stores.patch
new file mode 100644 (file)
index 0000000..796f184
--- /dev/null
@@ -0,0 +1,68 @@
+From 9a79524d1420e6b79a6868208c264f4518d1318e Mon Sep 17 00:00:00 2001
+From: Karl Mehltretter <kmehltretter@gmail.com>
+Date: Tue, 26 May 2026 13:47:15 +0200
+Subject: kcov: use WRITE_ONCE() for selftest mode stores
+
+From: Karl Mehltretter <kmehltretter@gmail.com>
+
+commit 9a79524d1420e6b79a6868208c264f4518d1318e upstream.
+
+The KCOV selftest enables coverage by setting current->kcov_mode to
+KCOV_MODE_TRACE_PC without installing a coverage area.  If an interrupt
+records coverage in that window, the access should fault and expose the
+bug.
+
+When building for QEMU raspi0 (Raspberry Pi Zero, ARMv6, CONFIG_CPU_V6K=y,
+CONFIG_CURRENT_POINTER_IN_TPIDRURO=y) with GCC 13.3.0, the store that
+enables the mode is removed.  The generated kcov_init() code only stores
+zero after the wait loop:
+
+  mrc  15, 0, r3, cr13, cr0, {3}
+  str  r4, [r3, #2028]
+
+where r4 is zero.  There is no store of KCOV_MODE_TRACE_PC before the
+loop, so the selftest reports success without exercising coverage.
+
+Use WRITE_ONCE() for the temporary mode stores.  With the same compiler
+and config, kcov_init() contains the intended mode store:
+
+  mov  r3, #2
+  mrc  15, 0, r2, cr13, cr0, {3}
+  str  r3, [r2, #2028]
+
+Now that the KCOV selftest is actually executed, it may expose KCOV
+instrumentation issues depending on the kernel config.  That is expected
+for a selftest that was intended to catch coverage from interrupt paths.
+
+Link: https://lore.kernel.org/20260526114715.38280-1-kmehltretter@gmail.com
+Fixes: 6cd0dd934b03 ("kcov: Add interrupt handling self test")
+Assisted-by: Codex:gpt-5
+Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
+Reviewed-by: Alexander Potapenko <glider@google.com>
+Cc: Andrey Konovalov <andreyknvl@gmail.com>
+Cc: Dmitry Vyukov <dvyukov@google.com>
+Cc: Kees Cook <kees@kernel.org>
+Cc: Marco Elver <elver@google.com>
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/kcov.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/kernel/kcov.c
++++ b/kernel/kcov.c
+@@ -1109,10 +1109,10 @@ static void __init selftest(void)
+        * potentially traced functions in this region.
+        */
+       start = jiffies;
+-      current->kcov_mode = KCOV_MODE_TRACE_PC;
++      WRITE_ONCE(current->kcov_mode, KCOV_MODE_TRACE_PC);
+       while ((jiffies - start) * MSEC_PER_SEC / HZ < 300)
+               ;
+-      current->kcov_mode = 0;
++      WRITE_ONCE(current->kcov_mode, 0);
+       pr_err("done running self test\n");
+ }
+ #endif
diff --git a/queue-7.1/kho-make-sure-scratch-size-is-always-aligned-by-cma_min_alignment_bytes.patch b/queue-7.1/kho-make-sure-scratch-size-is-always-aligned-by-cma_min_alignment_bytes.patch
new file mode 100644 (file)
index 0000000..67d88b9
--- /dev/null
@@ -0,0 +1,72 @@
+From 0e39380a7316122e1b00012b3f3cd3e318b3e7d3 Mon Sep 17 00:00:00 2001
+From: "Pratyush Yadav (Google)" <pratyush@kernel.org>
+Date: Tue, 19 May 2026 18:05:49 +0200
+Subject: kho: make sure scratch size is always aligned by CMA_MIN_ALIGNMENT_BYTES
+
+From: Pratyush Yadav (Google) <pratyush@kernel.org>
+
+commit 0e39380a7316122e1b00012b3f3cd3e318b3e7d3 upstream.
+
+When using scratch_scale, the scratch sizes are rounded up to
+CMA_MIN_ALIGNMENT_BYTES since they will be released as MIGRATE_CMA. This
+is not done when using fixed scratch sizes via command line. This can
+result in user specifying a size which is not aligned, and thus kernel
+releasing a pageblock that is only partially scratch.
+
+Do the rounding up for both cases in scratch_size_update().
+
+Fixes: 3dc92c311498 ("kexec: add Kexec HandOver (KHO) generation helpers")
+Cc: stable@kernel.org
+Signed-off-by: Pratyush Yadav (Google) <pratyush@kernel.org>
+Link: https://patch.msgid.link/20260519160554.2713361-1-pratyush@kernel.org
+Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
+Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/liveupdate/kexec_handover.c |   32 +++++++++++++++++++++-----------
+ 1 file changed, 21 insertions(+), 11 deletions(-)
+
+--- a/kernel/liveupdate/kexec_handover.c
++++ b/kernel/liveupdate/kexec_handover.c
+@@ -593,20 +593,30 @@ early_param("kho_scratch", kho_parse_scr
+ static void __init scratch_size_update(void)
+ {
+-      phys_addr_t size;
++      /*
++       * If fixed sizes are not provided via command line, calculate them
++       * now.
++       */
++      if (scratch_scale) {
++              phys_addr_t size;
+-      if (!scratch_scale)
+-              return;
++              size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT,
++                                                 NUMA_NO_NODE);
++              size = size * scratch_scale / 100;
++              scratch_size_lowmem = size;
+-      size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT,
+-                                         NUMA_NO_NODE);
+-      size = size * scratch_scale / 100;
+-      scratch_size_lowmem = round_up(size, CMA_MIN_ALIGNMENT_BYTES);
++              size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,
++                                                 NUMA_NO_NODE);
++              size = size * scratch_scale / 100 - scratch_size_lowmem;
++              scratch_size_global = size;
++      }
+-      size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,
+-                                         NUMA_NO_NODE);
+-      size = size * scratch_scale / 100 - scratch_size_lowmem;
+-      scratch_size_global = round_up(size, CMA_MIN_ALIGNMENT_BYTES);
++      /*
++       * Scratch areas are released as MIGRATE_CMA. Round them up to the right
++       * size.
++       */
++      scratch_size_lowmem = round_up(scratch_size_lowmem, CMA_MIN_ALIGNMENT_BYTES);
++      scratch_size_global = round_up(scratch_size_global, CMA_MIN_ALIGNMENT_BYTES);
+ }
+ static phys_addr_t __init scratch_size_node(int nid)
diff --git a/queue-7.1/landlock-fix-landlock_scope_signal-bypass-on-the-sigio-path.patch b/queue-7.1/landlock-fix-landlock_scope_signal-bypass-on-the-sigio-path.patch
new file mode 100644 (file)
index 0000000..51bc05e
--- /dev/null
@@ -0,0 +1,163 @@
+From 4b80320ca7ed03d6e683f95b6066565dc97b9f92 Mon Sep 17 00:00:00 2001
+From: Bryam Vargas <hexlabsecurity@proton.me>
+Date: Thu, 4 Jun 2026 23:16:56 +0000
+Subject: landlock: Fix LANDLOCK_SCOPE_SIGNAL bypass on the SIGIO path
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Bryam Vargas <hexlabsecurity@proton.me>
+
+commit 4b80320ca7ed03d6e683f95b6066565dc97b9f92 upstream.
+
+LANDLOCK_SCOPE_SIGNAL must prevent a sandboxed process from signaling
+processes outside its Landlock domain.  It can be bypassed through the
+asynchronous SIGIO delivery path.
+
+A sandboxed process that owns any file or socket can arm it with
+fcntl(fd, F_SETOWN, -pgid), fcntl(fd, F_SETSIG, SIGKILL) and O_ASYNC, so
+that an I/O event makes the kernel deliver the chosen signal to the
+whole process group.  As the head of its process group's task list (the
+default position right after fork()) that group can also hold the
+non-sandboxed process that launched it, e.g. a supervisor or a security
+monitor.  The sandbox can thus kill or signal the processes
+LANDLOCK_SCOPE_SIGNAL is meant to protect from it.
+
+The scope is enforced in hook_file_send_sigiotask() against the Landlock
+domain recorded at F_SETOWN time, not the live domain of the sender.
+control_current_fowner() decides whether to record that domain and skips
+recording it when the fowner target is in the caller's thread group,
+which is safe only for a single-task target (PIDTYPE_PID, PIDTYPE_TGID).
+For a process group (PIDTYPE_PGID) pid_task() returns only one member;
+recording is skipped whenever that member shares the caller's thread
+group, and hook_file_send_sigiotask() then lets the signal fan out to
+the whole group unchecked.
+
+Record the domain for every non single-process target so the scope is
+enforced against each group member at delivery time.
+
+That recording is necessary but not sufficient on its own: the kernel
+signals a process group through its members' thread-group leaders, and
+the leader of the registrant's own process can carry a different
+Landlock domain than the sibling thread that armed the owner.
+domain_is_scoped() would then deny that leader, even though commit
+18eb75f3af40 ("landlock: Always allow signals between threads of the
+same process") requires same-process delivery to be allowed.
+hook_task_kill() avoids this by evaluating same_thread_group() live, per
+recipient; the SIGIO path instead delegates the whole decision to a
+single registration-time check, which a process-group fan-out cannot
+honor.
+
+So also record the registrant's thread group next to its domain and
+exempt it at delivery: hook_file_send_sigiotask() allows the signal
+whenever the recipient belongs to the registrant's own process,
+restoring the same-process guarantee while keeping out-of-domain group
+members blocked.  The direct kill() path (hook_task_kill) already
+evaluates the live domain and is unaffected.
+
+Fixes: 18eb75f3af40 ("landlock: Always allow signals between threads of the same process")
+Cc: stable@vger.kernel.org
+Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
+Reviewed-by: Günther Noack <gnoack3000@gmail.com>
+Link: https://patch.msgid.link/56bffc24f3d0d08b45a686a48e99766b0a0821fa.1780614610.git.hexlabsecurity@proton.me
+[mic: Check pid_type earlier and improve comment, fix commit message,
+fix comment formatting]
+Signed-off-by: Mickaël Salaün <mic@digikod.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ security/landlock/fs.c   |   14 ++++++++++++++
+ security/landlock/fs.h   |   10 ++++++++++
+ security/landlock/task.c |   11 +++++++++++
+ 3 files changed, 35 insertions(+)
+
+--- a/security/landlock/fs.c
++++ b/security/landlock/fs.c
+@@ -1901,6 +1901,14 @@ static bool control_current_fowner(struc
+       lockdep_assert_held(&fown->lock);
+       /*
++       * A process-group or session owner (PIDTYPE_PGID/PIDTYPE_SID) fans the
++       * signal out to every member at delivery time, so record the domain and
++       * let hook_file_send_sigiotask() check the live scope per recipient.
++       */
++      if (fown->pid_type != PIDTYPE_PID && fown->pid_type != PIDTYPE_TGID)
++              return true;
++
++      /*
+        * Some callers (e.g. fcntl_dirnotify) may not be in an RCU read-side
+        * critical section.
+        */
+@@ -1916,6 +1924,7 @@ static void hook_file_set_fowner(struct
+ {
+       struct landlock_ruleset *prev_dom;
+       struct landlock_cred_security fown_subject = {};
++      struct pid *prev_tg, *fown_tg = NULL;
+       size_t fown_layer = 0;
+       if (control_current_fowner(file_f_owner(file))) {
+@@ -1928,21 +1937,26 @@ static void hook_file_set_fowner(struct
+               if (new_subject) {
+                       landlock_get_ruleset(new_subject->domain);
+                       fown_subject = *new_subject;
++                      fown_tg = get_pid(task_tgid(current));
+               }
+       }
+       prev_dom = landlock_file(file)->fown_subject.domain;
++      prev_tg = landlock_file(file)->fown_tg;
+       landlock_file(file)->fown_subject = fown_subject;
++      landlock_file(file)->fown_tg = fown_tg;
+ #ifdef CONFIG_AUDIT
+       landlock_file(file)->fown_layer = fown_layer;
+ #endif /* CONFIG_AUDIT*/
+       /* May be called in an RCU read-side critical section. */
+       landlock_put_ruleset_deferred(prev_dom);
++      put_pid(prev_tg);
+ }
+ static void hook_file_free_security(struct file *file)
+ {
++      put_pid(landlock_file(file)->fown_tg);
+       landlock_put_ruleset_deferred(landlock_file(file)->fown_subject.domain);
+ }
+--- a/security/landlock/fs.h
++++ b/security/landlock/fs.h
+@@ -78,6 +78,16 @@ struct landlock_file_security {
+        * euid.
+        */
+       struct landlock_cred_security fown_subject;
++      /**
++       * @fown_tg: Thread group of the task that set the file owner, pinned
++       * while @fown_subject holds a domain.  It lets
++       * hook_file_send_sigiotask() always allow a SIGIO delivered to the
++       * owner's own process -- e.g. the thread-group leader reached through a
++       * process-group owner -- matching the same-process exemption of
++       * hook_task_kill().  NULL when no domain is recorded.  Protected by
++       * file->f_owner->lock, like @fown_subject.
++       */
++      struct pid *fown_tg;
+ };
+ #ifdef CONFIG_AUDIT
+--- a/security/landlock/task.c
++++ b/security/landlock/task.c
+@@ -411,6 +411,17 @@ static int hook_file_send_sigiotask(stru
+       if (!subject->domain)
+               return 0;
++      /*
++       * Always allow delivery to the file owner's own process, including a
++       * thread-group leader reached through a process-group owner.  This
++       * mirrors hook_task_kill()'s same-process exemption and preserves the
++       * guarantee of commit 18eb75f3af40 ("landlock: Always allow signals
++       * between threads of the same process"), which the registration-time
++       * check cannot honor for a process-group target.
++       */
++      if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg)
++              return 0;
++
+       scoped_guard(rcu)
+       {
+               is_scoped = domain_is_scoped(subject->domain,
diff --git a/queue-7.1/mips-dec-ensure-32-bit-stack-location-for-o32-prom_printf.patch b/queue-7.1/mips-dec-ensure-32-bit-stack-location-for-o32-prom_printf.patch
new file mode 100644 (file)
index 0000000..8ae2dba
--- /dev/null
@@ -0,0 +1,123 @@
+From 5ff79e8bdc75db51e30298a75939e2308e7658e0 Mon Sep 17 00:00:00 2001
+From: "Maciej W. Rozycki" <macro@orcam.me.uk>
+Date: Wed, 6 May 2026 23:42:23 +0100
+Subject: MIPS: DEC: Ensure 32-bit stack location for o32 prom_printf()
+
+From: Maciej W. Rozycki <macro@orcam.me.uk>
+
+commit 5ff79e8bdc75db51e30298a75939e2308e7658e0 upstream.
+
+In 64-bit configurations calling any firmware entry points from a kernel
+thread other than the initial one will result in a situation where the
+stack has been placed in the XKPHYS 64-bit memory segment.
+
+Consequently the stack pointer is no longer a 32-bit value and when the
+32-bit firmware code called uses 32-bit ALU operations to manipulate the
+stack pointer, the calculated result is incorrect (in fact in the 64-bit
+MIPS ISA almost all 32-bit ALU operations will produce an unpredictable
+result when executed on 64-bit data) and control goes astray.
+
+This may happen when no final console driver has been enabled in the
+configuration and consequently the initial console continues being used
+late into bootstrap, or with an upcoming change that will switch the zs
+driver to use a platform device, which in turn will make the console
+handover happen only after other kernel threads have already been
+started, and the kernel will hang at:
+
+  pid_max: default: 32768 minimum: 301
+
+or somewhat later, but always before:
+
+  cblist_init_generic: Setting adjustable number of callback queues.
+
+has been printed.
+
+It seems that only the prom_printf() entry point is affected.  Of all
+the other entry points wired only rex_slot_address() and rex_gettcinfo()
+are called from a kernel thread other than the initial one, specifically
+kernel_init(), and they are leaf functions that do no business with the
+stack, having worked with no issue ever since 64-bit support was added
+for the platform back in 2002.
+
+To address this issue then, arrange for the stack to be switched in the
+o32 wrapper as required for prom_printf() only, by supplying call_o32()
+with a pointer to a chunk of initdata space, which is placed in the
+CKSEG0 32-bit compatibility segment, observing that prom_printf() is
+only called from console output handler and therefore with the console
+lock held, implying no need for this code to be reentrant.
+
+Other firmware entry points may be called with interrupts enabled and no
+lock held, and may therefore require that call_o32() be reentrant.  They
+trigger no issue at this point and "if it ain't broke, don't fix it," so
+just leave them alone.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
+Cc: stable@vger.kernel.org # v2.6.12+
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/mips/dec/prom/init.c        |    6 +++++-
+ arch/mips/include/asm/dec/prom.h |   15 +++++++++++++--
+ 2 files changed, 18 insertions(+), 3 deletions(-)
+
+--- a/arch/mips/dec/prom/init.c
++++ b/arch/mips/dec/prom/init.c
+@@ -3,7 +3,7 @@
+  * init.c: PROM library initialisation code.
+  *
+  * Copyright (C) 1998 Harald Koerfgen
+- * Copyright (C) 2002, 2004  Maciej W. Rozycki
++ * Copyright (C) 2002, 2004, 2026  Maciej W. Rozycki
+  */
+ #include <linux/init.h>
+ #include <linux/kernel.h>
+@@ -20,6 +20,10 @@
+ #include <asm/dec/prom.h>
++#ifdef CONFIG_64BIT
++unsigned long o32_stk[O32_STK_SIZE] __initdata = { 0 };
++#endif
++
+ int (*__rex_bootinit)(void);
+ int (*__rex_bootread)(void);
+ int (*__rex_getbitmap)(memmap *);
+--- a/arch/mips/include/asm/dec/prom.h
++++ b/arch/mips/include/asm/dec/prom.h
+@@ -4,7 +4,7 @@
+  *
+  *    DECstation PROM interface.
+  *
+- *    Copyright (C) 2002  Maciej W. Rozycki
++ *    Copyright (C) 2002, 2026  Maciej W. Rozycki
+  *
+  *    Based on arch/mips/dec/prom/prom.h by the Anonymous.
+  */
+@@ -97,6 +97,17 @@ extern int (*__pmax_close)(int);
+ #ifdef CONFIG_64BIT
++#define O32_STK_SIZE 512
++extern unsigned long o32_stk[];
++
++/* Switch the stack if outside the 32-bit address space.  */
++static inline unsigned long *o32_get_stk(void)
++{
++      long fp = (long)__builtin_frame_address(0);
++
++      return fp != (int)fp ? o32_stk + O32_STK_SIZE : NULL;
++}
++
+ /*
+  * On MIPS64 we have to call PROM functions via a helper
+  * dispatcher to accommodate ABI incompatibilities.
+@@ -128,7 +139,7 @@ int __DEC_PROM_O32(_prom_printf, (int (*
+ #define prom_getchar()                _prom_getchar(__prom_getchar, NULL)
+ #define prom_getenv(x)                _prom_getenv(__prom_getenv, NULL, x)
+-#define prom_printf(x...)     _prom_printf(__prom_printf, NULL, x)
++#define prom_printf(x...)     _prom_printf(__prom_printf, o32_get_stk(), x)
+ #else /* !CONFIG_64BIT */
diff --git a/queue-7.1/mips-ip22-gio-fix-device-reference-leak-in-probe.patch b/queue-7.1/mips-ip22-gio-fix-device-reference-leak-in-probe.patch
new file mode 100644 (file)
index 0000000..3f604e2
--- /dev/null
@@ -0,0 +1,39 @@
+From b82930a4c5dbc5c4df39c0f93d968c239f2c6885 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Fri, 24 Apr 2026 12:28:47 +0200
+Subject: MIPS: ip22-gio: fix device reference leak in probe
+
+From: Johan Hovold <johan@kernel.org>
+
+commit b82930a4c5dbc5c4df39c0f93d968c239f2c6885 upstream.
+
+The gio probe function needlessly takes a device reference which is
+never released and therefore prevents unbound gio devices from being
+freed.
+
+Fixes: e84de0c61905 ("MIPS: GIO bus support for SGI IP22/28")
+Cc: stable@vger.kernel.org     # 3.3
+Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/mips/sgi-ip22/ip22-gio.c |    4 ----
+ 1 file changed, 4 deletions(-)
+
+--- a/arch/mips/sgi-ip22/ip22-gio.c
++++ b/arch/mips/sgi-ip22/ip22-gio.c
+@@ -133,13 +133,9 @@ static int gio_device_probe(struct devic
+       if (!drv->probe)
+               return error;
+-      gio_dev_get(gio_dev);
+-
+       match = gio_match_device(drv->id_table, gio_dev);
+       if (match)
+               error = drv->probe(gio_dev, match);
+-      if (error)
+-              gio_dev_put(gio_dev);
+       return error;
+ }
diff --git a/queue-7.1/mips-ip22-gio-fix-gio-device-memory-leak.patch b/queue-7.1/mips-ip22-gio-fix-gio-device-memory-leak.patch
new file mode 100644 (file)
index 0000000..4504a02
--- /dev/null
@@ -0,0 +1,33 @@
+From 7de9a1b45f5a95b58145653c525c8fb80292d9ab Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Fri, 24 Apr 2026 12:28:46 +0200
+Subject: MIPS: ip22-gio: fix gio device memory leak
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 7de9a1b45f5a95b58145653c525c8fb80292d9ab upstream.
+
+The gio device release callback was never wired up so gio devices are
+not freed when the last reference is dropped.
+
+Fixes: e84de0c61905 ("MIPS: GIO bus support for SGI IP22/28")
+Cc: stable@vger.kernel.org     # 3.3
+Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/mips/sgi-ip22/ip22-gio.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/mips/sgi-ip22/ip22-gio.c
++++ b/arch/mips/sgi-ip22/ip22-gio.c
+@@ -101,6 +101,8 @@ int gio_device_register(struct gio_devic
+ {
+       giodev->dev.bus = &gio_bus_type;
+       giodev->dev.parent = &gio_bus;
++      giodev->dev.release = gio_release_dev;
++
+       return device_register(&giodev->dev);
+ }
+ EXPORT_SYMBOL_GPL(gio_device_register);
diff --git a/queue-7.1/mips-ip22-gio-fix-kfree-of-static-object.patch b/queue-7.1/mips-ip22-gio-fix-kfree-of-static-object.patch
new file mode 100644 (file)
index 0000000..0a0589d
--- /dev/null
@@ -0,0 +1,32 @@
+From c62cdd3e919bdf84c37ec46810f87cdb1736e822 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Fri, 24 Apr 2026 12:28:45 +0200
+Subject: MIPS: ip22-gio: fix kfree() of static object
+
+From: Johan Hovold <johan@kernel.org>
+
+commit c62cdd3e919bdf84c37ec46810f87cdb1736e822 upstream.
+
+The gio bus root device is a statically allocated object which must not
+be freed by kfree() on failure to register the device or bus.
+
+Fixes: 82242d28ff8b ("MIPS: IP22: Add missing put_device call")
+Cc: stable@vger.kernel.org     # 3.17
+Cc: Levente Kurusa <levex@linux.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/mips/sgi-ip22/ip22-gio.c |    1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/arch/mips/sgi-ip22/ip22-gio.c
++++ b/arch/mips/sgi-ip22/ip22-gio.c
+@@ -30,7 +30,6 @@ static struct {
+ static void gio_bus_release(struct device *dev)
+ {
+-      kfree(dev);
+ }
+ static struct device gio_bus = {
diff --git a/queue-7.1/mips-sched-fix-cpumask_offstack-memory-corruption.patch b/queue-7.1/mips-sched-fix-cpumask_offstack-memory-corruption.patch
new file mode 100644 (file)
index 0000000..2bef3cc
--- /dev/null
@@ -0,0 +1,94 @@
+From 98e37db4a34d3af3fb2f4648295c25b5e40b20e3 Mon Sep 17 00:00:00 2001
+From: Aaron Tomlin <atomlin@atomlin.com>
+Date: Tue, 26 May 2026 10:16:51 -0400
+Subject: mips: sched: Fix CPUMASK_OFFSTACK memory corruption
+
+From: Aaron Tomlin <atomlin@atomlin.com>
+
+commit 98e37db4a34d3af3fb2f4648295c25b5e40b20e3 upstream.
+
+This patch addresses a critical memory management flaw. When
+CONFIG_CPUMASK_OFFSTACK is enabled, cpumask_var_t is a pointer.
+Consequently, sizeof(new_mask) evaluates to the pointer size, causing
+copy_from_user() to clobber the mask pointer. Furthermore, the old
+logic performed copy_from_user() before allocating the mask.
+
+Fix this by allocating new_mask first. To handle variable-sized user
+masks correctly, use cpumask_size() to truncate overly large user masks
+or pad undersized masks with zeros before copying the data directly into
+the allocated buffer.
+
+Fixes: 295cbf6d63165 ("[MIPS] Move FPU affinity code into separate file.")
+Cc: stable@vger.kernel.org
+Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/mips/kernel/mips-mt-fpaff.c |   28 +++++++++++++++-------------
+ 1 file changed, 15 insertions(+), 13 deletions(-)
+
+--- a/arch/mips/kernel/mips-mt-fpaff.c
++++ b/arch/mips/kernel/mips-mt-fpaff.c
+@@ -71,11 +71,16 @@ asmlinkage long mipsmt_sys_sched_setaffi
+       struct task_struct *p;
+       int retval;
+-      if (len < sizeof(new_mask))
+-              return -EINVAL;
+-
+-      if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask)))
+-              return -EFAULT;
++      if (!alloc_cpumask_var(&new_mask, GFP_KERNEL))
++              return -ENOMEM;
++      if (len < cpumask_size())
++              cpumask_clear(new_mask);
++      else if (len > cpumask_size())
++              len = cpumask_size();
++      if (copy_from_user(new_mask, user_mask_ptr, len)) {
++              retval = -EFAULT;
++              goto out_free_new_mask;
++      }
+       cpus_read_lock();
+       rcu_read_lock();
+@@ -84,7 +89,8 @@ asmlinkage long mipsmt_sys_sched_setaffi
+       if (!p) {
+               rcu_read_unlock();
+               cpus_read_unlock();
+-              return -ESRCH;
++              retval = -ESRCH;
++              goto out_free_new_mask;
+       }
+       /* Prevent p going away */
+@@ -95,13 +101,9 @@ asmlinkage long mipsmt_sys_sched_setaffi
+               retval = -ENOMEM;
+               goto out_put_task;
+       }
+-      if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) {
+-              retval = -ENOMEM;
+-              goto out_free_cpus_allowed;
+-      }
+       if (!alloc_cpumask_var(&effective_mask, GFP_KERNEL)) {
+               retval = -ENOMEM;
+-              goto out_free_new_mask;
++              goto out_free_cpus_allowed;
+       }
+       if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) {
+               retval = -EPERM;
+@@ -142,13 +144,13 @@ asmlinkage long mipsmt_sys_sched_setaffi
+       }
+ out_unlock:
+       free_cpumask_var(effective_mask);
+-out_free_new_mask:
+-      free_cpumask_var(new_mask);
+ out_free_cpus_allowed:
+       free_cpumask_var(cpus_allowed);
+ out_put_task:
+       put_task_struct(p);
+       cpus_read_unlock();
++out_free_new_mask:
++      free_cpumask_var(new_mask);
+       return retval;
+ }
diff --git a/queue-7.1/mm-damon-core-always-put-unsuccessfully-committed-target-pids.patch b/queue-7.1/mm-damon-core-always-put-unsuccessfully-committed-target-pids.patch
new file mode 100644 (file)
index 0000000..7ee373b
--- /dev/null
@@ -0,0 +1,161 @@
+From 6a66c557a2ab2609575bafd15e093669c05f9711 Mon Sep 17 00:00:00 2001
+From: SeongJae Park <sj@kernel.org>
+Date: Thu, 4 Jun 2026 18:38:48 -0700
+Subject: mm/damon/core: always put unsuccessfully committed target pids
+
+From: SeongJae Park <sj@kernel.org>
+
+commit 6a66c557a2ab2609575bafd15e093669c05f9711 upstream.
+
+damon_commit_target() puts and gets the destination and the source target
+pids.  It puts the destination target pid because it will be overwritten
+by the source target pid.  It gets the source pid because the caller is
+supposed to eventually put the pids.  In more detail, the caller will call
+damon_destroy_ctx() after damon_commit_ctx() to destroy the entire source
+context.  And in this case, [f]vaddr operation set's cleanup_target()
+callback will put the pids.
+
+The commit operation is made at the context level.  The operation can fail
+in multiple places including in the middle and after the targets commit
+operations.  For any such failures, immediately the error is returned to
+the damon_commit_ctx() caller.  If some or all of the source target pids
+were committed to the destination during the unsuccessful context commit
+attempt, those pids should be put twice.
+
+The source context will do the put operations using the above explained
+routine.  However, let's suppose the destination context was not
+originally using [f]vaddr operation set and the commit failed before the
+ops of the source context is committed.  The destination does not have the
+cleanup_target() ops callback, so it cannot put the pids via the
+damon_destroy_ctx().
+
+As a result, the pids are leaked.  The issue in the real world would be
+not very common.  The commit feature is for changing parameters of running
+DAMON context while inheriting internal status like the monitoring
+results.  The monitoring results of a physical address range ain't have
+things that are beneficial to be inherited to a virtual address ranges
+monitoring.  So the problem-causing DAMON control would be not very common
+in the real world.  That said, it is a supported feature.  And
+damon_commit_target() failure due to memory allocation is relatively
+realistic [1] if there are a huge number of target regions.
+
+Fix by putting the pids in the commit operation in case of the failures.
+
+The issue was discovered [2] by Sashiko.
+
+Link: https://lore.kernel.org/20260605013849.83750-1-sj@kernel.org
+Link: https://lore.kernel.org/20260603112306.58490-1-akinobu.mita@gmail.com [1]
+Link: https://lore.kernel.org/20260320020056.835-1-sj@kernel.org [2]
+Fixes: 83dc7bbaecae ("mm/damon/sysfs: use damon_commit_ctx()")
+Signed-off-by: SeongJae Park <sj@kernel.org>
+Cc: <stable@vger.kernel.org> # 6.11.x
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/damon/core.c |   55 +++++++++++++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 47 insertions(+), 8 deletions(-)
+
+--- a/mm/damon/core.c
++++ b/mm/damon/core.c
+@@ -1257,10 +1257,36 @@ static int damon_commit_target(
+       return 0;
+ }
++/*
++ * damon_revert_target_commits() - revert unsuccessful target commits.
++ * @dst:      Commit destination context
++ * @failed:   Commit failed destination target
++ * @src:      Commit source context
++ *
++ * Revert target states that changed by damon_commit_target(), and cannot be
++ * cleaned up by the destination context's ops.cleanup_target().
++ */
++static void damon_revert_target_commits(struct damon_ctx *dst,
++              struct damon_target *failed, struct damon_ctx *src)
++{
++      struct damon_target *target;
++
++      if (!damon_target_has_pid(src))
++              return;
++      if (dst->ops.cleanup_target)
++              return;
++      damon_for_each_target(target, dst) {
++              if (target == failed)
++                      return;
++              put_pid(target->pid);
++      }
++}
++
+ static int damon_commit_targets(
+               struct damon_ctx *dst, struct damon_ctx *src)
+ {
+       struct damon_target *dst_target, *next, *src_target, *new_target;
++      struct damon_target *failed;
+       int i = 0, j = 0, err;
+       damon_for_each_target_safe(dst_target, next, dst) {
+@@ -1274,8 +1300,10 @@ static int damon_commit_targets(
+                                       dst_target, damon_target_has_pid(dst),
+                                       src_target, damon_target_has_pid(src),
+                                       src->min_region_sz);
+-                      if (err)
+-                              return err;
++                      if (err) {
++                              failed = dst_target;
++                              goto out;
++                      }
+               } else {
+                       struct damos *s;
+@@ -1289,25 +1317,34 @@ static int damon_commit_targets(
+               }
+       }
++      failed = NULL;
+       damon_for_each_target_safe(src_target, next, src) {
+               if (j++ < i)
+                       continue;
+               /* target to remove has no matching dst */
+-              if (src_target->obsolete)
+-                      return -EINVAL;
++              if (src_target->obsolete) {
++                      err = -EINVAL;
++                      goto out;
++              }
+               new_target = damon_new_target();
+-              if (!new_target)
+-                      return -ENOMEM;
++              if (!new_target) {
++                      err = -ENOMEM;
++                      goto out;
++              }
+               err = damon_commit_target(new_target, false,
+                               src_target, damon_target_has_pid(src),
+                               src->min_region_sz);
+               if (err) {
+                       damon_destroy_target(new_target, NULL);
+-                      return err;
++                      goto out;
+               }
+               damon_add_target(dst, new_target);
+       }
+       return 0;
++
++out:
++      damon_revert_target_commits(dst, failed, src);
++      return err;
+ }
+ /**
+@@ -1346,8 +1383,10 @@ int damon_commit_ctx(struct damon_ctx *d
+        */
+       if (!damon_attrs_equals(&dst->attrs, &src->attrs)) {
+               err = damon_set_attrs(dst, &src->attrs);
+-              if (err)
++              if (err) {
++                      damon_revert_target_commits(dst, NULL, src);
+                       return err;
++              }
+       }
+       dst->ops = src->ops;
+       dst->addr_unit = src->addr_unit;
diff --git a/queue-7.1/mm-damon-core-make-charge_addr_from-aware-of-end-address-exclusivity.patch b/queue-7.1/mm-damon-core-make-charge_addr_from-aware-of-end-address-exclusivity.patch
new file mode 100644 (file)
index 0000000..c1f7ab5
--- /dev/null
@@ -0,0 +1,40 @@
+From 5a2d162e22bf33eb89d53e802d0fc1ec422e19b6 Mon Sep 17 00:00:00 2001
+From: SeongJae Park <sj@kernel.org>
+Date: Mon, 27 Apr 2026 21:29:40 -0700
+Subject: mm/damon/core: make charge_addr_from aware of end-address exclusivity
+
+From: SeongJae Park <sj@kernel.org>
+
+commit 5a2d162e22bf33eb89d53e802d0fc1ec422e19b6 upstream.
+
+DAMON region end address is exclusive one, but charge_addr_from is
+assigned assuming the end address is inclusive.  As a result, DAMOS action
+to next up to min_region_sz memory can be skipped.  This is quite
+negligible user impact.  But, the bug is a bug that can be very simply
+fixed.  Fix the wrong assignment to respect the exclusiveness of the
+address.
+
+The issue was discovered [1] by Sashiko.
+
+Link: https://lore.kernel.org/20260428042942.118230-1-sj@kernel.org
+Link: https://lore.kernel.org/20260428032324.115663-1-sj@kernel.org [1]
+Fixes: 50585192bc2e ("mm/damon/schemes: skip already charged targets and regions")
+Signed-off-by: SeongJae Park <sj@kernel.org>
+Cc: <stable@vger.kernel.org> # 5.16.x
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/damon/core.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/mm/damon/core.c
++++ b/mm/damon/core.c
+@@ -2106,7 +2106,7 @@ static void damos_apply_scheme(struct da
+               if (damos_quota_is_set(quota) &&
+                               quota->charged_sz >= quota->esz) {
+                       quota->charge_target_from = t;
+-                      quota->charge_addr_from = r->ar.end + 1;
++                      quota->charge_addr_from = r->ar.end;
+               }
+       }
+       if (s->action != DAMOS_STAT)
diff --git a/queue-7.1/mm-damon-sysfs-schemes-fix-dir-put-orders-in-access_pattern_add_dirs.patch b/queue-7.1/mm-damon-sysfs-schemes-fix-dir-put-orders-in-access_pattern_add_dirs.patch
new file mode 100644 (file)
index 0000000..dc4c252
--- /dev/null
@@ -0,0 +1,67 @@
+From d58fdbe37a829fd2e5803dd4e5a72992dd8c5368 Mon Sep 17 00:00:00 2001
+From: SeongJae Park <sj@kernel.org>
+Date: Wed, 17 Jun 2026 17:56:47 -0700
+Subject: mm/damon/sysfs-schemes: fix dir put orders in access_pattern_add_dirs()
+
+From: SeongJae Park <sj@kernel.org>
+
+commit d58fdbe37a829fd2e5803dd4e5a72992dd8c5368 upstream.
+
+Patch series "mm/damon/sysfs-schemes: fix wrong directories put orders in
+error paths".
+
+Error paths of damon_sysfs_access_pattern_add_dirs() and
+damon_sysfs_scheme_add_dirs() functions put references to directories in
+wrong orders.  As a result, uninitialized memory dereference and/or
+memory leak can happen.  Fix those.
+
+
+This patch (of 2):
+
+In access_pattern_add_dirs(), error handling path puts references starting
+from setup failed directories.  If the failure happpened from the initial
+allication in the setup functions, uninitialized memory dereference
+happen.  The allocation failures will not commonly happen, but the
+consequence is quite bad.  Fix the wrong reference put orders.
+
+The issue was discovered [1] by Sashiko.
+
+Link: https://lore.kernel.org/20260618005650.83868-2-sj@kernel.org
+Link: https://lore.kernel.org/20260617060005.86852-1-sj@kernel.org [1]
+Fixes: 7e84b1f8212a ("mm/damon/sysfs: support DAMON-based Operation Schemes")
+Signed-off-by: SeongJae Park <sj@kernel.org>
+Cc: <stable@vger.kernel.org> # 5.18.x
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/damon/sysfs-schemes.c |    9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+--- a/mm/damon/sysfs-schemes.c
++++ b/mm/damon/sysfs-schemes.c
+@@ -1767,22 +1767,19 @@ static int damon_sysfs_access_pattern_ad
+       err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
+                       &access_pattern->sz, "sz");
+       if (err)
+-              goto put_sz_out;
++              return err;
+       err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
+                       &access_pattern->nr_accesses, "nr_accesses");
+       if (err)
+-              goto put_nr_accesses_sz_out;
++              goto put_sz_out;
+       err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
+                       &access_pattern->age, "age");
+       if (err)
+-              goto put_age_nr_accesses_sz_out;
++              goto put_nr_accesses_sz_out;
+       return 0;
+-put_age_nr_accesses_sz_out:
+-      kobject_put(&access_pattern->age->kobj);
+-      access_pattern->age = NULL;
+ put_nr_accesses_sz_out:
+       kobject_put(&access_pattern->nr_accesses->kobj);
+       access_pattern->nr_accesses = NULL;
diff --git a/queue-7.1/mm-damon-sysfs-schemes-put-stats-for-scheme_add_dirs-internal-error.patch b/queue-7.1/mm-damon-sysfs-schemes-put-stats-for-scheme_add_dirs-internal-error.patch
new file mode 100644 (file)
index 0000000..9f05805
--- /dev/null
@@ -0,0 +1,59 @@
+From 05ea83ee88ca70f8932906d9f2617ff996f45b50 Mon Sep 17 00:00:00 2001
+From: SeongJae Park <sj@kernel.org>
+Date: Wed, 17 Jun 2026 17:56:48 -0700
+Subject: mm/damon/sysfs-schemes: put stats for scheme_add_dirs() internal error
+
+From: SeongJae Park <sj@kernel.org>
+
+commit 05ea83ee88ca70f8932906d9f2617ff996f45b50 upstream.
+
+damon_sysfs_scheme_add_dirs() setup the tried_regions directory after the
+stats directory setup is completed.  When the tried_regions directory
+setup is failed, the setup function ensures the reference for the tried
+regions directory is released.  Hence the error path should put references
+on setup succeeded directory objects, starting from the stats directory.
+However, the error path is putting the tried_regions directory instead of
+the stats directory.
+
+As a direct result, the stats directory object is leaked.  Worse yet, if
+the tried_regions directory setup failed from the initial allocation, the
+scheme->tried_regions field remains uninitialized.  The following
+kobject_put(&scheme->tried_regions->kobj) call in the error path will
+dereference the uninitialized memory.  The setup failures should not be
+common.  But once it happens, the consequence is quite bad.
+
+Fix this issue by correctly putting the stats directory instead of the
+tried_regions directory.
+
+The issue was discovered [1] by Sashiko.
+
+Link: https://lore.kernel.org/20260618005650.83868-3-sj@kernel.org
+Link: https://lore.kernel.org/20260617005223.96813-1-sj@kernel.org [1]
+Fixes: 5181b75f438d ("mm/damon/sysfs-schemes: implement schemes/tried_regions directory")
+Signed-off-by: SeongJae Park <sj@kernel.org>
+Cc: <stable@vger.kernel.org> # 6.2.x
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/damon/sysfs-schemes.c |    8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/mm/damon/sysfs-schemes.c
++++ b/mm/damon/sysfs-schemes.c
+@@ -2283,12 +2283,12 @@ static int damon_sysfs_scheme_add_dirs(s
+               goto put_filters_watermarks_quotas_access_pattern_out;
+       err = damon_sysfs_scheme_set_tried_regions(scheme);
+       if (err)
+-              goto put_tried_regions_out;
++              goto put_stats_out;
+       return 0;
+-put_tried_regions_out:
+-      kobject_put(&scheme->tried_regions->kobj);
+-      scheme->tried_regions = NULL;
++put_stats_out:
++      kobject_put(&scheme->stats->kobj);
++      scheme->stats = NULL;
+ put_filters_watermarks_quotas_access_pattern_out:
+       kobject_put(&scheme->ops_filters->kobj);
+       scheme->ops_filters = NULL;
diff --git a/queue-7.1/mm-huge_memory-preserve-pmd_swp_uffd_wp-on-device-private-pmd-downgrade.patch b/queue-7.1/mm-huge_memory-preserve-pmd_swp_uffd_wp-on-device-private-pmd-downgrade.patch
new file mode 100644 (file)
index 0000000..e9f525d
--- /dev/null
@@ -0,0 +1,46 @@
+From f7e2c21bd1f57cd5350eecdfdb5d6025ca6afbab Mon Sep 17 00:00:00 2001
+From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
+Date: Fri, 29 May 2026 18:23:28 +0100
+Subject: mm/huge_memory: preserve pmd_swp_uffd_wp on device-private PMD downgrade
+
+From: Kiryl Shutsemau (Meta) <kas@kernel.org>
+
+commit f7e2c21bd1f57cd5350eecdfdb5d6025ca6afbab upstream.
+
+change_non_present_huge_pmd() rewrites a writable device-private PMD swap
+entry into a readable one without carrying pmd_swp_uffd_wp() across.  The
+PTE-level change_softleaf_pte() does this correctly; mirror that here,
+matching what copy_huge_pmd() does for the fork path.  Without the carry,
+a plain mprotect() over a UFFD_WP-marked device-private THP strips the bit
+and the trap is bypassed on swap-in.
+
+Link: https://lore.kernel.org/20260529172331.356655-5-kas@kernel.org
+Fixes: 368076f52ebe ("mm/huge_memory: add device-private THP support to PMD operations")
+Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
+Reported-by: Sashiko AI review <sashiko-bot@kernel.org>
+Reviewed-by: Balbir Singh <balbirs@nvidia.com>
+Cc: David Hildenbrand <david@kernel.org>
+Cc: Lorenzo Stoakes <ljs@kernel.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Peter Xu <peterx@redhat.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/huge_memory.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/mm/huge_memory.c
++++ b/mm/huge_memory.c
+@@ -2638,6 +2638,8 @@ static void change_non_present_huge_pmd(
+       } else if (softleaf_is_device_private_write(entry)) {
+               entry = make_readable_device_private_entry(swp_offset(entry));
+               newpmd = swp_entry_to_pmd(entry);
++              if (pmd_swp_uffd_wp(*pmd))
++                      newpmd = pmd_swp_mkuffd_wp(newpmd);
+       } else {
+               newpmd = *pmd;
+       }
diff --git a/queue-7.1/mm-hugetlb-fix-hugetlb-cgroup-rsvd-charge-uncharge-mismatch.patch b/queue-7.1/mm-hugetlb-fix-hugetlb-cgroup-rsvd-charge-uncharge-mismatch.patch
new file mode 100644 (file)
index 0000000..77d83c2
--- /dev/null
@@ -0,0 +1,84 @@
+From 15807d0ddde37407af72859426b654f3d1972b00 Mon Sep 17 00:00:00 2001
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+Date: Sat, 28 Mar 2026 12:25:34 +0530
+Subject: mm/hugetlb: fix hugetlb cgroup rsvd charge/uncharge mismatch
+
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+
+commit 15807d0ddde37407af72859426b654f3d1972b00 upstream.
+
+In alloc_hugetlb_folio(), a single h_cg pointer is used for both the rsvd
+and non-rsvd hugetlb cgroup charges.  When map_chg is set,
+hugetlb_cgroup_charge_cgroup_rsvd() stores the charged cgroup in h_cg, but
+the immediately following hugetlb_cgroup_charge_cgroup() overwrites h_cg
+with the non-rsvd cgroup pointer.
+
+As a result, hugetlb_cgroup_commit_charge_rsvd() stores the wrong
+(non-rsvd) cgroup pointer into the folio's rsvd slot.
+
+When the folio is later freed, free_huge_folio() unconditionally calls
+both hugetlb_cgroup_uncharge_folio() and
+hugetlb_cgroup_uncharge_folio_rsvd().  The rsvd uncharge reads back the
+wrong cgroup from the folio and decrements a counter that was never
+charged for that cgroup, causing a page_counter underflow:
+
+  page_counter underflow: -512 nr_pages=512
+  WARNING: mm/page_counter.c:61 at page_counter_cancel
+
+Fix this by introducing a separate h_cg_rsvd pointer exclusively for the
+rsvd charge path, keeping the rsvd and non-rsvd charges fully independent
+through their charge, commit, and error uncharge paths.
+
+Link: https://lore.kernel.org/20260328065534.346053-1-kartikey406@gmail.com
+Fixes: 08cf9faf7558 ("hugetlb_cgroup: support noreserve mappings")
+Reported-by: syzbot+226c1f947186f8fef796@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=226c1f947186f8fef796
+Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
+Reviewed-by: Muchun Song <muchun.song@linux.dev>
+Cc: David Hildenbrand <david@kernel.org>
+Cc: Oscar Salvador <osalvador@suse.de>
+Cc: Mina Almasry <almasrymina@google.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/hugetlb.c |    7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/mm/hugetlb.c
++++ b/mm/hugetlb.c
+@@ -2862,6 +2862,7 @@ struct folio *alloc_hugetlb_folio(struct
+       map_chg_state map_chg;
+       int ret, idx;
+       struct hugetlb_cgroup *h_cg = NULL;
++      struct hugetlb_cgroup *h_cg_rsvd = NULL;
+       gfp_t gfp = htlb_alloc_mask(h) | __GFP_RETRY_MAYFAIL;
+       idx = hstate_index(h);
+@@ -2912,7 +2913,7 @@ struct folio *alloc_hugetlb_folio(struct
+        */
+       if (map_chg) {
+               ret = hugetlb_cgroup_charge_cgroup_rsvd(
+-                      idx, pages_per_huge_page(h), &h_cg);
++                      idx, pages_per_huge_page(h), &h_cg_rsvd);
+               if (ret)
+                       goto out_subpool_put;
+       }
+@@ -2954,7 +2955,7 @@ struct folio *alloc_hugetlb_folio(struct
+        */
+       if (map_chg) {
+               hugetlb_cgroup_commit_charge_rsvd(idx, pages_per_huge_page(h),
+-                                                h_cg, folio);
++                                                h_cg_rsvd, folio);
+       }
+       spin_unlock_irq(&hugetlb_lock);
+@@ -3006,7 +3007,7 @@ out_uncharge_cgroup:
+ out_uncharge_cgroup_reservation:
+       if (map_chg)
+               hugetlb_cgroup_uncharge_cgroup_rsvd(idx, pages_per_huge_page(h),
+-                                                  h_cg);
++                                                  h_cg_rsvd);
+ out_subpool_put:
+       /*
+        * put page to subpool iff the quota of subpool's rsv_hpages is used
diff --git a/queue-7.1/mm-memory_hotplug-fix-incorrect-altmap-passing-in-error-path.patch b/queue-7.1/mm-memory_hotplug-fix-incorrect-altmap-passing-in-error-path.patch
new file mode 100644 (file)
index 0000000..6f9bc72
--- /dev/null
@@ -0,0 +1,61 @@
+From 2fac4afa0e2e68841334c78c1821e49f74fbc66a Mon Sep 17 00:00:00 2001
+From: Muchun Song <songmuchun@bytedance.com>
+Date: Tue, 28 Apr 2026 16:18:51 +0800
+Subject: mm/memory_hotplug: fix incorrect altmap passing in error path
+
+From: Muchun Song <songmuchun@bytedance.com>
+
+commit 2fac4afa0e2e68841334c78c1821e49f74fbc66a upstream.
+
+In create_altmaps_and_memory_blocks(), when arch_add_memory() succeeds
+with memmap_on_memory enabled, the vmemmap pages are allocated from
+params.altmap.  If create_memory_block_devices() subsequently fails, the
+error path calls arch_remove_memory() with a NULL altmap instead of
+params.altmap.
+
+This is a bug that could lead to memory corruption.  Since altmap is NULL,
+vmemmap_free() falls back to freeing the vmemmap pages into the system
+buddy allocator via free_pages() instead of the altmap.
+arch_remove_memory() then immediately destroys the physical linear mapping
+for this memory.  This injects unowned pages into the buddy allocator,
+causing machine checks or memory corruption if the system later attempts
+to allocate and use those freed pages.
+
+Fix this by passing params.altmap to arch_remove_memory() in the error
+path.
+
+Link: https://lore.kernel.org/20260428081855.1249045-3-songmuchun@bytedance.com
+Fixes: 6b8f0798b85a ("mm/memory_hotplug: split memmap_on_memory requests across memblocks")
+Signed-off-by: Muchun Song <songmuchun@bytedance.com>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Acked-by: Liam R. Howlett <liam@infradead.org>
+Reviewed-by: Georgi Djakov <georgi.djakov@oss.qualcomm.com>
+Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
+Cc: Joao Martins <joao.m.martins@oracle.com>
+Cc: Lorenzo Stoakes <ljs@kernel.org>
+Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
+Cc: Michael Ellerman <mpe@ellerman.id.au>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Cc: Nicholas Piggin <npiggin@gmail.com>
+Cc: Oscar Salvador <osalvador@suse.de>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/memory_hotplug.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/mm/memory_hotplug.c
++++ b/mm/memory_hotplug.c
+@@ -1470,7 +1470,7 @@ static int create_altmaps_and_memory_blo
+               ret = create_memory_block_devices(cur_start, memblock_size, nid,
+                                                 params.altmap, group);
+               if (ret) {
+-                      arch_remove_memory(cur_start, memblock_size, NULL);
++                      arch_remove_memory(cur_start, memblock_size, params.altmap);
+                       kfree(params.altmap);
+                       goto out;
+               }
diff --git a/queue-7.1/mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch b/queue-7.1/mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch
new file mode 100644 (file)
index 0000000..97df433
--- /dev/null
@@ -0,0 +1,111 @@
+From 94405c6136839f7c462249c8b4b957bcb9527a9d Mon Sep 17 00:00:00 2001
+From: Muchun Song <songmuchun@bytedance.com>
+Date: Tue, 28 Apr 2026 16:18:54 +0800
+Subject: mm/mm_init: fix pageblock migratetype for ZONE_DEVICE compound pages
+
+From: Muchun Song <songmuchun@bytedance.com>
+
+commit 94405c6136839f7c462249c8b4b957bcb9527a9d upstream.
+
+The memmap_init_zone_device() function only initializes the migratetype of
+the first pageblock of a compound page.  If the compound page size exceeds
+pageblock_nr_pages (e.g., 1GB hugepages with 2MB pageblocks), subsequent
+pageblocks in the compound page remain uninitialized.
+
+Move the migratetype initialization out of __init_zone_device_page() and
+into a separate pageblock_migratetype_init_range() function.  This
+iterates over the entire PFN range of the memory, ensuring that all
+pageblocks are correctly initialized.
+
+Also remove the stale confusing comment about MEMINIT_HOTPLUG above the
+migratetype setting since it is an obsolete relic from commit 966cf44f637e
+("mm: defer ZONE_DEVICE page initialization to the point where we init
+pgmap") and no longer makes sense here.
+
+Link: https://lore.kernel.org/20260428081855.1249045-6-songmuchun@bytedance.com
+Fixes: c4386bd8ee3a ("mm/memremap: add ZONE_DEVICE support for compound pages")
+Signed-off-by: Muchun Song <songmuchun@bytedance.com>
+Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Reviewed-by: Oscar Salvador <osalvador@suse.de>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Acked-by: Liam R. Howlett <liam@infradead.org>
+Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
+Cc: Joao Martins <joao.m.martins@oracle.com>
+Cc: Lorenzo Stoakes <ljs@kernel.org>
+Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
+Cc: Michael Ellerman <mpe@ellerman.id.au>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Nicholas Piggin <npiggin@gmail.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/mm_init.c |   34 +++++++++++++++++++---------------
+ 1 file changed, 19 insertions(+), 15 deletions(-)
+
+--- a/mm/mm_init.c
++++ b/mm/mm_init.c
+@@ -674,6 +674,20 @@ static inline void fixup_hashdist(void)
+ static inline void fixup_hashdist(void) {}
+ #endif /* CONFIG_NUMA */
++#ifdef CONFIG_ZONE_DEVICE
++static __meminit void pageblock_migratetype_init_range(unsigned long pfn,
++              unsigned long nr_pages, int migratetype)
++{
++      const unsigned long end = pfn + nr_pages;
++
++      for (pfn = pageblock_align(pfn); pfn < end; pfn += pageblock_nr_pages) {
++              init_pageblock_migratetype(pfn_to_page(pfn), migratetype, false);
++              if (IS_ALIGNED(pfn, PAGES_PER_SECTION))
++                      cond_resched();
++      }
++}
++#endif
++
+ /*
+  * Initialize a reserved page unconditionally, finding its zone first.
+  */
+@@ -1012,21 +1026,6 @@ static void __ref __init_zone_device_pag
+       page->zone_device_data = NULL;
+       /*
+-       * Mark the block movable so that blocks are reserved for
+-       * movable at startup. This will force kernel allocations
+-       * to reserve their blocks rather than leaking throughout
+-       * the address space during boot when many long-lived
+-       * kernel allocations are made.
+-       *
+-       * Please note that MEMINIT_HOTPLUG path doesn't clear memmap
+-       * because this is done early in section_activate()
+-       */
+-      if (pageblock_aligned(pfn)) {
+-              init_pageblock_migratetype(page, MIGRATE_MOVABLE, false);
+-              cond_resched();
+-      }
+-
+-      /*
+        * ZONE_DEVICE pages other than MEMORY_TYPE_GENERIC are released
+        * directly to the driver page allocator which will set the page count
+        * to 1 when allocating the page.
+@@ -1122,6 +1121,9 @@ void __ref memmap_init_zone_device(struc
+               __init_zone_device_page(page, pfn, zone_idx, nid, pgmap);
++              if (IS_ALIGNED(pfn, PAGES_PER_SECTION))
++                      cond_resched();
++
+               if (pfns_per_compound == 1)
+                       continue;
+@@ -1129,6 +1131,8 @@ void __ref memmap_init_zone_device(struc
+                                    compound_nr_pages(altmap, pgmap));
+       }
++      pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE);
++
+       pr_debug("%s initialised %lu pages in %ums\n", __func__,
+               nr_pages, jiffies_to_msecs(jiffies - start));
+ }
diff --git a/queue-7.1/mm-mm_init-fix-uninitialized-struct-pages-for-zone_device.patch b/queue-7.1/mm-mm_init-fix-uninitialized-struct-pages-for-zone_device.patch
new file mode 100644 (file)
index 0000000..24b3245
--- /dev/null
@@ -0,0 +1,73 @@
+From cd681403a87085562499d60325b7b45d3be11217 Mon Sep 17 00:00:00 2001
+From: Muchun Song <songmuchun@bytedance.com>
+Date: Tue, 28 Apr 2026 16:18:55 +0800
+Subject: mm/mm_init: fix uninitialized struct pages for ZONE_DEVICE
+
+From: Muchun Song <songmuchun@bytedance.com>
+
+commit cd681403a87085562499d60325b7b45d3be11217 upstream.
+
+If DAX memory is hotplugged into an unoccupied subsection of an early
+section, section_activate() reuses the unoptimized boot memmap.  However,
+compound_nr_pages() still assumes that vmemmap optimization is in effect
+and initializes only the reduced number of struct pages.  As a result, the
+remaining tail struct pages are left uninitialized, which can later lead
+to unexpected behavior or crashes.
+
+Fix this by treating early sections as unoptimized when calculating how
+many struct pages to initialize.
+
+Link: https://lore.kernel.org/20260428081855.1249045-7-songmuchun@bytedance.com
+Fixes: 6fd3620b3428 ("mm/page_alloc: reuse tail struct pages for compound devmaps")
+Signed-off-by: Muchun Song <songmuchun@bytedance.com>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Acked-by: Liam R. Howlett <liam@infradead.org>
+Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
+Cc: Joao Martins <joao.m.martins@oracle.com>
+Cc: Lorenzo Stoakes <ljs@kernel.org>
+Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
+Cc: Michael Ellerman <mpe@ellerman.id.au>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Nicholas Piggin <npiggin@gmail.com>
+Cc: Oscar Salvador <osalvador@suse.de>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/mm_init.c |   13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+--- a/mm/mm_init.c
++++ b/mm/mm_init.c
+@@ -1055,10 +1055,17 @@ static void __ref __init_zone_device_pag
+  * of how the sparse_vmemmap internals handle compound pages in the lack
+  * of an altmap. See vmemmap_populate_compound_pages().
+  */
+-static inline unsigned long compound_nr_pages(struct vmem_altmap *altmap,
++static inline unsigned long compound_nr_pages(unsigned long pfn,
++                                            struct vmem_altmap *altmap,
+                                             struct dev_pagemap *pgmap)
+ {
+-      if (!vmemmap_can_optimize(altmap, pgmap))
++      /*
++       * If DAX memory is hot-plugged into an unoccupied subsection
++       * of an early section, the unoptimized boot memmap is reused.
++       * See section_activate().
++       */
++      if (early_section(__pfn_to_section(pfn)) ||
++          !vmemmap_can_optimize(altmap, pgmap))
+               return pgmap_vmemmap_nr(pgmap);
+       return VMEMMAP_RESERVE_NR * (PAGE_SIZE / sizeof(struct page));
+@@ -1128,7 +1135,7 @@ void __ref memmap_init_zone_device(struc
+                       continue;
+               memmap_init_compound(page, pfn, zone_idx, nid, pgmap,
+-                                   compound_nr_pages(altmap, pgmap));
++                                   compound_nr_pages(pfn, altmap, pgmap));
+       }
+       pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE);
diff --git a/queue-7.1/mm-sparse-vmemmap-fix-vmemmap-accounting-underflow.patch b/queue-7.1/mm-sparse-vmemmap-fix-vmemmap-accounting-underflow.patch
new file mode 100644 (file)
index 0000000..a31272c
--- /dev/null
@@ -0,0 +1,137 @@
+From c373f7f98e6ad591c85d40548cf8b6443be69311 Mon Sep 17 00:00:00 2001
+From: Muchun Song <songmuchun@bytedance.com>
+Date: Tue, 28 Apr 2026 16:18:50 +0800
+Subject: mm/sparse-vmemmap: fix vmemmap accounting underflow
+
+From: Muchun Song <songmuchun@bytedance.com>
+
+commit c373f7f98e6ad591c85d40548cf8b6443be69311 upstream.
+
+Patch series "mm: Fix vmemmap optimization accounting and initialization",
+v8.
+
+The series fixes several bugs in vmemmap optimization, mainly around
+incorrect page accounting and memmap initialization in DAX and memory
+hotplug paths.  It also fixes pageblock migratetype initialization and
+struct page initialization for ZONE_DEVICE compound pages.
+
+Patches 1-4 fix vmemmap accounting issues.  Patch 1 fixes an accounting
+underflow in the section activation failure path by moving vmemmap page
+accounting into the lower-level allocation and freeing helpers.  Patch 2
+fixes incorrect altmap passing in the memory hotplug error path.  Patch 3
+passes pgmap through memory deactivation paths so the teardown side can
+determine whether vmemmap optimization was in effect.  Patch 4 uses that
+information to account the optimized DAX vmemmap size correctly.
+
+Patches 5-6 fix initialization issues in mm/mm_init.  One makes sure all
+pageblocks in ZONE_DEVICE compound pages get their migratetype
+initialized.  The other fixes a case where DAX memory hotplug reuses an
+unoptimized early-section memmap while compound_nr_pages() still assumes
+vmemmap optimization, leaving tail struct pages uninitialized.
+
+
+This patch (of 6):
+
+In section_activate(), if populate_section_memmap() fails, the error
+handling path calls section_deactivate() to roll back the state.  This
+causes a vmemmap accounting imbalance.
+
+Since commit c3576889d87b ("mm: fix accounting of memmap pages"), memmap
+pages are accounted for only after populate_section_memmap() succeeds.
+However, the failure path unconditionally calls section_deactivate(),
+which decreases the vmemmap count.  Consequently, a failure in
+populate_section_memmap() leads to an accounting underflow, incorrectly
+reducing the system's tracked vmemmap usage.
+
+Fix this more thoroughly by moving all accounting calls into the lower
+level functions that actually perform the vmemmap allocation and freeing:
+
+  - populate_section_memmap() accounts for newly allocated vmemmap pages -
+depopulate_section_memmap() unaccounts when vmemmap is freed
+
+This ensures proper accounting in all code paths, including error handling
+and early section cases.
+
+Link: https://lore.kernel.org/20260428081855.1249045-1-songmuchun@bytedance.com
+Link: https://lore.kernel.org/20260428081855.1249045-2-songmuchun@bytedance.com
+Fixes: c3576889d87b ("mm: fix accounting of memmap pages")
+Signed-off-by: Muchun Song <songmuchun@bytedance.com>
+Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
+Acked-by: Oscar Salvador <osalvador@suse.de>
+Acked-by: David Hildenbrand (Arm) <david@kernel.org>
+Acked-by: Liam R. Howlett <liam@infradead.org>
+Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
+Cc: Joao Martins <joao.m.martins@oracle.com>
+Cc: Lorenzo Stoakes <ljs@kernel.org>
+Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
+Cc: Michael Ellerman <mpe@ellerman.id.au>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Nicholas Piggin <npiggin@gmail.com>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@kernel.org>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ mm/sparse-vmemmap.c |   20 ++++++++++++--------
+ 1 file changed, 12 insertions(+), 8 deletions(-)
+
+--- a/mm/sparse-vmemmap.c
++++ b/mm/sparse-vmemmap.c
+@@ -656,7 +656,12 @@ static struct page * __meminit populate_
+               unsigned long nr_pages, int nid, struct vmem_altmap *altmap,
+               struct dev_pagemap *pgmap)
+ {
+-      return __populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap);
++      struct page *page = __populate_section_memmap(pfn, nr_pages, nid, altmap,
++                                                    pgmap);
++
++      memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE));
++
++      return page;
+ }
+ static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages,
+@@ -665,13 +670,17 @@ static void depopulate_section_memmap(un
+       unsigned long start = (unsigned long) pfn_to_page(pfn);
+       unsigned long end = start + nr_pages * sizeof(struct page);
++      memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)));
+       vmemmap_free(start, end, altmap);
+ }
++
+ static void free_map_bootmem(struct page *memmap)
+ {
+       unsigned long start = (unsigned long)memmap;
+       unsigned long end = (unsigned long)(memmap + PAGES_PER_SECTION);
++      memmap_boot_pages_add(-1L * (DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page),
++                                                PAGE_SIZE)));
+       vmemmap_free(start, end, NULL);
+ }
+@@ -774,14 +783,10 @@ static void section_deactivate(unsigned
+        * The memmap of early sections is always fully populated. See
+        * section_activate() and pfn_valid() .
+        */
+-      if (!section_is_early) {
+-              memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)));
++      if (!section_is_early)
+               depopulate_section_memmap(pfn, nr_pages, altmap);
+-      } else if (memmap) {
+-              memmap_boot_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page),
+-                                                        PAGE_SIZE)));
++      else if (memmap)
+               free_map_bootmem(memmap);
+-      }
+       if (empty)
+               ms->section_mem_map = (unsigned long)NULL;
+@@ -826,7 +831,6 @@ static struct page * __meminit section_a
+               section_deactivate(pfn, nr_pages, altmap);
+               return ERR_PTR(-ENOMEM);
+       }
+-      memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE));
+       return memmap;
+ }
diff --git a/queue-7.1/mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch b/queue-7.1/mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch
new file mode 100644 (file)
index 0000000..d230d57
--- /dev/null
@@ -0,0 +1,52 @@
+From 79d1661502c6e4b6f626185cef72cf2fa78116e1 Mon Sep 17 00:00:00 2001
+From: Florian Fuchs <fuchsfl@gmail.com>
+Date: Mon, 18 May 2026 13:45:20 +0200
+Subject: mtd: maps: vmu-flash: fix fault in unaligned fixup
+
+From: Florian Fuchs <fuchsfl@gmail.com>
+
+commit 79d1661502c6e4b6f626185cef72cf2fa78116e1 upstream.
+
+Use kzalloc_obj() / kzalloc_objs() to allocate the memcard structs,
+instead of kmalloc_obj() / kmalloc_objs() to prevent access to
+uninitialized data.
+
+Fixes runtime error: Fault in unaligned fixup: 0000 [#1] at
+mtd_get_fact_prot_info.
+
+Fixes: 47a72688fae7 ("mtd: flash mapping support for Dreamcast VMU.")
+Cc: stable@vger.kernel.org
+Signed-off-by: Florian Fuchs <fuchsfl@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/maps/vmu-flash.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/mtd/maps/vmu-flash.c
++++ b/drivers/mtd/maps/vmu-flash.c
+@@ -609,7 +609,7 @@ static int vmu_connect(struct maple_devi
+       basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
+-      card = kmalloc_obj(struct memcard);
++      card = kzalloc_obj(struct memcard);
+       if (!card) {
+               error = -ENOMEM;
+               goto fail_nomem;
+@@ -627,13 +627,13 @@ static int vmu_connect(struct maple_devi
+       * Not sure there are actually any multi-partition devices in the
+       * real world, but the hardware supports them, so, so will we
+       */
+-      card->parts = kmalloc_objs(struct vmupart, card->partitions);
++      card->parts = kzalloc_objs(struct vmupart, card->partitions);
+       if (!card->parts) {
+               error = -ENOMEM;
+               goto fail_partitions;
+       }
+-      card->mtd = kmalloc_objs(struct mtd_info, card->partitions);
++      card->mtd = kzalloc_objs(struct mtd_info, card->partitions);
+       if (!card->mtd) {
+               error = -ENOMEM;
+               goto fail_mtd_info;
diff --git a/queue-7.1/mtd-rawnand-fix-condition-in-nand_select_target.patch b/queue-7.1/mtd-rawnand-fix-condition-in-nand_select_target.patch
new file mode 100644 (file)
index 0000000..60e4c2f
--- /dev/null
@@ -0,0 +1,31 @@
+From 8507c2cc9e4fa402401819f44d1e8a5ef4d11d8b Mon Sep 17 00:00:00 2001
+From: Arseniy Krasnov <avkrasnov@rulkc.org>
+Date: Tue, 5 May 2026 11:30:30 +0300
+Subject: mtd: rawnand: fix condition in 'nand_select_target()'
+
+From: Arseniy Krasnov <avkrasnov@rulkc.org>
+
+commit 8507c2cc9e4fa402401819f44d1e8a5ef4d11d8b upstream.
+
+'cs' here must be in range [0:nanddev_ntargets[.
+
+Cc: stable@vger.kernel.org
+Fixes: 32813e288414 ("mtd: rawnand: Get rid of chip->numchips")
+Signed-off-by: Arseniy Krasnov <avkrasnov@rulkc.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/nand/raw/nand_base.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/raw/nand_base.c
++++ b/drivers/mtd/nand/raw/nand_base.c
+@@ -175,7 +175,7 @@ void nand_select_target(struct nand_chip
+        * cs should always lie between 0 and nanddev_ntargets(), when that's
+        * not the case it's a bug and the caller should be fixed.
+        */
+-      if (WARN_ON(cs > nanddev_ntargets(&chip->base)))
++      if (WARN_ON(cs >= nanddev_ntargets(&chip->base)))
+               return;
+       chip->cur_cs = cs;
diff --git a/queue-7.1/mtd-rawnand-pl353-fix-probe-resource-allocation.patch b/queue-7.1/mtd-rawnand-pl353-fix-probe-resource-allocation.patch
new file mode 100644 (file)
index 0000000..8a77f8a
--- /dev/null
@@ -0,0 +1,35 @@
+From 19ed11aee966d91beebdef9d32ce926474872f79 Mon Sep 17 00:00:00 2001
+From: Bastien Curutchet <bastien.curutchet@bootlin.com>
+Date: Tue, 26 May 2026 09:10:00 +0200
+Subject: mtd: rawnand: pl353: fix probe resource allocation
+
+From: Bastien Curutchet <bastien.curutchet@bootlin.com>
+
+commit 19ed11aee966d91beebdef9d32ce926474872f79 upstream.
+
+During probe(), the devm_ioremap() is called with the parent device
+instead of the current one. So when the module is unloaded, the register
+area isn't released.
+
+Target the pl35x device in the devm_ioremap() instead of its parent.
+
+Cc: stable@vger.kernel.org
+Fixes: 08d8c62164a3 ("mtd: rawnand: pl353: Add support for the ARM PL353 SMC NAND controller")
+Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/nand/raw/pl35x-nand-controller.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/raw/pl35x-nand-controller.c
++++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c
+@@ -1155,7 +1155,7 @@ static int pl35x_nand_probe(struct platf
+       nfc->controller.ops = &pl35x_nandc_ops;
+       INIT_LIST_HEAD(&nfc->chips);
+-      nfc->conf_regs = devm_ioremap_resource(&smc_amba->dev, &smc_amba->res);
++      nfc->conf_regs = devm_ioremap_resource(nfc->dev, &smc_amba->res);
+       if (IS_ERR(nfc->conf_regs))
+               return PTR_ERR(nfc->conf_regs);
diff --git a/queue-7.1/mtd-slram-remove-failed-entries-from-the-device-list.patch b/queue-7.1/mtd-slram-remove-failed-entries-from-the-device-list.patch
new file mode 100644 (file)
index 0000000..a771909
--- /dev/null
@@ -0,0 +1,85 @@
+From 36f1648644d769c496a8e47e53603e863e358d73 Mon Sep 17 00:00:00 2001
+From: Ruoyu Wang <ruoyuw560@gmail.com>
+Date: Tue, 9 Jun 2026 16:45:27 +0800
+Subject: mtd: slram: remove failed entries from the device list
+
+From: Ruoyu Wang <ruoyuw560@gmail.com>
+
+commit 36f1648644d769c496a8e47e53603e863e358d73 upstream.
+
+register_device() links a new slram_mtdlist entry before allocating all
+of the state needed by the entry. If a later allocation, memremap(), or
+mtd_device_register() fails, the partially initialized entry remains on
+the global list. A later cleanup can then dereference or free invalid
+state from that failed entry.
+
+Unwind the partially initialized entry and clear the list tail on each
+failure path after the entry has been linked.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable@vger.kernel.org
+Signed-off-by: Ruoyu Wang <ruoyuw560@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/devices/slram.c |   22 ++++++++++++++++------
+ 1 file changed, 16 insertions(+), 6 deletions(-)
+
+--- a/drivers/mtd/devices/slram.c
++++ b/drivers/mtd/devices/slram.c
+@@ -129,6 +129,7 @@ static int slram_write(struct mtd_info *
+ static int register_device(char *name, unsigned long start, unsigned long length)
+ {
+       slram_mtd_list_t **curmtd;
++      int ret = -ENOMEM;
+       curmtd = &slram_mtdlist;
+       while (*curmtd) {
+@@ -155,14 +156,15 @@ static int register_device(char *name, u
+       if (!(*curmtd)->mtdinfo) {
+               E("slram: Cannot allocate new MTD device.\n");
+-              return(-ENOMEM);
++              goto err_free_list;
+       }
+       if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start =
+               memremap(start, length,
+                        MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) {
+               E("slram: memremap failed\n");
+-              return -EIO;
++              ret = -EIO;
++              goto err_free_priv;
+       }
+       ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end =
+               ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length;
+@@ -183,10 +185,8 @@ static int register_device(char *name, u
+       if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0))   {
+               E("slram: Failed to register new device\n");
+-              memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start);
+-              kfree((*curmtd)->mtdinfo->priv);
+-              kfree((*curmtd)->mtdinfo);
+-              return(-EAGAIN);
++              ret = -EAGAIN;
++              goto err_unmap;
+       }
+       T("slram: Registered device %s from %luKiB to %luKiB\n", name,
+                       (start / 1024), ((start + length) / 1024));
+@@ -194,6 +194,16 @@ static int register_device(char *name, u
+                       ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start,
+                       ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end);
+       return(0);
++
++err_unmap:
++      memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start);
++err_free_priv:
++      kfree((*curmtd)->mtdinfo->priv);
++err_free_list:
++      kfree((*curmtd)->mtdinfo);
++      kfree(*curmtd);
++      *curmtd = NULL;
++      return ret;
+ }
+ static void unregister_devices(void)
diff --git a/queue-7.1/net-9p-fix-infinite-loop-in-p9_client_rpc-on-fatal-signal.patch b/queue-7.1/net-9p-fix-infinite-loop-in-p9_client_rpc-on-fatal-signal.patch
new file mode 100644 (file)
index 0000000..57eb6b1
--- /dev/null
@@ -0,0 +1,101 @@
+From 6b4f48728faa8bb514368f7eacda05565dea8696 Mon Sep 17 00:00:00 2001
+From: Vasiliy Kovalev <kovalev@altlinux.org>
+Date: Wed, 15 Apr 2026 18:52:37 +0300
+Subject: net/9p: fix infinite loop in p9_client_rpc on fatal signal
+
+From: Vasiliy Kovalev <kovalev@altlinux.org>
+
+commit 6b4f48728faa8bb514368f7eacda05565dea8696 upstream.
+
+When p9_client_rpc() is called with type P9_TFLUSH and the transport
+has no peer (e.g. fd transport backed by pipes with no 9p server),
+a fatal signal causes an infinite loop:
+
+  again:
+       err = io_wait_event_killable(req->wq, ...)
+       /* SIGKILL wakes the task, returns -ERESTARTSYS */
+
+       if (err == -ERESTARTSYS && c->status == Connected &&
+               type == P9_TFLUSH) {
+               sigpending = 1;
+               clear_thread_flag(TIF_SIGPENDING);
+               goto again;
+       }
+
+clear_thread_flag() clears TIF_SIGPENDING before jumping back to
+io_wait_event_killable(). signal_pending_state() checks TIF_SIGPENDING,
+finds it zero, and the task goes to sleep again. The task can only wake
+on the next signal delivery that calls signal_wake_up() and sets
+TIF_SIGPENDING again. When that happens the loop repeats, clears
+TIF_SIGPENDING, and sleeps again indefinitely.
+
+This is triggered in practice by coredump_wait(): when a thread in a
+multi-threaded process causes a coredump (e.g. via SIGSYS from Syscall
+User Dispatch), coredump_wait() sends SIGKILL to all other threads and
+waits for them to call mm_release(). If one of those threads is blocked
+in p9_client_rpc() over an fd transport with no peer, it enters the
+P9_TFLUSH loop and never calls mm_release(), so coredump_wait() stalls
+forever:
+
+INFO: task syz.0.18:676 blocked for more than 143 seconds.
+      Not tainted 6.12.77+ #1
+task:syz.0.18 state:D stack:27600 pid:676 tgid:673 ppid:630 flags:0x00000004
+Call Trace:
+ <TASK>
+ context_switch kernel/sched/core.c:5344 [inline]
+ __schedule+0xcb4/0x5d50 kernel/sched/core.c:6724
+ __schedule_loop kernel/sched/core.c:6801 [inline]
+ schedule+0xe5/0x350 kernel/sched/core.c:6816
+ schedule_timeout+0x253/0x290 kernel/time/timer.c:2593
+ do_wait_for_common kernel/sched/completion.c:95 [inline]
+ __wait_for_common+0x409/0x600 kernel/sched/completion.c:116
+ wait_for_common kernel/sched/completion.c:127 [inline]
+ wait_for_completion_state+0x1d/0x40 kernel/sched/completion.c:264
+ coredump_wait fs/coredump.c:448 [inline]
+ do_coredump+0x854/0x4350 fs/coredump.c:629
+ get_signal+0x1425/0x2730 kernel/signal.c:2903
+ arch_do_signal_or_restart+0x81/0x880 arch/x86/kernel/signal.c:337
+ exit_to_user_mode_loop kernel/entry/common.c:111 [inline]
+ exit_to_user_mode_prepare include/linux/entry-common.h:328 [inline]
+ __syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline]
+ syscall_exit_to_user_mode+0xf9/0x160 kernel/entry/common.c:218
+ do_syscall_64+0x102/0x220 arch/x86/entry/common.c:84
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+ </TASK>
+
+Fix: check fatal_signal_pending() before clearing TIF_SIGPENDING in the
+P9_TFLUSH retry loop. At that point TIF_SIGPENDING is still set, so
+fatal_signal_pending() works correctly. If a fatal signal is pending,
+jump to recalc_sigpending to restore TIF_SIGPENDING and return
+-ERESTARTSYS to the caller.
+
+The same defect is present in stable kernels back to 5.4. On those
+kernels the infinite loop is broken earlier by a second SIGKILL from
+the parent process (e.g. kill_and_wait() retrying after a timeout),
+resulting in a zombie process and a shutdown delay rather than a
+permanent D-state hang, but the underlying flaw is the same.
+
+Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
+
+Fixes: 91b8534fa8f5 ("9p: make rpc code common and rework flush code")
+Closes: https://syzkaller.appspot.com/bug?extid=3ce7863f8fc836a427e7
+Cc: stable@vger.kernel.org
+Signed-off-by: Vasiliy Kovalev <kovalev@altlinux.org>
+Message-ID: <20260415155237.182891-1-kovalev@altlinux.org>
+Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/9p/client.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/net/9p/client.c
++++ b/net/9p/client.c
+@@ -600,6 +600,8 @@ again:
+       if (err == -ERESTARTSYS && c->status == Connected &&
+           type == P9_TFLUSH) {
++              if (fatal_signal_pending(current))
++                      goto recalc_sigpending;
+               sigpending = 1;
+               clear_thread_flag(TIF_SIGPENDING);
+               goto again;
diff --git a/queue-7.1/ntfs-add-bounds-check-before-accessing-ea-entries.patch b/queue-7.1/ntfs-add-bounds-check-before-accessing-ea-entries.patch
new file mode 100644 (file)
index 0000000..30b06a7
--- /dev/null
@@ -0,0 +1,57 @@
+From 937282f7d15b593d0be765fa2ced164130ec87f7 Mon Sep 17 00:00:00 2001
+From: Hyunchul Lee <hyc.lee@gmail.com>
+Date: Sat, 23 May 2026 13:14:23 +0900
+Subject: ntfs: add bounds check before accessing EA entries
+
+From: Hyunchul Lee <hyc.lee@gmail.com>
+
+commit 937282f7d15b593d0be765fa2ced164130ec87f7 upstream.
+
+in ntfs_ea_lookup and ntfs_listxattr, this verifies that there is enough
+space in the EA entry before accessing the next_entry_offset field of
+the EA entry.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/ea.c |   16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/fs/ntfs/ea.c
++++ b/fs/ntfs/ea.c
+@@ -53,11 +53,11 @@ static int ntfs_ea_lookup(char *ea_buf,
+       loff_t offset, p_ea_size;
+       unsigned int next;
+-      if (ea_buf_size < sizeof(struct ea_attr))
+-              goto out;
+-
+       offset = 0;
+       do {
++              if (ea_buf_size - offset < sizeof(struct ea_attr))
++                      break;
++
+               p_ea = (const struct ea_attr *)&ea_buf[offset];
+               next = le32_to_cpu(p_ea->next_entry_offset);
+               p_ea_size = next ? next : (ea_buf_size - offset);
+@@ -479,13 +479,13 @@ ssize_t ntfs_listxattr(struct dentry *de
+       if (ea_info_qsize > ea_buf_size || ea_info_qsize == 0)
+               goto out;
+-      if (ea_info_qsize < sizeof(struct ea_attr)) {
+-              err = -EIO;
+-              goto out;
+-      }
+-
+       offset = 0;
+       do {
++              if (ea_info_qsize - offset < sizeof(struct ea_attr)) {
++                      err = -EIO;
++                      goto out;
++              }
++
+               p_ea = (const struct ea_attr *)&ea_buf[offset];
+               next = le32_to_cpu(p_ea->next_entry_offset);
+               ea_size = next ? next : (ea_info_qsize - offset);
diff --git a/queue-7.1/ntfs-add-wq_percpu-to-alloc_workqueue-users.patch b/queue-7.1/ntfs-add-wq_percpu-to-alloc_workqueue-users.patch
new file mode 100644 (file)
index 0000000..9e40ffa
--- /dev/null
@@ -0,0 +1,50 @@
+From 38e8db370843b518ff9bee4af46c6b800684cc78 Mon Sep 17 00:00:00 2001
+From: Marco Crivellari <marco.crivellari@suse.com>
+Date: Thu, 14 May 2026 15:54:08 +0200
+Subject: ntfs: Add WQ_PERCPU to alloc_workqueue users
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Marco Crivellari <marco.crivellari@suse.com>
+
+commit 38e8db370843b518ff9bee4af46c6b800684cc78 upstream.
+
+This continues the effort to refactor workqueue APIs, which began with
+the introduction of new workqueues and a new alloc_workqueue flag in:
+
+  commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq")
+  commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag")
+
+The refactoring is going to alter the default behavior of
+alloc_workqueue() to be unbound by default.
+
+With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND),
+any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND
+must now use WQ_PERCPU. For more details see the Link tag below.
+
+In order to keep alloc_workqueue() behavior identical, explicitly request
+WQ_PERCPU.
+
+Cc: stable@vger.kernel.org # v7.1
+Link: https://lore.kernel.org/all/20250221112003.1dSuoGyc@linutronix.de/
+Suggested-by: Tejun Heo <tj@kernel.org>
+Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/super.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/ntfs/super.c
++++ b/fs/ntfs/super.c
+@@ -2656,7 +2656,7 @@ MODULE_ALIAS_FS("ntfs");
+ static int ntfs_workqueue_init(void)
+ {
+-      ntfs_wq = alloc_workqueue("ntfs-bg-io", 0, 0);
++      ntfs_wq = alloc_workqueue("ntfs-bg-io", WQ_PERCPU, 0);
+       if (!ntfs_wq)
+               return -ENOMEM;
+       return 0;
diff --git a/queue-7.1/ntfs-avoid-heap-allocation-for-free-cluster-readahead-state.patch b/queue-7.1/ntfs-avoid-heap-allocation-for-free-cluster-readahead-state.patch
new file mode 100644 (file)
index 0000000..14534b0
--- /dev/null
@@ -0,0 +1,109 @@
+From c05132077df57a384919f61d7f8a8e76d748a6d4 Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Fri, 22 May 2026 23:20:48 +0900
+Subject: ntfs: avoid heap allocation for free-cluster readahead state
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit c05132077df57a384919f61d7f8a8e76d748a6d4 upstream.
+
+get_nr_free_clusters() allocates a temporary file_ra_state before it
+publishes the precomputed free cluster count, sets NVolFreeClusterKnown(),
+and wakes vol->free_waitq. If that allocation fails, the worker returns
+without setting the flag or waking waiters, so callers waiting for the free
+count can block indefinitely.
+
+The readahead state is only used synchronously while scanning the bitmap.
+Keep it on the stack and pass it by address to the readahead helper. This
+eliminates the early allocation failure path instead of adding a special
+case that publishes a conservative count and wakes the waitqueue.
+Zero-initialize the on-stack state because file_ra_state_init() only sets
+ra_pages and prev_pos.
+
+Apply the same treatment to __get_nr_free_mft_records(), which scans the
+MFT bitmap with the same short-lived readahead state.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/super.c |   22 ++++++----------------
+ 1 file changed, 6 insertions(+), 16 deletions(-)
+
+--- a/fs/ntfs/super.c
++++ b/fs/ntfs/super.c
+@@ -1955,7 +1955,7 @@ s64 get_nr_free_clusters(struct ntfs_vol
+       struct address_space *mapping = vol->lcnbmp_ino->i_mapping;
+       struct folio *folio;
+       pgoff_t index, max_index;
+-      struct file_ra_state *ra;
++      struct file_ra_state ra = { 0 };
+       ntfs_debug("Entering.");
+       /* Serialize accesses to the cluster bitmap. */
+@@ -1963,11 +1963,7 @@ s64 get_nr_free_clusters(struct ntfs_vol
+       if (NVolFreeClusterKnown(vol))
+               return atomic64_read(&vol->free_clusters);
+-      ra = kzalloc(sizeof(*ra), GFP_NOFS);
+-      if (!ra)
+-              return 0;
+-
+-      file_ra_state_init(ra, mapping);
++      file_ra_state_init(&ra, mapping);
+       /*
+        * Convert the number of bits into bytes rounded up, then convert into
+@@ -1986,7 +1982,7 @@ s64 get_nr_free_clusters(struct ntfs_vol
+                * Get folio from page cache, getting it from backing store
+                * if necessary, and increment the use count.
+                */
+-              folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
++              folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
+               /* Ignore pages which errored synchronously. */
+               if (IS_ERR(folio)) {
+@@ -2025,7 +2021,6 @@ s64 get_nr_free_clusters(struct ntfs_vol
+       else
+               atomic64_set(&vol->free_clusters, nr_free);
+-      kfree(ra);
+       NVolSetFreeClusterKnown(vol);
+       wake_up_all(&vol->free_waitq);
+       ntfs_debug("Exiting.");
+@@ -2080,15 +2075,11 @@ static unsigned long __get_nr_free_mft_r
+       struct address_space *mapping = vol->mftbmp_ino->i_mapping;
+       struct folio *folio;
+       pgoff_t index;
+-      struct file_ra_state *ra;
++      struct file_ra_state ra = { 0 };
+       ntfs_debug("Entering.");
+-      ra = kzalloc(sizeof(*ra), GFP_NOFS);
+-      if (!ra)
+-              return 0;
+-
+-      file_ra_state_init(ra, mapping);
++      file_ra_state_init(&ra, mapping);
+       /* Use multiples of 4 bytes, thus max_size is PAGE_SIZE / 4. */
+       ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = 0x%lx.",
+@@ -2100,7 +2091,7 @@ static unsigned long __get_nr_free_mft_r
+                * Get folio from page cache, getting it from backing store
+                * if necessary, and increment the use count.
+                */
+-              folio = ntfs_get_locked_folio(mapping, index, max_index, ra);
++              folio = ntfs_get_locked_folio(mapping, index, max_index, &ra);
+               /* Ignore pages which errored synchronously. */
+               if (IS_ERR(folio)) {
+@@ -2132,7 +2123,6 @@ static unsigned long __get_nr_free_mft_r
+       else
+               atomic64_set(&vol->free_mft_records, nr_free);
+-      kfree(ra);
+       ntfs_debug("Exiting.");
+       return nr_free;
+ }
diff --git a/queue-7.1/ntfs-avoid-self-deadlock-during-inode-eviction.patch b/queue-7.1/ntfs-avoid-self-deadlock-during-inode-eviction.patch
new file mode 100644 (file)
index 0000000..1f5681b
--- /dev/null
@@ -0,0 +1,71 @@
+From 77dc384207d5fa63ba97c3bf3285fe1215a1cbf6 Mon Sep 17 00:00:00 2001
+From: Hyunchul Lee <hyc.lee@gmail.com>
+Date: Thu, 2 Jul 2026 14:28:16 +0900
+Subject: ntfs: avoid self-deadlock during inode eviction
+
+From: Hyunchul Lee <hyc.lee@gmail.com>
+
+commit 77dc384207d5fa63ba97c3bf3285fe1215a1cbf6 upstream.
+
+An attribute-list update performed while allocating clusters can drop the
+last reference to the temporary attribute inode. Evicting that inode
+drops its reference to the base inode and can invoke ntfs_drop_big_inode()
+for the base inode from within the base inode's own writeback path.
+
+If the base inode is unlinked, ntfs_drop_big_inode() calls
+truncate_setsize(), which waits for the inode's folio writeback to
+complete. The same writeback worker is responsible for completing that
+writeback, so it waits for itself indefinitely.
+
+Prevent this self-deadlock by grabbing a reference to the base inode at the
+beginning of ntfs_writepages() and releasing it at the end of the function.
+This defers eviction until all bios have been submitted, allowing the wait
+for folio writeback to complete safely.
+
+Fixes: b041ca562526 ("ntfs: update iomap and address space operations")
+Cc: stable@vger.kernel.org
+Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/aops.c | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
+index 1fbf832ad165..88e5b2def66c 100644
+--- a/fs/ntfs/aops.c
++++ b/fs/ntfs/aops.c
+@@ -251,6 +251,8 @@ static int ntfs_writepages(struct address_space *mapping,
+               .wbc            = wbc,
+               .ops            = &ntfs_writeback_ops,
+       };
++      bool need_iput = false;
++      int ret;
+       if (NVolShutdown(ni->vol))
+               return -EIO;
+@@ -267,7 +269,20 @@ static int ntfs_writepages(struct address_space *mapping,
+               return -EOPNOTSUPP;
+       }
+-      return iomap_writepages(&wpc);
++      /*
++       * Prevent eviction in writeback to avoid deadlock in
++       * ntfs_drop_big_inode().
++       */
++      if ((ni->type == AT_DATA || ni->type == AT_INDEX_ALLOCATION) &&
++          igrab(inode))
++              need_iput = true;
++
++      ret = iomap_writepages(&wpc);
++
++      if (need_iput)
++              iput(inode);
++
++      return ret;
+ }
+ static int ntfs_swap_activate(struct swap_info_struct *sis,
+-- 
+2.55.0
+
diff --git a/queue-7.1/ntfs-centalize-index_root-header-validation.patch b/queue-7.1/ntfs-centalize-index_root-header-validation.patch
new file mode 100644 (file)
index 0000000..79af6f4
--- /dev/null
@@ -0,0 +1,104 @@
+From 8b97b302f553a480fb76d2afd53cd6c0635a9dcd Mon Sep 17 00:00:00 2001
+From: Hyunchul Lee <hyc.lee@gmail.com>
+Date: Sat, 23 May 2026 13:14:21 +0900
+Subject: ntfs: centalize $INDEX_ROOT header validation
+
+From: Hyunchul Lee <hyc.lee@gmail.com>
+
+commit 8b97b302f553a480fb76d2afd53cd6c0635a9dcd upstream.
+
+Add a dedicated helper to perform stricter validation of $INDEX_ROOT and
+use it for both directory inodes and named index inodes. This keeps the
+root size and header geometry checks consistent across both read paths.
+
+Cc: stable@vger.kernel.org # v7.1
+Tested-by: woot000 <woot000@woot000.com>
+Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/index.c |   18 ++++++++++++++++++
+ fs/ntfs/index.h |    3 +++
+ fs/ntfs/inode.c |   11 ++---------
+ 3 files changed, 23 insertions(+), 9 deletions(-)
+
+--- a/fs/ntfs/index.c
++++ b/fs/ntfs/index.c
+@@ -544,6 +544,24 @@ int ntfs_index_block_inconsistent(struct
+       return 0;
+ }
++int ntfs_index_root_inconsistent(struct ntfs_volume *vol,
++                               const struct attr_record *a,
++                               const struct index_root *ir, u64 inum)
++{
++      u32 value_length = le32_to_cpu(a->data.resident.value_length);
++
++      if (value_length < offsetof(struct index_root, index)) {
++              ntfs_error(vol->sb, "$INDEX_ROOT in inode %llu is too small.",
++                         (unsigned long long)inum);
++              return -EIO;
++      }
++
++      return ntfs_index_header_inconsistent(vol, &ir->index,
++                                            value_length -
++                                            offsetof(struct index_root, index),
++                                            inum);
++}
++
+ static struct index_root *ntfs_ir_lookup(struct ntfs_inode *ni, __le16 *name,
+               u32 name_len, struct ntfs_attr_search_ctx **ctx)
+ {
+--- a/fs/ntfs/index.h
++++ b/fs/ntfs/index.h
+@@ -89,6 +89,9 @@ struct ntfs_index_context {
+       bool sync_write;
+ };
++int ntfs_index_root_inconsistent(struct ntfs_volume *vol,
++                               const struct attr_record *a,
++                               const struct index_root *ir, u64 inum);
+ int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
+                                 const struct index_block *ib,
+                                 u32 block_size, s64 vcn, u64 inum);
+--- a/fs/ntfs/inode.c
++++ b/fs/ntfs/inode.c
+@@ -896,7 +896,6 @@ skip_attr_list_load:
+        */
+       if (S_ISDIR(vi->i_mode)) {
+               struct index_root *ir;
+-              u8 *ir_end, *index_end;
+ view_index_meta:
+               /* It is a directory, find index root attribute. */
+@@ -946,10 +945,7 @@ view_index_meta:
+               }
+               ir = (struct index_root *)((u8 *)a +
+                               le16_to_cpu(a->data.resident.value_offset));
+-              ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length);
+-              index_end = (u8 *)&ir->index +
+-                              le32_to_cpu(ir->index.index_length);
+-              if (index_end > ir_end) {
++              if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no)) {
+                       ntfs_error(vi->i_sb, "Directory index is corrupt.");
+                       goto unm_err_out;
+               }
+@@ -1492,7 +1488,6 @@ static int ntfs_read_locked_index_inode(
+       struct attr_record *a;
+       struct ntfs_attr_search_ctx *ctx;
+       struct index_root *ir;
+-      u8 *ir_end, *index_end;
+       int err = 0;
+       ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no);
+@@ -1543,9 +1538,7 @@ static int ntfs_read_locked_index_inode(
+       }
+       ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset));
+-      ir_end = (u8 *)ir + le32_to_cpu(a->data.resident.value_length);
+-      index_end = (u8 *)&ir->index + le32_to_cpu(ir->index.index_length);
+-      if (index_end > ir_end) {
++      if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no)) {
+               ntfs_error(vi->i_sb, "Index is corrupt.");
+               goto unm_err_out;
+       }
diff --git a/queue-7.1/ntfs-detect-mapping-pairs-lcn-accumulator-overflow.patch b/queue-7.1/ntfs-detect-mapping-pairs-lcn-accumulator-overflow.patch
new file mode 100644 (file)
index 0000000..619e262
--- /dev/null
@@ -0,0 +1,51 @@
+From ec4f061f2219e0f0c6465d56d0380bf749235a53 Mon Sep 17 00:00:00 2001
+From: Samuel Moelius <sam.moelius@trailofbits.com>
+Date: Wed, 3 Jun 2026 17:41:09 +0000
+Subject: ntfs: detect mapping-pairs LCN accumulator overflow
+
+From: Samuel Moelius <sam.moelius@trailofbits.com>
+
+commit ec4f061f2219e0f0c6465d56d0380bf749235a53 upstream.
+
+The NTFS mapping-pairs parser accumulates relative LCN deltas in a
+signed integer.  A corrupted attribute can drive that addition past
+the representable range.
+
+One corrupt runlist shape sets the accumulated LCN to S64_MAX and
+then adds a delta of 1 in the next mapping-pairs entry.
+
+Signed overflow is undefined and can turn an invalid runlist into a
+different set of physical clusters.
+
+Check the LCN addition for overflow before storing the next run.
+
+Cc: stable@vger.kernel.org # v7.1
+Assisted-by: Codex:gpt-5.5-cyber-preview
+Signed-off-by: Samuel Moelius <sam.moelius@trailofbits.com>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/runlist.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
+index d8e8aa7b5bc0..cbb6576cf725 100644
+--- a/fs/ntfs/runlist.c
++++ b/fs/ntfs/runlist.c
+@@ -860,7 +860,11 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *
+                       for (deltaxcn = (s8)buf[b--]; b > b2; b--)
+                               deltaxcn = (deltaxcn << 8) + buf[b];
+                       /* Change the current lcn to its new value. */
+-                      lcn += deltaxcn;
++                      if (unlikely(check_add_overflow(lcn, deltaxcn, &lcn))) {
++                              ntfs_error(vol->sb,
++                                              "LCN overflow in mapping pairs array.");
++                              goto err_out;
++                      }
+ #ifdef DEBUG
+                       /*
+                        * On NTFS 1.2-, apparently can have lcn == -1 to
+-- 
+2.55.0
+
diff --git a/queue-7.1/ntfs-do-not-replace-volume-name-after-lookup-errors.patch b/queue-7.1/ntfs-do-not-replace-volume-name-after-lookup-errors.patch
new file mode 100644 (file)
index 0000000..dc266c1
--- /dev/null
@@ -0,0 +1,51 @@
+From 40d88020d0797f96a93edd2e8edc413c2e2d8f84 Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Sat, 30 May 2026 23:35:10 +0900
+Subject: ntfs: do not replace volume name after lookup errors
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit 40d88020d0797f96a93edd2e8edc413c2e2d8f84 upstream.
+
+ntfs_write_volume_label() removes an existing $VOLUME_NAME attribute and
+then adds the replacement. The old code only distinguished lookup success
+from all other results, so any lookup error was treated like an absent
+label and the add path still ran.
+
+That is unsafe once lookup-time validation rejects corrupt $VOLUME_NAME
+records with -EIO: the corrupt record would remain in place and a second
+$VOLUME_NAME record could be appended next to it.
+
+Only add the replacement after the old label was removed successfully or
+after lookup returned -ENOENT. Propagate all other lookup errors, and
+also stop if removing the old attribute fails.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/super.c |   11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/fs/ntfs/super.c
++++ b/fs/ntfs/super.c
+@@ -452,10 +452,15 @@ int ntfs_write_volume_label(struct ntfs_
+               goto out;
+       }
+-      if (!ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0,
+-                           ctx))
+-              ntfs_attr_record_rm(ctx);
++      ret = ntfs_attr_lookup(AT_VOLUME_NAME, NULL, 0, 0, 0, NULL, 0,
++                             ctx);
++      if (!ret)
++              ret = ntfs_attr_record_rm(ctx);
++      else if (ret == -ENOENT)
++              ret = 0;
+       ntfs_attr_put_search_ctx(ctx);
++      if (ret)
++              goto out;
+       ret = ntfs_resident_attr_record_add(vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0,
+                                           (u8 *)uname, uname_len * sizeof(__le16), 0);
diff --git a/queue-7.1/ntfs-fail-attrlist-updates-when-the-superblock-is-inactive.patch b/queue-7.1/ntfs-fail-attrlist-updates-when-the-superblock-is-inactive.patch
new file mode 100644 (file)
index 0000000..0f8081c
--- /dev/null
@@ -0,0 +1,58 @@
+From 0ebe8f625ab0520217a425d7cd366e4670484941 Mon Sep 17 00:00:00 2001
+From: Peiyang He <peiyang_he@smail.nju.edu.cn>
+Date: Mon, 6 Jul 2026 12:00:15 +0800
+Subject: ntfs: fail attrlist updates when the superblock is inactive
+
+From: Peiyang He <peiyang_he@smail.nju.edu.cn>
+
+commit 0ebe8f625ab0520217a425d7cd366e4670484941 upstream.
+
+generic_shutdown_super() clears SB_ACTIVE before evicting cached inodes.
+If eviction selects the fake inode for a base inode's unnamed
+$ATTRIBUTE_LIST attribute, ntfs_evict_big_inode() drops the fake inode's
+reference on the base inode while the fake inode is still hashed and marked
+I_FREEING.
+
+That iput can synchronously write back the base inode. The writeback path
+may update mapping pairs and call ntfs_attrlist_update(), which
+unconditionally calls ntfs_attr_iget() for the same $ATTRIBUTE_LIST fake
+inode. VFS then finds the I_FREEING inode and waits for eviction to finish,
+but the current task is still inside that eviction path, causing a
+self-deadlock in find_inode().
+
+Fix this by mirroring the teardown guard used by __ntfs_write_inode():
+once SB_ACTIVE has been cleared, do not try to iget the attribute-list
+fake inode. Return -EIO so teardown aborts the update instead of waiting on
+the inode it is evicting.
+
+Reported-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
+Closes: https://lore.kernel.org/all/AB8D5E603E6EA856+ae5f622a-dd3a-4e38-bdd2-42276ae0e1a8@smail.nju.edu.cn/
+Fixes: 495e90fa3348 ("ntfs: update attrib operations")
+Cc: stable@vger.kernel.org
+Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
+Assisted-by: Codex:gpt-5.5
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/attrlist.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/fs/ntfs/attrlist.c
++++ b/fs/ntfs/attrlist.c
+@@ -57,6 +57,15 @@ int ntfs_attrlist_update(struct ntfs_ino
+       struct ntfs_inode *attr_ni;
+       int err;
++      /*
++       * generic_shutdown_super() clears SB_ACTIVE before evicting cached
++       * inodes. Do not look up the attribute-list inode after SB_ACTIVE has
++       * been cleared; it may already be I_FREEING, and waiting on it can
++       * self-deadlock.
++       */
++      if (!(VFS_I(base_ni)->i_sb->s_flags & SB_ACTIVE))
++              return -EIO;
++
+       attr_vi = ntfs_attr_iget(VFS_I(base_ni), AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
+       if (IS_ERR(attr_vi)) {
+               err = PTR_ERR(attr_vi);
diff --git a/queue-7.1/ntfs-fix-hole-runlist-memory-leak-in-insert-range-error-path.patch b/queue-7.1/ntfs-fix-hole-runlist-memory-leak-in-insert-range-error-path.patch
new file mode 100644 (file)
index 0000000..167ff9e
--- /dev/null
@@ -0,0 +1,37 @@
+From 06769b8f23b4b645b270c438649fff79768fb6fe Mon Sep 17 00:00:00 2001
+From: Peiyang He <peiyang_he@smail.nju.edu.cn>
+Date: Sun, 5 Jul 2026 19:14:09 +0800
+Subject: ntfs: fix hole runlist memory leak in insert range error path
+
+From: Peiyang He <peiyang_he@smail.nju.edu.cn>
+
+commit 06769b8f23b4b645b270c438649fff79768fb6fe upstream.
+
+ntfs_non_resident_attr_insert_range() allocates hole_rl before mapping the
+whole runlist. If ntfs_attr_map_whole_runlist() fails, the error path drops
+ni->runlist.lock and returns without freeing hole_rl. This leaks memory
+of sizeof(*hole_rl) * 2 bytes.
+
+Fix this memory leak by freeing hole_rl before returning from
+that error path, matching the later error paths in the same function.
+
+Fixes: 495e90fa3348 ("ntfs: update attrib operations")
+Cc: stable@vger.kernel.org
+Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/attrib.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/fs/ntfs/attrib.c
++++ b/fs/ntfs/attrib.c
+@@ -5327,6 +5327,7 @@ int ntfs_non_resident_attr_insert_range(
+       ret = ntfs_attr_map_whole_runlist(ni);
+       if (ret) {
+               up_write(&ni->runlist.lock);
++              kfree(hole_rl);
+               return ret;
+       }
diff --git a/queue-7.1/ntfs-fix-incorrect-size-of-symbolic-link.patch b/queue-7.1/ntfs-fix-incorrect-size-of-symbolic-link.patch
new file mode 100644 (file)
index 0000000..a78af29
--- /dev/null
@@ -0,0 +1,31 @@
+From 05a5ff86a7f12c861e3516d3dc4d092ce620742d Mon Sep 17 00:00:00 2001
+From: Hyunchul Lee <hyc.lee@gmail.com>
+Date: Mon, 15 Jun 2026 08:49:52 +0900
+Subject: ntfs: fix incorrect size of symbolic link
+
+From: Hyunchul Lee <hyc.lee@gmail.com>
+
+commit 05a5ff86a7f12c861e3516d3dc4d092ce620742d upstream.
+
+This patch fixes the issue where a symbolic link size is displayed as 0.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/inode.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/fs/ntfs/inode.c
++++ b/fs/ntfs/inode.c
+@@ -1201,6 +1201,9 @@ no_data_attr_special_case:
+       else
+               vi->i_blocks = ni->allocated_size >> 9;
++      if (S_ISLNK(vi->i_mode) && ni->target)
++              vi->i_size = strlen(ni->target);
++
+       ntfs_debug("Done.");
+       return 0;
+ unm_err_out:
diff --git a/queue-7.1/ntfs-fix-mrec_lock-abba-deadlock-in-rename.patch b/queue-7.1/ntfs-fix-mrec_lock-abba-deadlock-in-rename.patch
new file mode 100644 (file)
index 0000000..f307c1b
--- /dev/null
@@ -0,0 +1,144 @@
+From eb94f5a41a193a425e09a63cb75dffd151d8f42e Mon Sep 17 00:00:00 2001
+From: Peiyang He <peiyang_he@smail.nju.edu.cn>
+Date: Tue, 30 Jun 2026 11:08:56 +0800
+Subject: ntfs: fix mrec_lock ABBA deadlock in rename
+
+From: Peiyang He <peiyang_he@smail.nju.edu.cn>
+
+commit eb94f5a41a193a425e09a63cb75dffd151d8f42e upstream.
+
+ntfs_file_fsync(), ntfs_dir_fsync() and __ntfs_write_inode() lock an
+inode's mrec_lock before taking the mrec_lock of its parent directory.
+
+ntfs_rename() takes old_ni->mrec_lock and old_dir_ni->mrec_lock
+before taking new_ni->mrec_lock for an existing target, or
+new_dir_ni->mrec_lock for a cross-directory rename.
+This can deadlock when ntfs_file_fsync() or __ntfs_write_inode() holds
+the target inode, or when ntfs_dir_fsync() holds a child target
+directory, while rename() holds the parent directory and waits for the
+target.
+
+Fix this by locking the existing target inode before taking any parent
+directory mrec_lock. For cross-directory renames where the target parent
+is a descendant of the source parent, lock the target parent before the
+source parent so the directory order matches the child-to-parent order used
+by ntfs_file_fsync(), ntfs_dir_fsync(), and __ntfs_write_inode().
+
+Reported-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
+Closes: https://lore.kernel.org/all/C4D296F0E9F3D66C+9397ffbc-eb55-44bb-9b3f-5da4809e7955@smail.nju.edu.cn/
+Fixes: af0db57d4293 ("ntfs: update inode operations")
+Cc: stable@vger.kernel.org
+Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn>
+Assisted-by: Codex:gpt-5.5
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/namei.c |   62 ++++++++++++++++++++++++++++----------------------------
+ 1 file changed, 31 insertions(+), 31 deletions(-)
+
+--- a/fs/ntfs/namei.c
++++ b/fs/ntfs/namei.c
+@@ -1264,6 +1264,7 @@ static int ntfs_rename(struct mnt_idmap
+       struct ntfs_volume *vol = NTFS_SB(sb);
+       struct ntfs_inode *old_ni, *new_ni = NULL;
+       struct ntfs_inode *old_dir_ni = NTFS_I(old_dir), *new_dir_ni = NTFS_I(new_dir);
++      bool new_dir_first = false;
+       if (NVolShutdown(old_dir_ni->vol))
+               return -EIO;
+@@ -1299,36 +1300,39 @@ static int ntfs_rename(struct mnt_idmap
+       old_inode = old_dentry->d_inode;
+       new_inode = new_dentry->d_inode;
+       old_ni = NTFS_I(old_inode);
++      if (new_inode)
++              new_ni = NTFS_I(new_inode);
++      if (old_dir != new_dir)
++              new_dir_first = is_subdir(new_dentry->d_parent,
++                                        old_dentry->d_parent);
+       if (!(vol->vol_flags & VOLUME_IS_DIRTY))
+               ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY);
+       mutex_lock_nested(&old_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL);
+-      mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
++      if (new_ni)
++              mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2);
++
++      if (old_dir == new_dir) {
++              mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
++      } else if (new_dir_first) {
++              mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
++              mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2);
++      } else {
++              mutex_lock_nested(&old_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
++              mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2);
++      }
+-      if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni)) {
++      if (NInoBeingDeleted(old_ni) || NInoBeingDeleted(old_dir_ni) ||
++          (new_ni && NInoBeingDeleted(new_ni)) ||
++          (old_dir != new_dir && NInoBeingDeleted(new_dir_ni))) {
+               err = -ENOENT;
+-              goto unlock_old;
++              goto err_out;
+       }
+       is_dir = S_ISDIR(old_inode->i_mode);
+       if (new_inode) {
+-              new_ni = NTFS_I(new_inode);
+-              mutex_lock_nested(&new_ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL_2);
+-              if (old_dir != new_dir) {
+-                      mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2);
+-                      if (NInoBeingDeleted(new_dir_ni)) {
+-                              err = -ENOENT;
+-                              goto err_out;
+-                      }
+-              }
+-
+-              if (NInoBeingDeleted(new_ni)) {
+-                      err = -ENOENT;
+-                      goto err_out;
+-              }
+-
+               if (is_dir) {
+                       struct mft_record *ni_mrec;
+@@ -1346,14 +1350,6 @@ static int ntfs_rename(struct mnt_idmap
+               err = ntfs_delete(new_ni, new_dir_ni, uname_new, new_name_len, false);
+               if (err)
+                       goto err_out;
+-      } else {
+-              if (old_dir != new_dir) {
+-                      mutex_lock_nested(&new_dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT_2);
+-                      if (NInoBeingDeleted(new_dir_ni)) {
+-                              err = -ENOENT;
+-                              goto err_out;
+-                      }
+-              }
+       }
+       err = __ntfs_link(old_ni, new_dir_ni, uname_new, new_name_len);
+@@ -1384,13 +1380,17 @@ static int ntfs_rename(struct mnt_idmap
+       inode_inc_iversion(new_dir);
+ err_out:
+-      if (old_dir != new_dir)
++      if (old_dir == new_dir) {
++              mutex_unlock(&old_dir_ni->mrec_lock);
++      } else if (new_dir_first) {
++              mutex_unlock(&old_dir_ni->mrec_lock);
+               mutex_unlock(&new_dir_ni->mrec_lock);
+-      if (new_inode)
++      } else {
++              mutex_unlock(&new_dir_ni->mrec_lock);
++              mutex_unlock(&old_dir_ni->mrec_lock);
++      }
++      if (new_ni)
+               mutex_unlock(&new_ni->mrec_lock);
+-
+-unlock_old:
+-      mutex_unlock(&old_dir_ni->mrec_lock);
+       mutex_unlock(&old_ni->mrec_lock);
+       if (uname_new)
+               kmem_cache_free(ntfs_name_cache, uname_new);
diff --git a/queue-7.1/ntfs-fix-off-by-one-in-mapping-pairs-decoding-bounds-checks.patch b/queue-7.1/ntfs-fix-off-by-one-in-mapping-pairs-decoding-bounds-checks.patch
new file mode 100644 (file)
index 0000000..ba8de30
--- /dev/null
@@ -0,0 +1,70 @@
+From 18760a74ef7c28df93726445b5595162e62ed341 Mon Sep 17 00:00:00 2001
+From: Ron de Bruijn <rmbruijn@gmail.com>
+Date: Sat, 30 May 2026 09:19:18 +0900
+Subject: ntfs: fix off-by-one in mapping pairs decoding bounds checks
+
+From: Ron de Bruijn <rmbruijn@gmail.com>
+
+commit 18760a74ef7c28df93726445b5595162e62ed341 upstream.
+
+In ntfs_mapping_pairs_decompress(), attr_end points one byte past the
+end of the attribute record:
+
+    attr_end = (u8 *)attr + le32_to_cpu(attr->length);
+
+The two bounds checks validating that mapping pair data bytes fit within
+the attribute use strict greater-than (>), which allows a one-byte
+out-of-bounds read when the data extends exactly to attr_end:
+
+  b = *buf & 0xf;
+  if (b) {
+      if (unlikely(buf + b > attr_end))   // off-by-one
+          goto io_error;
+      for (deltaxcn = (s8)buf[b--]; b; b--)
+          deltaxcn = (deltaxcn << 8) + buf[b];
+  }
+
+When buf + b == attr_end, the check evaluates to false and buf[b] reads
+one byte past the valid attribute boundary. The same pattern appears in
+the LCN delta bytes check.
+
+Fix both checks to use >= so that buf[b] at exactly attr_end is
+correctly rejected as out of bounds.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: Ron de Bruijn <rmbruijn@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/runlist.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/fs/ntfs/runlist.c
++++ b/fs/ntfs/runlist.c
+@@ -763,7 +763,7 @@ struct runlist_element *ntfs_mapping_pai
+       buf = (u8 *)attr +
+               le16_to_cpu(attr->data.non_resident.mapping_pairs_offset);
+       attr_end = (u8 *)attr + le32_to_cpu(attr->length);
+-      if (unlikely(buf < (u8 *)attr || buf > attr_end)) {
++      if (unlikely(buf < (u8 *)attr || buf >= attr_end)) {
+               ntfs_error(vol->sb, "Corrupt attribute.");
+               return ERR_PTR(-EIO);
+       }
+@@ -811,7 +811,7 @@ struct runlist_element *ntfs_mapping_pai
+                */
+               b = *buf & 0xf;
+               if (b) {
+-                      if (unlikely(buf + b > attr_end))
++                      if (unlikely(buf + b >= attr_end))
+                               goto io_error;
+                       for (deltaxcn = (s8)buf[b--]; b; b--)
+                               deltaxcn = (deltaxcn << 8) + buf[b];
+@@ -855,7 +855,7 @@ struct runlist_element *ntfs_mapping_pai
+                       u8 b2 = *buf & 0xf;
+                       b = b2 + ((*buf >> 4) & 0xf);
+-                      if (buf + b > attr_end)
++                      if (buf + b >= attr_end)
+                               goto io_error;
+                       for (deltaxcn = (s8)buf[b--]; b > b2; b--)
+                               deltaxcn = (deltaxcn << 8) + buf[b];
diff --git a/queue-7.1/ntfs-fix-warn_on-for-resident-attribute-in-ntfs_map_runlist_nolock.patch b/queue-7.1/ntfs-fix-warn_on-for-resident-attribute-in-ntfs_map_runlist_nolock.patch
new file mode 100644 (file)
index 0000000..e7343ec
--- /dev/null
@@ -0,0 +1,63 @@
+From b8d6c528e9d57d263fee1a648409f84a68b2561d Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Thu, 2 Jul 2026 10:31:44 +0900
+Subject: ntfs: fix WARN_ON for resident attribute in ntfs_map_runlist_nolock()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit b8d6c528e9d57d263fee1a648409f84a68b2561d upstream.
+
+When ntfs_map_runlist_nolock() needs to look up the attribute extent
+containing a target VCN (ctx_needs_reset == true), it calls
+ntfs_attr_lookup() and then expects the result to be a non-resident
+attribute, since only non-resident attributes have a mapping pairs
+array to decompress.
+
+A crafted NTFS image can place a resident attribute where a non-resident
+one is expected, causing ntfs_attr_lookup() to succeed but return a
+resident attribute record.  Previously this was caught only by a
+WARN_ON(), which does not stop execution.  The code then falls through to
+read a->data.non_resident.highest_vcn from what is actually a resident
+attribute, accessing the wrong union member and corrupting the VCN range
+check.
+
+The caller path triggering this warning during mount is:
+
+  ntfs_map_runlist_nolock
+  ntfs_empty_logfile
+  load_system_files
+  ntfs_fill_super
+
+In this path ctx is NULL, so ntfs_map_runlist_nolock() allocates a
+temporary search context internally and sets ctx_needs_reset = true.
+The existing resident-attribute guard in the ctx != NULL branch already
+returns -EIO silently for the same condition; make the ctx_needs_reset
+path consistent by replacing the WARN_ON() with the same -EIO error
+return.
+
+This causes the crafted image to be rejected with a mount error instead
+of triggering a kernel warning.
+
+Fixes: 495e90fa3348 ("ntfs: update attrib operations")
+Cc: stable@vger.kernel.org
+Reported-by: Sangho Lee <kudo3228@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/attrib.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/fs/ntfs/attrib.c
++++ b/fs/ntfs/attrib.c
+@@ -174,7 +174,10 @@ int ntfs_map_runlist_nolock(struct ntfs_
+                               err = -EIO;
+                       goto err_out;
+               }
+-              WARN_ON(!ctx->attr->non_resident);
++              if (unlikely(!ctx->attr->non_resident)) {
++                      err = -EIO;
++                      goto err_out;
++              }
+       }
+       a = ctx->attr;
+       /*
diff --git a/queue-7.1/ntfs-free-volume-wide-resources-on-fill_super-failure.patch b/queue-7.1/ntfs-free-volume-wide-resources-on-fill_super-failure.patch
new file mode 100644 (file)
index 0000000..df36cf1
--- /dev/null
@@ -0,0 +1,73 @@
+From a9523a7d3b24b3a6b25ec1eb668ee6618cacf05e Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Thu, 21 May 2026 19:17:49 +0900
+Subject: ntfs: free volume-wide resources on fill_super failure
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit a9523a7d3b24b3a6b25ec1eb668ee6618cacf05e upstream.
+
+ntfs_fill_super()'s err_out_now path frees only the volume struct via
+kfree(vol), leaving several vol-owned allocations behind on every mount
+failure:
+
+  - vol->nls_map, loaded by ntfs_init_fs_context() via
+    load_nls_default() (or replaced by an explicit nls= option in
+    ntfs_parse_param()), is never unload_nls()'d.
+
+  - vol->volume_label, allocated by load_system_files() through
+    ntfs_ucstonls() once the $Volume name attribute has been parsed, is
+    not released by load_system_files()'s own error labels nor by the
+    fill_super() inline cleanup that only runs on d_make_root()
+    failure.  Any later failure inside load_system_files() leaks it.
+
+  - vol->lcn_empty_bits_per_page was kvfree()'d in
+    unl_upcase_iput_tmp_ino_err_out_now without clearing the pointer,
+    so it could not be folded into a single common cleanup.
+
+Because the failure paths never call ntfs_volume_free() and never reach
+the d_make_root() inline cleanup block (it sits above the label and is
+jumped over by the load_system_files() / kvmalloc failure gotos), these
+resources accumulate per failed mount attempt with no chance of
+recovery short of unloading the module.  This is a silent leak: the
+inodes loaded prior to failure remain hashed but generic_shutdown_super()
+skips evict_inodes() when sb->s_root is unset, so no CHECK_DATA_CORRUPTION
+warning is emitted either.
+
+Move the per-volume frees down to err_out_now and drop the
+lcn_empty_bits_per_page kvfree() from the upper label so the cleanup is
+performed exactly once on every failure path.  Using unconditional
+kvfree() / kfree() / unload_nls() is safe because they all accept NULL
+and the upper labels that previously freed nls_map (the d_make_root()
+inline cleanup) already clear the pointer.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/super.c |    5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/fs/ntfs/super.c
++++ b/fs/ntfs/super.c
+@@ -2536,8 +2536,6 @@ static int ntfs_fill_super(struct super_
+       }
+       /* Error exit code path. */
+ unl_upcase_iput_tmp_ino_err_out_now:
+-      if (vol->lcn_empty_bits_per_page)
+-              kvfree(vol->lcn_empty_bits_per_page);
+       /*
+        * Decrease the number of upcase users and destroy the global default
+        * upcase table if necessary.
+@@ -2557,6 +2555,9 @@ iput_tmp_ino_err_out_now:
+       /* Errors at this stage are irrelevant. */
+ err_out_now:
+       sb->s_fs_info = NULL;
++      kvfree(vol->lcn_empty_bits_per_page);
++      kfree(vol->volume_label);
++      unload_nls(vol->nls_map);
+       kfree(vol);
+       ntfs_debug("Failed, returning -EINVAL.");
+       lockdep_on();
diff --git a/queue-7.1/ntfs-grow-index-root-value-before-reparent-header-update.patch b/queue-7.1/ntfs-grow-index-root-value-before-reparent-header-update.patch
new file mode 100644 (file)
index 0000000..164e821
--- /dev/null
@@ -0,0 +1,156 @@
+From 0bb508fb3b97e4802ec727fd2af4d608f65dd190 Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Tue, 9 Jun 2026 00:49:15 +0900
+Subject: ntfs: grow index root value before reparent header update
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit 0bb508fb3b97e4802ec727fd2af4d608f65dd190 upstream.
+
+ntfs_ir_reparent() moves the resident index root entries into an index
+block and leaves a small root stub containing the child VCN. That root
+stub can be larger than the existing resident value. For example, an
+empty root with value_length 48 has an index area of 32 bytes, while the
+large-index root stub needs index_length and allocated_size of 40 bytes.
+
+The current code publishes the larger index.index_length and
+index.allocated_size before resizing the resident value. If the resize
+returns -ENOSPC, the recovery path can call ntfs_inode_add_attrlist(),
+which looks attributes up again while the root header says
+allocated_size 40 but the resident value still only provides 32 bytes of
+index area. Lookup-time $INDEX_ROOT validation then correctly rejects
+that transient layout as corrupt.
+
+This reproduces as a generic/013 failure under qemu. In the failing run,
+the transient root had value_len=48, index_size=32, index_length=40, and
+allocated_size=40, and ntfsprogs-plus ntfsck reported "Corrupt index
+root in MFT record 1177".
+
+When the root stub grows, resize the resident value before publishing the
+larger root header. If the resize fails, the old root remains valid for
+recovery lookups. Keep the existing header-before-resize ordering for
+shrink or same-size cases so the resident value never temporarily
+exposes an allocated_size beyond its bounds.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/index.c |   78 ++++++++++++++++++++++++++++++++++----------------------
+ 1 file changed, 48 insertions(+), 30 deletions(-)
+
+--- a/fs/ntfs/index.c
++++ b/fs/ntfs/index.c
+@@ -1176,6 +1176,8 @@ static int ntfs_ir_reparent(struct ntfs_
+       struct index_entry *ie;
+       struct index_block *ib = NULL;
+       s64 new_ib_vcn;
++      u32 index_length;
++      u32 old_value_length;
+       int ix_root_size;
+       int ret = 0;
+@@ -1223,6 +1225,21 @@ retry:
+               goto clear_bmp;
+       }
++      old_value_length = le32_to_cpu(ctx->attr->data.resident.value_length);
++      index_length = le32_to_cpu(ir->index.entries_offset) +
++              sizeof(struct index_entry_header) + sizeof(s64);
++      ix_root_size = offsetof(struct index_root, index) + index_length;
++      /* Grow the resident value before publishing the larger root header. */
++      if (ix_root_size > old_value_length) {
++              ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size);
++              if (ret)
++                      goto resize_failed;
++
++              icx->idx_ni->data_size = ix_root_size;
++              icx->idx_ni->initialized_size = ix_root_size;
++              icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7;
++      }
++
+       ntfs_ir_nill(ir);
+       ie = ntfs_ie_get_first(&ir->index);
+@@ -1231,48 +1248,49 @@ retry:
+       ir->index.flags = LARGE_INDEX;
+       NInoSetIndexAllocPresent(icx->idx_ni);
+-      ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) +
+-                      le16_to_cpu(ie->length));
++      ir->index.index_length = cpu_to_le32(index_length);
+       ir->index.allocated_size = ir->index.index_length;
+-      ix_root_size = sizeof(struct index_root) - sizeof(struct index_header) +
+-              le32_to_cpu(ir->index.allocated_size);
+-      ret  = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size);
+-      if (ret) {
+-              /*
+-               * When there is no space to build a non-resident
+-               * index, we may have to move the root to an extent
+-               */
+-              if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) {
++      if (ix_root_size <= old_value_length) {
++              ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size);
++              if (ret)
++                      goto resize_failed;
++
++              icx->idx_ni->data_size = ix_root_size;
++              icx->idx_ni->initialized_size = ix_root_size;
++              icx->idx_ni->allocated_size = (ix_root_size + 7) & ~7;
++      }
++      ntfs_ie_set_vcn(ie, new_ib_vcn);
++      goto err_out;
++
++resize_failed:
++      /*
++       * When there is no space to build a non-resident
++       * index, we may have to move the root to an extent
++       */
++      if ((ret == -ENOSPC) && (ctx->al_entry || !ntfs_inode_add_attrlist(icx->idx_ni))) {
++              ntfs_attr_put_search_ctx(ctx);
++              ctx = NULL;
++              ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx);
++              if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size -
++                              le32_to_cpu(ctx->attr->data.resident.value_length))) {
++                      if (ntfs_attrlist_update(ctx->base_ntfs_ino ?
++                                               ctx->base_ntfs_ino : ctx->ntfs_ino))
++                              goto clear_bmp;
+                       ntfs_attr_put_search_ctx(ctx);
+                       ctx = NULL;
+-                      ir = ntfs_ir_lookup(icx->idx_ni, icx->name, icx->name_len, &ctx);
+-                      if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size -
+-                                      le32_to_cpu(ctx->attr->data.resident.value_length))) {
+-                              if (ntfs_attrlist_update(ctx->base_ntfs_ino ?
+-                                                       ctx->base_ntfs_ino : ctx->ntfs_ino))
+-                                      goto clear_bmp;
+-                              ntfs_attr_put_search_ctx(ctx);
+-                              ctx = NULL;
+-                              goto retry;
+-                      }
++                      goto retry;
+               }
+-              goto clear_bmp;
+-      } else {
+-              icx->idx_ni->data_size = icx->idx_ni->initialized_size = ix_root_size;
+-              icx->idx_ni->allocated_size = (ix_root_size  + 7) & ~7;
+       }
+-      ntfs_ie_set_vcn(ie, new_ib_vcn);
+-
++clear_bmp:
++      ntfs_ibm_clear(icx, new_ib_vcn);
++      goto err_out;
+ err_out:
+       kvfree(ib);
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+ out:
+       return ret;
+-clear_bmp:
+-      ntfs_ibm_clear(icx, new_ib_vcn);
+-      goto err_out;
+ }
+ /*
diff --git a/queue-7.1/ntfs-make-system-files-immutable-to-prevent-corruption.patch b/queue-7.1/ntfs-make-system-files-immutable-to-prevent-corruption.patch
new file mode 100644 (file)
index 0000000..106104f
--- /dev/null
@@ -0,0 +1,47 @@
+From f72df3a4c33b64de3418ec74d1ad4f028e09d161 Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Thu, 2 Jul 2026 20:36:59 +0900
+Subject: ntfs: make system files immutable to prevent corruption
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit f72df3a4c33b64de3418ec74d1ad4f028e09d161 upstream.
+
+When a system file such as $Bitmap is exposed via show_sys_files and
+written from userspace, the volume is corrupted and, because the cluster
+allocator scans $Bitmap through the same inode's page cache, a write to
+$Bitmap also deadlocks writeback against the folio it already holds locked.
+
+These files are maintained by the driver itself and have no valid reason
+to be written through the file interface. Mark base metadata files
+(mft_no < FILE_first_user) as immutable during inode read so the VFS
+rejects write, mmap, truncate and unlink with -EPERM. Directories are
+skipped so the root and $Extend remain usable. Internal metadata updates
+do not go through the VFS write path and are unaffected.
+
+Fixes: af0db57d4293 ("ntfs: update inode operations")
+Cc: stable@vger.kernel.org
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/inode.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/fs/ntfs/inode.c
++++ b/fs/ntfs/inode.c
+@@ -1184,6 +1184,15 @@ no_data_attr_special_case:
+               vi->i_flags |= S_IMMUTABLE;
+       /*
++       * System files such as $Bitmap and $MFT are maintained by the driver
++       * itself, and writing them from userspace corrupts the volume.
++       * Always make them immutable regardless of the sys_immutable option.
++       * Directories are skipped so the root and $Extend stay usable.
++       */
++      if (ni->mft_no < FILE_first_user && S_ISREG(vi->i_mode))
++              vi->i_flags |= S_IMMUTABLE;
++
++      /*
+        * The number of 512-byte blocks used on disk (for stat). This is in so
+        * far inaccurate as it doesn't account for any named streams or other
+        * special non-resident attributes, but that is how Windows works, too,
diff --git a/queue-7.1/ntfs-not-change-0-byte-data-attribute-to-non-resident.patch b/queue-7.1/ntfs-not-change-0-byte-data-attribute-to-non-resident.patch
new file mode 100644 (file)
index 0000000..4749c45
--- /dev/null
@@ -0,0 +1,58 @@
+From 0aad21570197973af4a1b25b3fb8ed3aeb9e7670 Mon Sep 17 00:00:00 2001
+From: Hyunchul Lee <hyc.lee@gmail.com>
+Date: Thu, 28 May 2026 11:15:35 +0900
+Subject: ntfs: not change 0-byte $DATA attribute to non-resident
+
+From: Hyunchul Lee <hyc.lee@gmail.com>
+
+commit 0aad21570197973af4a1b25b3fb8ed3aeb9e7670 upstream.
+
+When ntfs_resident_attr_resize() cannot grow a resident attribute in
+place, it retries after converting other resident attributes to
+non-resident to free space in the MFT recrord.
+
+Do not select zero-length resident $DATA attributes for this conversion.
+fsck treats 0-byte non-resident $DATA attribute as corruptions.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/attrib.c |    7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/fs/ntfs/attrib.c
++++ b/fs/ntfs/attrib.c
+@@ -4621,10 +4621,12 @@ attr_resize_again:
+       while (!(err = ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx))) {
+               struct inode *tvi;
+               struct attr_record *a;
++              u32 value_len;
+               a = ctx->attr;
+               if (a->non_resident || a->type == AT_ATTRIBUTE_LIST)
+                       continue;
++              value_len = le32_to_cpu(a->data.resident.value_length);
+               if (ntfs_attr_can_be_non_resident(vol, a->type))
+                       continue;
+@@ -4636,6 +4638,8 @@ attr_resize_again:
+               if (le32_to_cpu(a->length) <= (sizeof(struct attr_record) - sizeof(s64)) +
+                               ((a->name_length * sizeof(__le16) + 7) & ~7) + 8)
+                       continue;
++              if (a->type == AT_DATA && !value_len)
++                      continue;
+               if (a->type == AT_DATA)
+                       tvi = ntfs_iget(sb, base_ni->mft_no);
+@@ -4648,8 +4652,7 @@ attr_resize_again:
+                       continue;
+               }
+-              if (ntfs_attr_make_non_resident(NTFS_I(tvi),
+-                  le32_to_cpu(ctx->attr->data.resident.value_length))) {
++              if (ntfs_attr_make_non_resident(NTFS_I(tvi), value_len)) {
+                       iput(tvi);
+                       continue;
+               }
diff --git a/queue-7.1/ntfs-only-alias-volume-upcase-to-default-on-exact-match.patch b/queue-7.1/ntfs-only-alias-volume-upcase-to-default-on-exact-match.patch
new file mode 100644 (file)
index 0000000..d39e0cc
--- /dev/null
@@ -0,0 +1,64 @@
+From 8f4b6e8bda121ae3d4d8e332b789664604d3e9d2 Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Thu, 21 May 2026 19:17:51 +0900
+Subject: ntfs: only alias volume $UpCase to default on exact match
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit 8f4b6e8bda121ae3d4d8e332b789664604d3e9d2 upstream.
+
+load_and_init_upcase() currently aliases vol->upcase to the global
+default upcase whenever the shared prefix matches, and then truncates
+vol->upcase_len to that shorter prefix.  The result is correct only by
+accident: upcase[] accesses in name collation are gated by upcase_len,
+so the prefix-equality alias produces the same fold output as keeping
+the volume's own shorter table.
+
+Still, prefix equality is not equality: the volume table is logically
+distinct from the default and should not be replaced by it unless they
+are byte-for-byte identical.  Use memcmp() to compare the complete table
+in one expression and drop the now-redundant upcase_len rewrite.
+
+No user-visible change is expected for compliant volumes whose $UpCase
+has exactly default_upcase_len entries; shorter volume tables are no
+longer aliased to the default.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/super.c |   12 +++---------
+ 1 file changed, 3 insertions(+), 9 deletions(-)
+
+--- a/fs/ntfs/super.c
++++ b/fs/ntfs/super.c
+@@ -1329,7 +1329,6 @@ static bool load_and_init_upcase(struct
+       u8 *addr;
+       pgoff_t index, max_index;
+       unsigned int size;
+-      int i, max;
+       ntfs_debug("Entering.");
+       /* Read upcase table and setup vol->upcase and vol->upcase_len. */
+@@ -1380,16 +1379,11 @@ read_partial_upcase_page:
+               mutex_unlock(&ntfs_lock);
+               return true;
+       }
+-      max = default_upcase_len;
+-      if (max > vol->upcase_len)
+-              max = vol->upcase_len;
+-      for (i = 0; i < max; i++)
+-              if (vol->upcase[i] != default_upcase[i])
+-                      break;
+-      if (i == max) {
++      if (default_upcase_len == vol->upcase_len &&
++          !memcmp(vol->upcase, default_upcase,
++                  default_upcase_len * sizeof(*default_upcase))) {
+               kvfree(vol->upcase);
+               vol->upcase = default_upcase;
+-              vol->upcase_len = max;
+               ntfs_nr_upcase_users++;
+               mutex_unlock(&ntfs_lock);
+               ntfs_debug("Volume specified $UpCase matches default. Using default.");
diff --git a/queue-7.1/ntfs-reinit-search-context-before-volume-information-lookup.patch b/queue-7.1/ntfs-reinit-search-context-before-volume-information-lookup.patch
new file mode 100644 (file)
index 0000000..72ac151
--- /dev/null
@@ -0,0 +1,40 @@
+From 45dd046ced0f5982a6d64ca449de3a61f5f15669 Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Sat, 30 May 2026 23:35:11 +0900
+Subject: ntfs: reinit search context before volume information lookup
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit 45dd046ced0f5982a6d64ca449de3a61f5f15669 upstream.
+
+On mount the volume inode is searched for $VOLUME_NAME and then, reusing
+the same search context, for $VOLUME_INFORMATION. The $VOLUME_NAME lookup
+is optional and its result is otherwise ignored.
+
+Once lookup-time validation can reject a corrupt $VOLUME_NAME with -EIO,
+the search context is left in an undefined state: ntfs_attr_find()
+documents that on an actual error @ctx->attr is undefined. Continuing the
+$VOLUME_INFORMATION search from that context is not contractually valid.
+
+Reinitialize the search context before the $VOLUME_INFORMATION lookup so
+it always starts from a well-defined state regardless of the
+$VOLUME_NAME lookup outcome.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/super.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/fs/ntfs/super.c
++++ b/fs/ntfs/super.c
+@@ -1537,6 +1537,7 @@ iput_volume_failed:
+                       vol->volume_label = NULL;
+       }
++      ntfs_attr_reinit_search_ctx(ctx);
+       if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0,
+                       ctx) || ctx->attr->non_resident || ctx->attr->flags) {
+               ntfs_attr_put_search_ctx(ctx);
diff --git a/queue-7.1/ntfs-reject-non-resident-records-for-resident-only-attributes.patch b/queue-7.1/ntfs-reject-non-resident-records-for-resident-only-attributes.patch
new file mode 100644 (file)
index 0000000..5ed44bc
--- /dev/null
@@ -0,0 +1,76 @@
+From 097cdfd0a55df5af82c9753833f39a8bfadbcfcb Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Tue, 9 Jun 2026 00:49:14 +0900
+Subject: ntfs: reject non-resident records for resident-only attributes
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit 097cdfd0a55df5af82c9753833f39a8bfadbcfcb upstream.
+
+The shared lookup-time attribute validator rejects non-resident
+$FILE_NAME and $VOLUME_NAME records because their formats require
+resident values and callers handle returned records as resident
+attributes. Other resident-only attribute types still pass through the
+generic non-resident mapping-pairs checks.
+
+That leaves real resident/non-resident union confusion paths. Inode load
+looks up $STANDARD_INFORMATION and then reads data.resident.value_offset
+without checking a->non_resident. ntfs_inode_sync_standard_information()
+does the same when updating the standard information value.
+ntfs_write_volume_flags() also looks up $VOLUME_INFORMATION and reads
+data.resident.value_offset directly. $INDEX_ROOT callers in dir.c and
+index.c depend on the same lookup contract before consuming the resident
+index root value.
+
+Reject non-resident records for all resident-only attribute types in the
+shared validator. Keep the existing $FILE_NAME and $VOLUME_NAME behavior,
+but factor it through a helper and extend it to
+$STANDARD_INFORMATION, $OBJECT_ID, $VOLUME_INFORMATION, $INDEX_ROOT, and
+$EA_INFORMATION. For $OBJECT_ID and $EA_INFORMATION this is contract
+hardening for resident-only formats; this patch only rejects the
+non-resident form and does not add new resident value validation for
+those types.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/attrib.c |   18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+--- a/fs/ntfs/attrib.c
++++ b/fs/ntfs/attrib.c
+@@ -597,6 +597,22 @@ static u32 ntfs_resident_attr_min_value_
+       }
+ }
++static bool ntfs_attr_type_is_resident_only(const __le32 type)
++{
++      switch (type) {
++      case AT_STANDARD_INFORMATION:
++      case AT_FILE_NAME:
++      case AT_OBJECT_ID:
++      case AT_VOLUME_NAME:
++      case AT_VOLUME_INFORMATION:
++      case AT_INDEX_ROOT:
++      case AT_EA_INFORMATION:
++              return true;
++      default:
++              return false;
++      }
++}
++
+ static bool ntfs_file_name_attr_value_is_valid(const u8 *value, const u32 value_length)
+ {
+       const struct file_name_attr *fn;
+@@ -692,7 +708,7 @@ static bool ntfs_attr_value_is_valid(str
+       u32 min_len;
+       if (a->non_resident) {
+-              if (a->type == AT_FILE_NAME || a->type == AT_VOLUME_NAME)
++              if (ntfs_attr_type_is_resident_only(a->type))
+                       goto corrupt;
+               if (!ntfs_non_resident_attr_value_is_valid(a))
+                       goto corrupt;
diff --git a/queue-7.1/ntfs-sanitize-mft-references-returned-from-ntfs_lookup_inode_by_name.patch b/queue-7.1/ntfs-sanitize-mft-references-returned-from-ntfs_lookup_inode_by_name.patch
new file mode 100644 (file)
index 0000000..10ae45d
--- /dev/null
@@ -0,0 +1,93 @@
+From d97a36bae86a9a4021562ded2987f904e6bcb1d7 Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Mon, 6 Jul 2026 12:00:00 +0900
+Subject: ntfs: sanitize MFT references returned from ntfs_lookup_inode_by_name()
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit d97a36bae86a9a4021562ded2987f904e6bcb1d7 upstream.
+
+ntfs_lookup_inode_by_name() returns MFT references read from directory
+index entries on disk. These values are untrusted, but the function can
+currently return an error-marked MFT reference to its callers without
+validating it.
+
+Callers later decode lookup failures with MREF_ERR(). A crafted NTFS image
+can set the MREF error bit while leaving the low bits as an arbitrary
+value, causing callers to consume a bogus pseudo-errno instead of treating
+the lookup result as corrupted on-disk metadata.
+
+Fix this at the source by normalizing every error-marked MFT reference
+returned from ntfs_lookup_inode_by_name() to ERR_MREF(-EIO). Apply this to
+all four directory lookup return paths so every caller gets a validated
+result without needing additional checks or an API change.
+
+This keeps the sanitization in the common lookup helper, which is cleaner
+than duplicating validation in each caller.
+
+Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"")
+Cc: stable@vger.kernel.org
+Reported-by: Hongling Zeng <zenghongling@kylinos.cn>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/dir.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c
+index 4b6bd5f30c65..6fa9ae3377cb 100644
+--- a/fs/ntfs/dir.c
++++ b/fs/ntfs/dir.c
+@@ -23,6 +23,13 @@
+ __le16 I30[5] = { cpu_to_le16('$'), cpu_to_le16('I'),
+               cpu_to_le16('3'),       cpu_to_le16('0'), 0 };
++static inline u64 ntfs_check_mref(u64 mref)
++{
++      if (IS_ERR_MREF(mref))
++              return ERR_MREF(-EIO);
++      return mref;
++}
++
+ /*
+  * ntfs_lookup_inode_by_name - find an inode in a directory given its name
+  * @dir_ni:   ntfs inode of the directory in which to search for the name
+@@ -178,7 +185,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
+                       mref = le64_to_cpu(ie->data.dir.indexed_file);
+                       ntfs_attr_put_search_ctx(ctx);
+                       unmap_mft_record(dir_ni);
+-                      return mref;
++                      return ntfs_check_mref(mref);
+               }
+               /*
+                * For a case insensitive mount, we also perform a case
+@@ -273,7 +280,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
+               if (name) {
+                       ntfs_attr_put_search_ctx(ctx);
+                       unmap_mft_record(dir_ni);
+-                      return name->mref;
++                      return ntfs_check_mref(name->mref);
+               }
+               ntfs_debug("Entry not found.");
+               err = -ENOENT;
+@@ -413,7 +420,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
+                       mref = le64_to_cpu(ie->data.dir.indexed_file);
+                       kfree(kaddr);
+                       iput(ia_vi);
+-                      return mref;
++                      return ntfs_check_mref(mref);
+               }
+               /*
+                * For a case insensitive mount, we also perform a case
+@@ -538,7 +545,7 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname,
+       if (name) {
+               kfree(kaddr);
+               iput(ia_vi);
+-              return name->mref;
++              return ntfs_check_mref(name->mref);
+       }
+       ntfs_debug("Entry not found.");
+       err = -ENOENT;
+-- 
+2.55.0
+
diff --git a/queue-7.1/ntfs-skip-extent-mft-records-in-writeback-to-prevent-deadlock.patch b/queue-7.1/ntfs-skip-extent-mft-records-in-writeback-to-prevent-deadlock.patch
new file mode 100644 (file)
index 0000000..4b9bdc7
--- /dev/null
@@ -0,0 +1,222 @@
+From 76bc14c7097ff678b2b5dbfd4fa33b46897d87ce Mon Sep 17 00:00:00 2001
+From: Hyunchul Lee <hyc.lee@gmail.com>
+Date: Thu, 21 May 2026 14:37:03 +0900
+Subject: ntfs: skip extent mft records in writeback to prevent deadlock
+
+From: Hyunchul Lee <hyc.lee@gmail.com>
+
+commit 76bc14c7097ff678b2b5dbfd4fa33b46897d87ce upstream.
+
+This patch fixes the ABBA deadlock between extent_lock and extent
+mrec_lock triggered by xfstests generic/113, that occurs since the commit
+6994acf33bae ("ntfs: use base mft_no when looking up base inode for
+               extent record").
+
+Path A (inode writeback):
+  VFS writeback
+    -> ntfs_write_inode()
+      -> __ntfs_write_inode()
+        -> mutex_lock(&ni->extent_lock)
+        -> mutex_lock(&tni->mrec_lock)
+
+Path B (MFT folio writeback):
+  VFS writeback of $MFT dirty folios
+    -> ntfs_mft_writepages()
+      -> ntfs_write_mft_block()
+        -> ntfs_may_write_mft_record()
+          -> holds one extent mrec_lock from a previous iteration
+          -> tries to acquire another base inode extent_lock
+
+By removing all extent_lock and extent mrec_lock acquisition from the MFT
+folio writeback path, the ABBA lock ordering is eliminated:
+
+Path A: __ntfs_write_inode(): extent_lock -> mrec_lock
+Path B (removed): ntfs_write_mft_block(): mrec_lock -> extent_lock
+
+Path B is always redundant for extent records because:
+
+1. mark_mft_record_dirty(ext_ni) does NOT dirty the MFT folio.
+   It only sets NInoDirty(ext_ni) and marks the base VFS inode dirty
+   via __mark_inode_dirty(I_DIRTY_DATASYNC), which triggers Path A.
+   Therefore, normal extent modifications never create a situation where
+   the MFT folio is dirty and Path B is not scheduled.
+
+2. The MFT folio only gets dirtied via ntfs_mft_mark_dirty() inside
+   ntfs_mft_record_alloc(). But all identified callers in attrib.c
+   (ntfs_attr_add, ntfs_attr_record_move_away,
+   ntfs_attr_make_non_resident, ntfs_attr_record_resize) follow through
+   with mark_mft_record_dirty(), which triggers Path A to write the
+   complete record.
+
+3. ntfs_evict_big_inode() calls ntfs_commit_inode() before freeing extent
+   inodes, ensuring all dirty extents are flushed via Path A before the
+   base inode leaves the icache.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/mft.c |  129 +---------------------------------------------------------
+ 1 file changed, 4 insertions(+), 125 deletions(-)
+
+--- a/fs/ntfs/mft.c
++++ b/fs/ntfs/mft.c
+@@ -743,23 +743,6 @@ static int ntfs_test_inode_wb(struct ino
+  *
+  * If the mft record is not a FILE record or it is a base mft record, we can
+  * safely write it and return 'true'.
+- *
+- * We now know the mft record is an extent mft record.  We check if the inode
+- * corresponding to its base mft record is in icache. If it is not, we cannot
+- * safely determine the state of the extent inode, so we return 'false'.
+- *
+- * We now have the base inode for the extent mft record.  We check if it has an
+- * ntfs inode for the extent mft record attached. If not, it is safe to write
+- * the extent mft record and we return 'true'.
+- *
+- * If the extent inode is attached, we check if it is dirty. If so, we return
+- * 'false' (letting the standard write_inode path handle it).
+- *
+- * If it is not dirty, we attempt to lock the extent mft record. If the lock
+- * was already taken, it is not safe to write and we return 'false'.
+- *
+- * If we manage to obtain the lock we have exclusive access to the extent mft
+- * record. We set @locked_ni to the now locked ntfs inode and return 'true'.
+  */
+ static bool ntfs_may_write_mft_record(struct ntfs_volume *vol, const u64 mft_no,
+               const struct mft_record *m, struct ntfs_inode **locked_ni,
+@@ -768,8 +751,7 @@ static bool ntfs_may_write_mft_record(st
+       struct super_block *sb = vol->sb;
+       struct inode *mft_vi = vol->mft_ino;
+       struct inode *vi;
+-      struct ntfs_inode *ni, *eni, **extent_nis;
+-      int i;
++      struct ntfs_inode *ni;
+       struct ntfs_attr na = {0};
+       ntfs_debug("Entering for inode 0x%llx.", mft_no);
+@@ -849,100 +831,10 @@ static bool ntfs_may_write_mft_record(st
+                               mft_no);
+               return true;
+       }
+-      /*
+-       * This is an extent mft record.  Check if the inode corresponding to
+-       * its base mft record is in icache and obtain a reference to it if it
+-       * is.
+-       */
+-      na.mft_no = MREF_LE(m->base_mft_record);
+-      na.state = 0;
+-      ntfs_debug("Mft record 0x%llx is an extent record.  Looking for base inode 0x%llx in icache.",
+-                      mft_no, na.mft_no);
+-      if (!na.mft_no) {
+-              /* Balance the below iput(). */
+-              vi = igrab(mft_vi);
+-              WARN_ON(vi != mft_vi);
+-      } else {
+-              vi = find_inode_nowait(sb, na.mft_no, ntfs_test_inode_wb, &na);
+-              if (na.state == NI_BeingDeleted || na.state == NI_BeingCreated)
+-                      return false;
+-      }
+-      if (!vi)
+-              return false;
+-      ntfs_debug("Base inode 0x%llx is in icache.", na.mft_no);
+-      /*
+-       * The base inode is in icache.  Check if it has the extent inode
+-       * corresponding to this extent mft record attached.
+-       */
+-      ni = NTFS_I(vi);
+-      mutex_lock(&ni->extent_lock);
+-      if (ni->nr_extents <= 0) {
+-              /*
+-               * The base inode has no attached extent inodes, write this
+-               * extent mft record.
+-               */
+-              mutex_unlock(&ni->extent_lock);
+-              *ref_vi = vi;
+-              ntfs_debug("Base inode 0x%llx has no attached extent inodes, write the extent record.",
+-                              na.mft_no);
+-              return true;
+-      }
+-      /* Iterate over the attached extent inodes. */
+-      extent_nis = ni->ext.extent_ntfs_inos;
+-      for (eni = NULL, i = 0; i < ni->nr_extents; ++i) {
+-              if (mft_no == extent_nis[i]->mft_no) {
+-                      /*
+-                       * Found the extent inode corresponding to this extent
+-                       * mft record.
+-                       */
+-                      eni = extent_nis[i];
+-                      break;
+-              }
+-      }
+-      /*
+-       * If the extent inode was not attached to the base inode, write this
+-       * extent mft record.
+-       */
+-      if (!eni) {
+-              mutex_unlock(&ni->extent_lock);
+-              *ref_vi = vi;
+-              ntfs_debug("Extent inode 0x%llx is not attached to its base inode 0x%llx, write the extent record.",
+-                              mft_no, na.mft_no);
+-              return true;
+-      }
+-      ntfs_debug("Extent inode 0x%llx is attached to its base inode 0x%llx.",
+-                      mft_no, na.mft_no);
+-      /* Take a reference to the extent ntfs inode. */
+-      atomic_inc(&eni->count);
+-      mutex_unlock(&ni->extent_lock);
+-
+-      /* if extent inode is dirty, write_inode will write it */
+-      if (NInoDirty(eni)) {
+-              atomic_dec(&eni->count);
+-              *ref_vi = vi;
+-              return false;
+-      }
+-
+-      /*
+-       * Found the extent inode coresponding to this extent mft record.
+-       * Try to take the mft record lock.
+-       */
+-      if (unlikely(!mutex_trylock(&eni->mrec_lock))) {
+-              atomic_dec(&eni->count);
+-              *ref_vi = vi;
+-              ntfs_debug("Extent mft record 0x%llx is already locked, do not write it.",
+-                              mft_no);
+-              return false;
+-      }
+-      ntfs_debug("Managed to lock extent mft record 0x%llx, write it.",
+-                      mft_no);
+-      /*
+-       * The write has to occur while we hold the mft record lock so return
+-       * the locked extent ntfs inode.
+-       */
+-      *locked_ni = eni;
+-      return true;
++      ntfs_debug("Mft record 0x%llx is an extent record, skip it.",
++                 mft_no);
++      return false;
+ }
+ static const char *es = "  Leaving inconsistent metadata.  Unmount and run chkdsk.";
+@@ -2792,19 +2684,6 @@ static int ntfs_write_mft_block(struct f
+                       s64 rl_len = 0;
+                       /*
+-                       * Skip $MFT extent mft records and let them being written
+-                       * by writeback to avioid deadlocks. the $MFT runlist
+-                       * lock must be taken before $MFT extent mrec_lock is taken.
+-                       */
+-                      if (tni && tni->nr_extents < 0 &&
+-                              tni->ext.base_ntfs_ino == NTFS_I(vol->mft_ino)) {
+-                              mutex_unlock(&tni->mrec_lock);
+-                              atomic_dec(&tni->count);
+-                              iput(vol->mft_ino);
+-                              continue;
+-                      }
+-
+-                      /*
+                        * The record should be written.  If a locked ntfs
+                        * inode was returned, add it to the array of locked
+                        * ntfs inodes.
diff --git a/queue-7.1/ntfs-update-index-root-allocated-size-before-shrink.patch b/queue-7.1/ntfs-update-index-root-allocated-size-before-shrink.patch
new file mode 100644 (file)
index 0000000..77de265
--- /dev/null
@@ -0,0 +1,72 @@
+From e782ca90ceb798bb1811b214bf814216f11aae6a Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Tue, 9 Jun 2026 00:49:16 +0900
+Subject: ntfs: update index root allocated size before shrink
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit e782ca90ceb798bb1811b214bf814216f11aae6a upstream.
+
+ntfs_ir_truncate() currently shrinks the resident $INDEX_ROOT value first
+and only updates index.allocated_size after re-looking up the attribute.
+During that relookup, the resident value_length can already be smaller
+while index.allocated_size still contains the old larger size.
+
+That leaves a transiently inconsistent $INDEX_ROOT layout and prevents
+lookup-time $INDEX_ROOT validation from being enabled: validation can
+correctly reject allocated_size extending past the newly shrunk resident
+value.
+
+When shrinking, lower index.allocated_size before shrinking value_length.
+If the truncate fails, restore the old allocated_size. Keep the existing
+grow ordering because the old allocated_size remains within the enlarged
+resident value until it is updated after the relookup. The shrink path is
+safe because the new value_length still covers struct index_root, so the
+index.allocated_size field remains present while it is updated first.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/index.c |   18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/fs/ntfs/index.c
++++ b/fs/ntfs/index.c
+@@ -1342,9 +1342,16 @@ out:
+ static int ntfs_ir_truncate(struct ntfs_index_context *icx, int data_size)
+ {
+       int ret;
++      u32 old_allocated_size;
++      bool shrink;
+       ntfs_debug("Entering\n");
++      old_allocated_size = le32_to_cpu(icx->ir->index.allocated_size);
++      shrink = data_size < old_allocated_size;
++      if (shrink)
++              icx->ir->index.allocated_size = cpu_to_le32(data_size);
++
+       /*
+        *  INDEX_ROOT must be resident and its entries can be moved to
+        *  struct index_block, so ENOSPC isn't a real error.
+@@ -1356,9 +1363,14 @@ static int ntfs_ir_truncate(struct ntfs_
+               if (!icx->ir)
+                       return -ENOENT;
+-              icx->ir->index.allocated_size = cpu_to_le32(data_size);
+-      } else if (ret != -ENOSPC)
+-              ntfs_error(icx->idx_ni->vol->sb, "Failed to truncate INDEX_ROOT");
++              if (!shrink)
++                      icx->ir->index.allocated_size = cpu_to_le32(data_size);
++      } else {
++              if (shrink)
++                      icx->ir->index.allocated_size = cpu_to_le32(old_allocated_size);
++              if (ret != -ENOSPC)
++                      ntfs_error(icx->idx_ni->vol->sb, "Failed to truncate INDEX_ROOT");
++      }
+       return ret;
+ }
diff --git a/queue-7.1/ntfs-validate-attribute-values-on-lookup.patch b/queue-7.1/ntfs-validate-attribute-values-on-lookup.patch
new file mode 100644 (file)
index 0000000..ffa1e86
--- /dev/null
@@ -0,0 +1,285 @@
+From d5803e3345dae9c6470bb61869885236276b9a35 Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Sat, 30 May 2026 23:35:09 +0900
+Subject: ntfs: validate attribute values on lookup
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit d5803e3345dae9c6470bb61869885236276b9a35 upstream.
+
+ntfs_attr_find() and ntfs_external_attr_find() check that generic
+resident attribute values fit in their attribute records and that
+fixed-size resident values are large enough. For variable-length resident
+formats, however, the fixed part is not enough: embedded length fields
+can still point callers past the resident value.
+
+A crafted image can set a small resident $FILE_NAME value_length while
+leaving file_name_length large. Callers then trust file_name_length and
+read past the resident value when converting or comparing the name. This
+was reproduced with a crafted image under KASAN as a slab-out-of-bounds
+read from the kmalloc-1k MFT record copy. The stack included
+ntfs_lookup(), ntfs_iget(), ntfs_read_locked_inode(), ntfs_attr_name_get(),
+ntfs_ucstonls(), and utf16s_to_utf8s().
+
+Add a shared attribute value validator and use it before a lookup path
+can return an attribute, including the AT_UNUSED enumeration case where
+callers inspect returned attributes directly. The helper validates
+resident value bounds, minimum resident value sizes, variable-length
+$FILE_NAME fields, and non-resident mapping-pairs metadata that was
+previously checked separately in both lookup paths.
+
+This also preserves the intended resident @val matching semantics in the
+external attribute lookup path. The old duplicated validation block
+overwrote the actual resident value length with the type-specific minimum
+length before comparing @val, so variable-length resident values could
+fail to match even when the bytes were identical. Keep the comparison on
+the actual value length, and make ntfs_attrlist_entry_add() compare
+resident attributes with lowest_vcn zero instead of reading the
+non-resident union member after a successful resident match.
+
+Reject non-resident $FILE_NAME records too: the format requires
+$FILE_NAME to be resident and callers treat returned records as resident.
+
+Cc: stable@vger.kernel.org # v7.1
+Fixes: 6ceb4cc81ef3 ("ntfs: add bound checking to ntfs_attr_find")
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/attrib.c   |  160 ++++++++++++++++++++++++++++++++---------------------
+ fs/ntfs/attrlist.c |   11 ++-
+ 2 files changed, 107 insertions(+), 64 deletions(-)
+
+--- a/fs/ntfs/attrib.c
++++ b/fs/ntfs/attrib.c
+@@ -595,6 +595,97 @@ static u32 ntfs_resident_attr_min_value_
+       }
+ }
++static bool ntfs_file_name_attr_value_is_valid(const u8 *value, const u32 value_length)
++{
++      const struct file_name_attr *fn;
++      u32 file_name_size;
++
++      fn = (const struct file_name_attr *)value;
++      file_name_size = fn->file_name_length * sizeof(__le16);
++
++      return file_name_size <=
++                      value_length - offsetof(struct file_name_attr, file_name);
++}
++
++struct ntfs_resident_attr_value {
++      const u8 *data;
++      u32 len;
++};
++
++static bool ntfs_resident_attr_value_get(const struct attr_record *a,
++                                       struct ntfs_resident_attr_value *value)
++{
++      u32 attr_len;
++      u16 value_offset;
++
++      attr_len = le32_to_cpu(a->length);
++      if (attr_len < offsetof(struct attr_record, data.resident.reserved) +
++                      sizeof(a->data.resident.reserved))
++              return false;
++
++      value->len = le32_to_cpu(a->data.resident.value_length);
++      value_offset = le16_to_cpu(a->data.resident.value_offset);
++
++      if (value->len > attr_len || value_offset > attr_len - value->len)
++              return false;
++
++      value->data = (const u8 *)a + value_offset;
++      return true;
++}
++
++static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
++{
++      u32 attr_len;
++      u32 min_len;
++      u16 mp_offset;
++
++      attr_len = le32_to_cpu(a->length);
++      min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
++                sizeof(a->data.non_resident.initialized_size);
++      if (attr_len < min_len)
++              return false;
++
++      mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
++      return mp_offset >= min_len && mp_offset <= attr_len;
++}
++
++static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol,
++                                   const struct attr_record *a,
++                                   const u64 mft_no)
++{
++      struct ntfs_resident_attr_value value;
++      u32 min_len;
++
++      if (a->non_resident) {
++              if (a->type == AT_FILE_NAME)
++                      goto corrupt;
++              if (!ntfs_non_resident_attr_value_is_valid(a))
++                      goto corrupt;
++              return true;
++      }
++
++      if (!ntfs_resident_attr_value_get(a, &value))
++              goto corrupt;
++
++      min_len = ntfs_resident_attr_min_value_length(a->type);
++      if (min_len && value.len < min_len)
++              goto corrupt;
++
++      switch (a->type) {
++      case AT_FILE_NAME:
++              if (!ntfs_file_name_attr_value_is_valid(value.data, value.len))
++                      goto corrupt;
++              break;
++      }
++      return true;
++
++corrupt:
++      ntfs_error(vol->sb,
++                 "Corrupt %#x attribute in MFT record %llu\n",
++                 le32_to_cpu(a->type), mft_no);
++      return false;
++}
++
+ /*
+  * ntfs_attr_find - find (next) attribute in mft record
+  * @type:     attribute type to find
+@@ -705,8 +796,11 @@ static int ntfs_attr_find(const __le32 t
+                       }
+               }
+-              if (type == AT_UNUSED)
++              if (type == AT_UNUSED) {
++                      if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no))
++                              break;
+                       return 0;
++              }
+               if (a->type != type)
+                       continue;
+               /*
+@@ -747,37 +841,8 @@ static int ntfs_attr_find(const __le32 t
+                       }
+               }
+-               /* Validate attribute's value offset/length */
+-              if (!a->non_resident) {
+-                      u32 min_len;
+-                      u32 value_length = le32_to_cpu(a->data.resident.value_length);
+-                      u16 value_offset = le16_to_cpu(a->data.resident.value_offset);
+-
+-                      if (value_length > le32_to_cpu(a->length) ||
+-                          value_offset > le32_to_cpu(a->length) - value_length)
+-                              break;
+-
+-                      min_len = ntfs_resident_attr_min_value_length(a->type);
+-                      if (min_len && value_length < min_len) {
+-                              ntfs_error(vol->sb,
+-                                         "Too small %#x resident attribute value in MFT record %lld\n",
+-                                         le32_to_cpu(a->type), (long long)ctx->ntfs_ino->mft_no);
+-                              break;
+-                      }
+-              } else {
+-                      u32 min_len;
+-                      u16 mp_offset;
+-
+-                      min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
+-                                sizeof(a->data.non_resident.initialized_size);
+-                      if (le32_to_cpu(a->length) < min_len)
+-                              break;
+-
+-                      mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
+-                      if (mp_offset < min_len ||
+-                          mp_offset > le32_to_cpu(a->length))
+-                              break;
+-              }
++              if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no))
++                      break;
+               /*
+                * The names match or @name not present and attribute is
+@@ -1299,22 +1364,8 @@ do_next_attr_loop:
+               ctx->attr = a;
+-              if (a->non_resident) {
+-                      u32 min_len;
+-                      u16 mp_offset;
+-
+-                      min_len = offsetof(struct attr_record,
+-                                         data.non_resident.initialized_size) +
+-                                sizeof(a->data.non_resident.initialized_size);
+-
+-                      if (le32_to_cpu(a->length) < min_len)
+-                              break;
+-
+-                      mp_offset =
+-                              le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
+-                      if (mp_offset < min_len || mp_offset > attr_len)
+-                              break;
+-              }
++              if (!ntfs_attr_value_is_valid(vol, a, ctx->ntfs_ino->mft_no))
++                      break;
+               /*
+                * If no @val specified or @val specified and it matches, we
+@@ -1326,19 +1377,6 @@ do_next_attr_loop:
+                       u32 value_length = le32_to_cpu(a->data.resident.value_length);
+                       u16 value_offset = le16_to_cpu(a->data.resident.value_offset);
+-                      if (attr_len < offsetof(struct attr_record, data.resident.reserved) +
+-                                      sizeof(a->data.resident.reserved))
+-                              break;
+-                      if (value_length > attr_len || value_offset > attr_len - value_length)
+-                              break;
+-
+-                      value_length = ntfs_resident_attr_min_value_length(a->type);
+-                      if (value_length && le32_to_cpu(a->data.resident.value_length) <
+-                          value_length) {
+-                              pr_err("Too small resident attribute value in MFT record %lld, type %#x\n",
+-                                     (long long)ctx->ntfs_ino->mft_no, a->type);
+-                              break;
+-                      }
+                       if (value_length == val_len &&
+                           !memcmp((u8 *)a + value_offset, val, val_len)) {
+ attr_found:
+--- a/fs/ntfs/attrlist.c
++++ b/fs/ntfs/attrlist.c
+@@ -118,6 +118,7 @@ int ntfs_attrlist_entry_add(struct ntfs_
+       int entry_len, entry_offset, err;
+       struct mft_record *ni_mrec;
+       u8 *old_al;
++      __le64 lowest_vcn;
+       if (!ni || !attr) {
+               ntfs_debug("Invalid arguments.\n");
+@@ -158,17 +159,21 @@ int ntfs_attrlist_entry_add(struct ntfs_
+               ntfs_error(ni->vol->sb, "Failed to get search context");
+               goto err_out;
+       }
++      if (attr->non_resident)
++              lowest_vcn = attr->data.non_resident.lowest_vcn;
++      else
++              lowest_vcn = 0;
+       err = ntfs_attr_lookup(attr->type, (attr->name_length) ? (__le16 *)
+                       ((u8 *)attr + le16_to_cpu(attr->name_offset)) :
+                       AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
+-                      (attr->non_resident) ? le64_to_cpu(attr->data.non_resident.lowest_vcn) :
+-                      0, (attr->non_resident) ? NULL : ((u8 *)attr +
++                      le64_to_cpu(lowest_vcn),
++                      (attr->non_resident) ? NULL : ((u8 *)attr +
+                       le16_to_cpu(attr->data.resident.value_offset)), (attr->non_resident) ?
+                       0 : le32_to_cpu(attr->data.resident.value_length), ctx);
+       if (!err) {
+               /* Found some extent, check it to be before new extent. */
+-              if (ctx->al_entry->lowest_vcn == attr->data.non_resident.lowest_vcn) {
++              if (ctx->al_entry->lowest_vcn == lowest_vcn) {
+                       err = -EEXIST;
+                       ntfs_debug("Such attribute already present in the attribute list.\n");
+                       ntfs_attr_put_search_ctx(ctx);
diff --git a/queue-7.1/ntfs-validate-index-block-header-more-strictly.patch b/queue-7.1/ntfs-validate-index-block-header-more-strictly.patch
new file mode 100644 (file)
index 0000000..507747d
--- /dev/null
@@ -0,0 +1,246 @@
+From 14bc34fe948523dc2b0174691f9af9e74eb4f3fd Mon Sep 17 00:00:00 2001
+From: Hyunchul Lee <hyc.lee@gmail.com>
+Date: Sat, 23 May 2026 13:14:20 +0900
+Subject: ntfs: validate index block header more strictly
+
+From: Hyunchul Lee <hyc.lee@gmail.com>
+
+commit 14bc34fe948523dc2b0174691f9af9e74eb4f3fd upstream.
+
+Modify ntfs_index_block_inconsisent() to perform stricter validation of
+INDEX_HEADER geometry in INDX blocks, and update
+ntfs_lookup_inode_by_name() to use that function to validate INDX
+blocks.
+
+Cc: stable@vger.kernel.org # v7.1
+Tested-by: woot000 <woot000@woot000.com>
+Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/dir.c   |   38 +++-----------------
+ fs/ntfs/index.c |  103 +++++++++++++++++++++++++++++++++++++++-----------------
+ fs/ntfs/index.h |    3 +
+ 3 files changed, 82 insertions(+), 62 deletions(-)
+
+--- a/fs/ntfs/dir.c
++++ b/fs/ntfs/dir.c
+@@ -342,43 +342,19 @@ fast_descend_into_child_node:
+                       dir_ni->mft_no);
+               goto unm_err_out;
+       }
+-      /* Catch multi sector transfer fixup errors. */
+-      if (unlikely(!ntfs_is_indx_record(ia->magic))) {
+-              ntfs_error(sb,
+-                      "Directory index record with vcn 0x%llx is corrupt.  Corrupt inode 0x%llx.  Run chkdsk.",
+-                      vcn, dir_ni->mft_no);
+-              goto unm_err_out;
+-      }
+-      if (le64_to_cpu(ia->index_block_vcn) != vcn) {
+-              ntfs_error(sb,
+-                      "Actual VCN (0x%llx) of index buffer is different from expected VCN (0x%llx). Directory inode 0x%llx is corrupt or driver bug.",
+-                      le64_to_cpu(ia->index_block_vcn),
+-                      vcn, dir_ni->mft_no);
+-              goto unm_err_out;
+-      }
+-      if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
+-                      dir_ni->itype.index.block_size) {
+-              ntfs_error(sb,
+-                      "Index buffer (VCN 0x%llx) of directory inode 0x%llx has a size (%u) differing from the directory specified size (%u). Directory inode is corrupt or driver bug.",
+-                      vcn, dir_ni->mft_no,
+-                      le32_to_cpu(ia->index.allocated_size) + 0x18,
+-                      dir_ni->itype.index.block_size);
+-              goto unm_err_out;
+-      }
+       index_end = (u8 *)ia + dir_ni->itype.index.block_size;
+       if (index_end > kaddr + PAGE_SIZE) {
+               ntfs_error(sb,
+-                      "Index buffer (VCN 0x%llx) of directory inode 0x%llx crosses page boundary. Impossible! Cannot access! This is probably a bug in the driver.",
+-                      vcn, dir_ni->mft_no);
++                         "Index buffer (VCN 0x%llx) of directory inode 0x%llx crosses page boundary. Impossible! Cannot access! This is probably a bug in the driver.",
++                         vcn, dir_ni->mft_no);
+               goto unm_err_out;
+       }
+-      index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length);
+-      if (index_end > (u8 *)ia + dir_ni->itype.index.block_size) {
+-              ntfs_error(sb,
+-                      "Size of index buffer (VCN 0x%llx) of directory inode 0x%llx exceeds maximum size.",
+-                      vcn, dir_ni->mft_no);
++      err = ntfs_index_block_inconsistent(vol, ia,
++                                          dir_ni->itype.index.block_size,
++                                          vcn, dir_ni->mft_no);
++      if (err)
+               goto unm_err_out;
+-      }
++      index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length);
+       /* The first index entry. */
+       ie = (struct index_entry *)((u8 *)&ia->index +
+                       le32_to_cpu(ia->index.entries_offset));
+--- a/fs/ntfs/index.c
++++ b/fs/ntfs/index.c
+@@ -306,6 +306,55 @@ static int ntfs_ie_end(struct index_entr
+       return ie->flags & INDEX_ENTRY_END || !ie->length;
+ }
++static int ntfs_index_header_inconsistent(struct ntfs_volume *vol,
++                                        const struct index_header *ih,
++                                        u32 bytes_available, u64 inum)
++{
++      u32 entries_offset, index_length, allocated_size;
++
++      if (bytes_available < sizeof(struct index_header)) {
++              ntfs_error(vol->sb,
++                         "index block in inode %llu is smaller than an index header.",
++                         (unsigned long long)inum);
++              return -EIO;
++      }
++
++      entries_offset = le32_to_cpu(ih->entries_offset);
++      index_length = le32_to_cpu(ih->index_length);
++      allocated_size = le32_to_cpu(ih->allocated_size);
++
++      if (entries_offset < sizeof(struct index_header) ||
++          entries_offset > bytes_available) {
++              ntfs_error(vol->sb,
++                         "Invalid index entry offset in inode %llu.",
++                         (unsigned long long)inum);
++              return -EIO;
++      }
++
++      if (index_length <= entries_offset) {
++              ntfs_error(vol->sb,
++                         "No space for index entries in inode %llu.",
++                         (unsigned long long)inum);
++              return -EIO;
++      }
++
++      if (allocated_size < index_length) {
++              ntfs_error(vol->sb,
++                         "Index entries overflow in inode %llu.",
++                         (unsigned long long)inum);
++              return -EIO;
++      }
++
++      if (allocated_size > bytes_available || index_length > bytes_available) {
++              ntfs_error(vol->sb,
++                         "Index entries in inode %llu exceed the available buffer.",
++                         (unsigned long long)inum);
++              return -EIO;
++      }
++
++      return 0;
++}
++
+ /*
+  *  Find the last entry in the index block
+  */
+@@ -440,7 +489,7 @@ static struct index_entry *ntfs_ie_dup_n
+  * The size of block is assumed to have been checked to be what is
+  * defined in the index root.
+  *
+- * Returns 0 if no error was found -1 otherwise (with errno unchanged)
++ * Returns 0 if no error was found, -EIO otherwise
+  *
+  * |<--->|  offsetof(struct index_block, index)
+  * |     |<--->|  sizeof(struct index_header)
+@@ -455,21 +504,20 @@ static struct index_entry *ntfs_ie_dup_n
+  *
+  * size(struct index_header) <= ent_offset < ind_length <= alloc_size < bk_size
+  */
+-static int ntfs_index_block_inconsistent(struct ntfs_index_context *icx,
+-              struct index_block *ib, s64 vcn)
++int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
++                                const struct index_block *ib,
++                                u32 block_size, s64 vcn, u64 inum)
+ {
+       u32 ib_size = (unsigned int)le32_to_cpu(ib->index.allocated_size) +
+               offsetof(struct index_block, index);
+-      struct super_block *sb = icx->idx_ni->vol->sb;
+-      unsigned long long inum = icx->idx_ni->mft_no;
++      struct super_block *sb = vol->sb;
+       ntfs_debug("Entering\n");
+       if (!ntfs_is_indx_record(ib->magic)) {
+-
+               ntfs_error(sb, "Corrupt index block signature: vcn %lld inode %llu\n",
+-                              vcn, (unsigned long long)icx->idx_ni->mft_no);
+-              return -1;
++                         vcn, (unsigned long long)inum);
++              return -EIO;
+       }
+       if (le64_to_cpu(ib->index_block_vcn) != vcn) {
+@@ -477,31 +525,22 @@ static int ntfs_index_block_inconsistent
+                       "Corrupt index block: s64 (%lld) is different from expected s64 (%lld) in inode %llu\n",
+                       (long long)le64_to_cpu(ib->index_block_vcn),
+                       vcn, inum);
+-              return -1;
++              return -EIO;
+       }
+-      if (ib_size != icx->block_size) {
++      if (ib_size != block_size) {
+               ntfs_error(sb,
+-                      "Corrupt index block : s64 (%lld) of inode %llu has a size (%u) differing from the index specified size (%u)\n",
+-                      vcn, inum, ib_size, icx->block_size);
+-              return -1;
+-      }
+-
+-      if (le32_to_cpu(ib->index.entries_offset) < sizeof(struct index_header)) {
+-              ntfs_error(sb, "Invalid index entry offset in inode %lld\n", inum);
+-              return -1;
+-      }
+-      if (le32_to_cpu(ib->index.index_length) <=
+-          le32_to_cpu(ib->index.entries_offset)) {
+-              ntfs_error(sb, "No space for index entries in inode %lld\n", inum);
+-              return -1;
+-      }
+-      if (le32_to_cpu(ib->index.allocated_size) <
+-          le32_to_cpu(ib->index.index_length)) {
+-              ntfs_error(sb, "Index entries overflow in inode %lld\n", inum);
+-              return -1;
++                         "Corrupt index block : s64 (%lld) of inode %llu has a size (%u) differing from the index specified size (%u)\n",
++                         vcn, inum, ib_size, block_size);
++              return -EIO;
+       }
++      if (ntfs_index_header_inconsistent(vol, &ib->index,
++                                         block_size -
++                                         offsetof(struct index_block, index),
++                                         inum))
++              return -EIO;
++
+       return 0;
+ }
+@@ -668,12 +707,14 @@ static int ntfs_ib_read(struct ntfs_inde
+               else
+                       ntfs_error(icx->idx_ni->vol->sb,
+                               "Failed to read full index block at %lld\n", pos);
+-              return -1;
++              return -EIO;
+       }
+       post_read_mst_fixup((struct ntfs_record *)((u8 *)dst), icx->block_size);
+-      if (ntfs_index_block_inconsistent(icx, dst, vcn))
+-              return -1;
++      if (ntfs_index_block_inconsistent(icx->idx_ni->vol, dst,
++                                        icx->block_size, vcn,
++                                        icx->idx_ni->mft_no))
++              return -EIO;
+       return 0;
+ }
+--- a/fs/ntfs/index.h
++++ b/fs/ntfs/index.h
+@@ -89,6 +89,9 @@ struct ntfs_index_context {
+       bool sync_write;
+ };
++int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
++                                const struct index_block *ib,
++                                u32 block_size, s64 vcn, u64 inum);
+ int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, struct ntfs_volume *vol,
+               const struct index_entry *ie, __le32 collation_rule, u64 inum);
+ struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, __le16 *name,
diff --git a/queue-7.1/ntfs-validate-index-entries-on-reading.patch b/queue-7.1/ntfs-validate-index-entries-on-reading.patch
new file mode 100644 (file)
index 0000000..084906d
--- /dev/null
@@ -0,0 +1,263 @@
+From 2221b691d7b2e17f08153f95848dacaa5d87e21d Mon Sep 17 00:00:00 2001
+From: Hyunchul Lee <hyc.lee@gmail.com>
+Date: Sat, 23 May 2026 13:14:22 +0900
+Subject: ntfs: validate index entries on reading
+
+From: Hyunchul Lee <hyc.lee@gmail.com>
+
+commit 2221b691d7b2e17f08153f95848dacaa5d87e21d upstream.
+
+Validate index entries immediately after reading an index root or index
+block from disk. This eliminates repeated checks in lookup and readdir,
+and reduce the risk of missing checks in those paths.
+
+Cc: stable@vger.kernel.org # v7.1
+Tested-by: woot000 <woot000@woot000.com>
+Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/dir.c   |   28 +-----------------
+ fs/ntfs/index.c |   86 ++++++++++++++++++++++++++++++--------------------------
+ fs/ntfs/index.h |    8 +++--
+ fs/ntfs/inode.c |    8 +++--
+ 4 files changed, 61 insertions(+), 69 deletions(-)
+
+--- a/fs/ntfs/dir.c
++++ b/fs/ntfs/dir.c
+@@ -135,10 +135,6 @@ u64 ntfs_lookup_inode_by_name(struct ntf
+               /* Key length should not be zero if it is not last entry. */
+               if (!ie->key_length)
+                       goto dir_err_out;
+-              /* Check the consistency of an index entry */
+-              if (ntfs_index_entry_inconsistent(NULL, vol, ie, COLLATION_FILE_NAME,
+-                              dir_ni->mft_no))
+-                      goto dir_err_out;
+               /*
+                * We perform a case sensitive comparison and if that matches
+                * we are done and return the mft reference of the inode (i.e.
+@@ -351,7 +347,8 @@ fast_descend_into_child_node:
+       }
+       err = ntfs_index_block_inconsistent(vol, ia,
+                                           dir_ni->itype.index.block_size,
+-                                          vcn, dir_ni->mft_no);
++                                          vcn, COLLATION_FILE_NAME,
++                                          dir_ni->mft_no);
+       if (err)
+               goto unm_err_out;
+       index_end = (u8 *)&ia->index + le32_to_cpu(ia->index.index_length);
+@@ -364,15 +361,6 @@ fast_descend_into_child_node:
+        * reach the last entry.
+        */
+       for (;; ie = (struct index_entry *)((u8 *)ie + le16_to_cpu(ie->length))) {
+-              /* Bounds checks. */
+-              if ((u8 *)ie < (u8 *)ia ||
+-                  (u8 *)ie + sizeof(struct index_entry_header) > index_end ||
+-                  (u8 *)ie + sizeof(struct index_entry_header) + le16_to_cpu(ie->key_length) >
+-                              index_end || (u8 *)ie + le16_to_cpu(ie->length) > index_end) {
+-                      ntfs_error(sb, "Index entry out of bounds in directory inode 0x%llx.",
+-                                      dir_ni->mft_no);
+-                      goto unm_err_out;
+-              }
+               /*
+                * The last entry cannot contain a name. It can however contain
+                * a pointer to a child node in the B+tree so we just break out.
+@@ -382,10 +370,6 @@ fast_descend_into_child_node:
+               /* Key length should not be zero if it is not last entry. */
+               if (!ie->key_length)
+                       goto unm_err_out;
+-              /* Check the consistency of an index entry */
+-              if (ntfs_index_entry_inconsistent(NULL, vol, ie, COLLATION_FILE_NAME,
+-                              dir_ni->mft_no))
+-                      goto unm_err_out;
+               /*
+                * We perform a case sensitive comparison and if that matches
+                * we are done and return the mft reference of the inode (i.e.
+@@ -868,6 +852,7 @@ static int ntfs_readdir(struct file *fil
+               ictx->vcn_size_bits = vol->cluster_size_bits;
+       else
+               ictx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
++      ictx->cr = ir->collation_rule;
+       /* The first index entry. */
+       next = (struct index_entry *)((u8 *)&ir->index +
+@@ -905,13 +890,6 @@ static int ntfs_readdir(struct file *fil
+               if (!next)
+                       break;
+ nextdir:
+-              /* Check the consistency of an index entry */
+-              if (ntfs_index_entry_inconsistent(ictx, vol, next, COLLATION_FILE_NAME,
+-                                      ndir->mft_no)) {
+-                      err = -EIO;
+-                      goto out;
+-              }
+-
+               if (ie_pos < actor->pos) {
+                       ie_pos += le16_to_cpu(next->length);
+                       continue;
+--- a/fs/ntfs/index.c
++++ b/fs/ntfs/index.c
+@@ -28,41 +28,10 @@
+  * length must have been checked beforehand to not overflow from the
+  * index record.
+  */
+-int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx,
+-              struct ntfs_volume *vol, const struct index_entry *ie,
+-              __le32 collation_rule, u64 inum)
+-{
+-      if (icx) {
+-              struct index_header *ih;
+-              u8 *ie_start, *ie_end;
+-
+-              if (icx->is_in_root)
+-                      ih = &icx->ir->index;
+-              else
+-                      ih = &icx->ib->index;
+-
+-              if ((le32_to_cpu(ih->index_length) > le32_to_cpu(ih->allocated_size)) ||
+-                              (le32_to_cpu(ih->index_length) > icx->block_size)) {
+-                      ntfs_error(vol->sb, "%s Index entry(0x%p)'s length is too big.",
+-                                      icx->is_in_root ? "Index root" : "Index block",
+-                                      (u8 *)icx->entry);
+-                      return -EINVAL;
+-              }
+-
+-              ie_start = (u8 *)ih + le32_to_cpu(ih->entries_offset);
+-              ie_end = (u8 *)ih + le32_to_cpu(ih->index_length);
+-
+-              if (ie_start > (u8 *)ie ||
+-                  ie_end <= (u8 *)ie + le16_to_cpu(ie->length) ||
+-                  le16_to_cpu(ie->length) > le32_to_cpu(ih->allocated_size) ||
+-                  le16_to_cpu(ie->length) > icx->block_size) {
+-                      ntfs_error(vol->sb, "Index entry(0x%p) is out of range from %s",
+-                                      (u8 *)icx->entry,
+-                                      icx->is_in_root ? "index root" : "index block");
+-                      return -EIO;
+-              }
+-      }
+-
++static int ntfs_index_entry_inconsistent(const struct ntfs_volume *vol,
++                                       const struct index_entry *ie,
++                                       __le32 collation_rule, u64 inum)
++{
+       if (ie->key_length &&
+           ((le16_to_cpu(ie->key_length) + offsetof(struct index_entry, key)) >
+            le16_to_cpu(ie->length))) {
+@@ -355,6 +324,44 @@ static int ntfs_index_header_inconsisten
+       return 0;
+ }
++int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol,
++                                  const struct index_header *ih,
++                                  __le32 collation_rule, u64 inum)
++{
++      struct index_entry *ie;
++      u8 *index_end = (u8 *)ih + le32_to_cpu(ih->index_length);
++
++      for (ie = ntfs_ie_get_first((struct index_header *)ih);
++            ; ie = ntfs_ie_get_next(ie)) {
++              if ((u8 *)ie + sizeof(struct index_entry_header) > index_end ||
++                  (u8 *)ie + le16_to_cpu(ie->length) > index_end) {
++                      ntfs_error(vol->sb,
++                                 "Index entry out of bounds in inode %llu.",
++                                 (unsigned long long)inum);
++                      return -EIO;
++              }
++
++              if (le16_to_cpu(ie->length) < sizeof(struct index_entry_header)) {
++                      ntfs_error(vol->sb,
++                                 "Index etnry too small in inode %llu.",
++                                 inum);
++                      return -EIO;
++              }
++
++              if (ntfs_ie_end(ie))
++                      break;
++
++              if (!ie->key_length)
++                      return -EIO;
++
++              if (ntfs_index_entry_inconsistent(vol, ie,
++                                                collation_rule, inum))
++                      return -EIO;
++      }
++
++      return 0;
++}
++
+ /*
+  *  Find the last entry in the index block
+  */
+@@ -506,7 +513,8 @@ static struct index_entry *ntfs_ie_dup_n
+  */
+ int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
+                                 const struct index_block *ib,
+-                                u32 block_size, s64 vcn, u64 inum)
++                                u32 block_size, s64 vcn, __le32 cr,
++                                u64 inum)
+ {
+       u32 ib_size = (unsigned int)le32_to_cpu(ib->index.allocated_size) +
+               offsetof(struct index_block, index);
+@@ -540,7 +548,8 @@ int ntfs_index_block_inconsistent(struct
+                                          offsetof(struct index_block, index),
+                                          inum))
+               return -EIO;
+-
++      if (ntfs_index_entries_inconsistent(vol, &ib->index, cr, inum))
++              return -EIO;
+       return 0;
+ }
+@@ -730,10 +739,9 @@ static int ntfs_ib_read(struct ntfs_inde
+       post_read_mst_fixup((struct ntfs_record *)((u8 *)dst), icx->block_size);
+       if (ntfs_index_block_inconsistent(icx->idx_ni->vol, dst,
+-                                        icx->block_size, vcn,
++                                        icx->block_size, vcn, icx->cr,
+                                         icx->idx_ni->mft_no))
+               return -EIO;
+-
+       return 0;
+ }
+--- a/fs/ntfs/index.h
++++ b/fs/ntfs/index.h
+@@ -94,9 +94,11 @@ int ntfs_index_root_inconsistent(struct
+                                const struct index_root *ir, u64 inum);
+ int ntfs_index_block_inconsistent(struct ntfs_volume *vol,
+                                 const struct index_block *ib,
+-                                u32 block_size, s64 vcn, u64 inum);
+-int ntfs_index_entry_inconsistent(struct ntfs_index_context *icx, struct ntfs_volume *vol,
+-              const struct index_entry *ie, __le32 collation_rule, u64 inum);
++                                u32 block_size, s64 vcn,
++                                __le32 cr, u64 inum);
++int ntfs_index_entries_inconsistent(const struct ntfs_volume *vol,
++                                  const struct index_header *ih,
++                                  __le32 collation_rule, u64 inum);
+ struct ntfs_index_context *ntfs_index_ctx_get(struct ntfs_inode *ni, __le16 *name,
+               u32 name_len);
+ void ntfs_index_ctx_put(struct ntfs_index_context *ictx);
+--- a/fs/ntfs/inode.c
++++ b/fs/ntfs/inode.c
+@@ -945,7 +945,9 @@ view_index_meta:
+               }
+               ir = (struct index_root *)((u8 *)a +
+                               le16_to_cpu(a->data.resident.value_offset));
+-              if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no)) {
++              if (ntfs_index_root_inconsistent(ni->vol, a, ir, ni->mft_no) ||
++                  ntfs_index_entries_inconsistent(ni->vol, &ir->index,
++                                                  ir->collation_rule, ni->mft_no)) {
+                       ntfs_error(vi->i_sb, "Directory index is corrupt.");
+                       goto unm_err_out;
+               }
+@@ -1538,7 +1540,9 @@ static int ntfs_read_locked_index_inode(
+       }
+       ir = (struct index_root *)((u8 *)a + le16_to_cpu(a->data.resident.value_offset));
+-      if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no)) {
++      if (ntfs_index_root_inconsistent(vol, a, ir, ni->mft_no) ||
++          ntfs_index_entries_inconsistent(vol, &ir->index,
++                                          ir->collation_rule, ni->mft_no)) {
+               ntfs_error(vi->i_sb, "Index is corrupt.");
+               goto unm_err_out;
+       }
diff --git a/queue-7.1/ntfs-validate-resident-index-root-values-on-lookup.patch b/queue-7.1/ntfs-validate-resident-index-root-values-on-lookup.patch
new file mode 100644 (file)
index 0000000..dec1742
--- /dev/null
@@ -0,0 +1,94 @@
+From fcf5bf0e8570798970e3ae8c95d04765ba2c5b97 Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Tue, 9 Jun 2026 00:49:17 +0900
+Subject: ntfs: validate resident index root values on lookup
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit fcf5bf0e8570798970e3ae8c95d04765ba2c5b97 upstream.
+
+Resident $INDEX_ROOT values carry index header fields that callers
+consume after lookup. Some callers already validate parts of the layout
+before walking entries, but those checks are scattered and do not cover
+all root header invariants, such as entries_offset alignment and lower
+bound, index_length, and allocated_size consistency.
+
+The resident root resize paths now keep these header fields consistent
+while the value size changes: ntfs_ir_truncate() lowers
+index.allocated_size before shrinking the resident value, and
+ntfs_ir_reparent() grows the resident value before publishing a larger
+root header. Lookup-time validation can therefore cover these invariants
+without tripping over the driver's own resize paths.
+
+Add $INDEX_ROOT to the minimum resident value size table and validate the
+resident index header fields before returning the attribute from lookup.
+Require 8-byte aligned index header fields, a sane entries_offset, an
+index_length within allocated_size, allocated_size within the resident
+value, and enough entry space for at least an index entry header.
+
+The shared validator already rejects non-resident records for
+resident-only attribute types, including $INDEX_ROOT.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/attrib.c |   31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+--- a/fs/ntfs/attrib.c
++++ b/fs/ntfs/attrib.c
+@@ -588,6 +588,8 @@ static u32 ntfs_resident_attr_min_value_
+                       sizeof(__le16) * 1;
+       case AT_VOLUME_INFORMATION:
+               return sizeof(struct volume_information);
++      case AT_INDEX_ROOT:
++              return sizeof(struct index_root);
+       case AT_EA_INFORMATION:
+               return sizeof(struct ea_information);
+       default:
+@@ -615,6 +617,31 @@ static bool ntfs_volume_name_attr_value_
+       return value_length <= NTFS_MAX_LABEL_LEN * sizeof(__le16);
+ }
++static bool ntfs_index_root_attr_value_is_valid(const u8 *value, const u32 value_length)
++{
++      const struct index_root *ir;
++      u32 index_size;
++      u32 entries_offset;
++      u32 index_length;
++      u32 allocated_size;
++
++      ir = (const struct index_root *)value;
++      index_size = value_length - offsetof(struct index_root, index);
++      entries_offset = le32_to_cpu(ir->index.entries_offset);
++      index_length = le32_to_cpu(ir->index.index_length);
++      allocated_size = le32_to_cpu(ir->index.allocated_size);
++
++      if ((entries_offset | index_length | allocated_size) & 7 ||
++          entries_offset < sizeof(struct index_header) ||
++          entries_offset > index_length ||
++          index_length > allocated_size ||
++          allocated_size > index_size ||
++          index_length - entries_offset < sizeof(struct index_entry_header))
++              return false;
++
++      return true;
++}
++
+ struct ntfs_resident_attr_value {
+       const u8 *data;
+       u32 len;
+@@ -688,6 +715,10 @@ static bool ntfs_attr_value_is_valid(str
+               if (!ntfs_volume_name_attr_value_is_valid(value.len))
+                       goto corrupt;
+               break;
++      case AT_INDEX_ROOT:
++              if (!ntfs_index_root_attr_value_is_valid(value.data, value.len))
++                      goto corrupt;
++              break;
+       }
+       return true;
diff --git a/queue-7.1/ntfs-validate-resident-volume-name-values-on-lookup.patch b/queue-7.1/ntfs-validate-resident-volume-name-values-on-lookup.patch
new file mode 100644 (file)
index 0000000..246abd9
--- /dev/null
@@ -0,0 +1,69 @@
+From b3f6cd1d54aa279cc4f47aa27939ebe517a2c390 Mon Sep 17 00:00:00 2001
+From: DaeMyung Kang <charsyam@gmail.com>
+Date: Sat, 30 May 2026 23:35:12 +0900
+Subject: ntfs: validate resident volume name values on lookup
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+commit b3f6cd1d54aa279cc4f47aa27939ebe517a2c390 upstream.
+
+The shared lookup-time attribute validator now has a safe caller path for
+$VOLUME_NAME corruption: ntfs_write_volume_label() no longer treats
+lookup errors as an absent label, and the mount path reinitializes its
+search context before continuing to $VOLUME_INFORMATION.
+
+Add $VOLUME_NAME-specific resident value validation. A volume name is
+stored as a UTF-16LE string, so reject odd byte lengths, and reject
+values longer than the NTFS volume label limit. Empty labels remain
+valid.
+
+Also reject non-resident $VOLUME_NAME records. $VOLUME_NAME is required
+to be resident, like $FILE_NAME; a crafted non-resident record would
+otherwise pass lookup and ntfs_write_volume_label() would remove it as if
+it were a normal resident attribute.
+
+Cc: stable@vger.kernel.org # v7.1
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs/attrib.c |   14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+--- a/fs/ntfs/attrib.c
++++ b/fs/ntfs/attrib.c
+@@ -607,6 +607,14 @@ static bool ntfs_file_name_attr_value_is
+                       value_length - offsetof(struct file_name_attr, file_name);
+ }
++static bool ntfs_volume_name_attr_value_is_valid(const u32 value_length)
++{
++      if (value_length & 1)
++              return false;
++
++      return value_length <= NTFS_MAX_LABEL_LEN * sizeof(__le16);
++}
++
+ struct ntfs_resident_attr_value {
+       const u8 *data;
+       u32 len;
+@@ -657,7 +665,7 @@ static bool ntfs_attr_value_is_valid(str
+       u32 min_len;
+       if (a->non_resident) {
+-              if (a->type == AT_FILE_NAME)
++              if (a->type == AT_FILE_NAME || a->type == AT_VOLUME_NAME)
+                       goto corrupt;
+               if (!ntfs_non_resident_attr_value_is_valid(a))
+                       goto corrupt;
+@@ -676,6 +684,10 @@ static bool ntfs_attr_value_is_valid(str
+               if (!ntfs_file_name_attr_value_is_valid(value.data, value.len))
+                       goto corrupt;
+               break;
++      case AT_VOLUME_NAME:
++              if (!ntfs_volume_name_attr_value_is_valid(value.len))
++                      goto corrupt;
++              break;
+       }
+       return true;
diff --git a/queue-7.1/ntfs3-bound-to_move-in-indx_insert_into_root-before-hdr_insert_head.patch b/queue-7.1/ntfs3-bound-to_move-in-indx_insert_into_root-before-hdr_insert_head.patch
new file mode 100644 (file)
index 0000000..acf4b65
--- /dev/null
@@ -0,0 +1,82 @@
+From 9b6926ac9c970ae0b2c2fe6289b16e9aa10b6a67 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Fri, 17 Apr 2026 19:33:05 -0400
+Subject: ntfs3: bound to_move in indx_insert_into_root before hdr_insert_head
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 9b6926ac9c970ae0b2c2fe6289b16e9aa10b6a67 upstream.
+
+indx_insert_into_root() promotes a full resident $INDEX_ROOT into
+$INDEX_ALLOCATION and copies all non-last resident root entries into
+a newly allocated INDEX_BUFFER via hdr_insert_head(). The source
+byte count 'to_move' is summed from the on-disk resident entry sizes
+and is independent of the destination buffer size, which comes from
+root->index_block_size (via indx->index_bits).
+
+A crafted NTFS image that keeps a valid, full resident root but
+shrinks root->index_block_size down to 512 after the root has been
+populated makes hdr_insert_head() memcpy attacker-controlled resident
+entry bytes past the end of the kmalloc(1u << indx->index_bits)
+allocation returned by indx_new(). For a 512-byte destination and a
+resident root whose non-last entries total 560 bytes, the memcpy
+overruns by 120 bytes and a following memmove extends the highest
+written offset to 136 bytes past the allocation. The overflow bytes
+are a direct copy of on-disk entries (via kmemdup), so they are
+fully attacker-controlled.
+
+The write is reachable from unprivileged open(O_CREAT) on a mounted
+crafted NTFS image: a single sufficiently long create in a directory
+whose resident root is already full forces root promotion and
+triggers the copy.
+
+This is a controlled out-of-bounds write of 120-136 bytes past a
+kmalloc(index_block_size) allocation, with attacker-controlled
+content. It is a bounded adjacent-heap corruption primitive; it is
+not an arbitrary-address write. Successful exploitation into a named
+victim object depends on the surrounding slab layout.
+
+Reject the copy at the sink. The destination's INDEX_HDR already
+reports hdr_total (the payload capacity of the new buffer) and
+hdr_used (the bytes already consumed by the terminal END entry
+installed by indx_new()); require that to_move fits in the remaining
+payload before calling hdr_insert_head(). On mismatch, fail with
+-EINVAL and mark the filesystem as having a detected on-disk
+inconsistency, which is the same behaviour as the surrounding
+validation in this function.
+
+Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/index.c |   16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/fs/ntfs3/index.c
++++ b/fs/ntfs3/index.c
+@@ -1742,6 +1742,22 @@ static int indx_insert_into_root(struct
+       hdr_used = le32_to_cpu(hdr->used);
+       hdr_total = le32_to_cpu(hdr->total);
++      /*
++       * The destination INDEX_BUFFER has 'hdr_total' bytes of payload
++       * available after the header, of which 'hdr_used' are already
++       * consumed by the single terminal END entry installed by
++       * indx_new(). A crafted image can present a resident root whose
++       * non-last entries (summing to 'to_move') exceed what fits in
++       * this buffer; copying them unchecked would overrun the
++       * kmalloc(1u << indx->index_bits) allocation backing the new
++       * buffer. Reject the copy in that case.
++       */
++      if (to_move > hdr_total - hdr_used) {
++              err = -EINVAL;
++              ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
++              goto out_put_n;
++      }
++
+       /* Copy root entries into new buffer. */
+       hdr_insert_head(hdr, re, to_move);
diff --git a/queue-7.1/ntfs3-cap-restart_table-free-chain-walker-at-rt-used.patch b/queue-7.1/ntfs3-cap-restart_table-free-chain-walker-at-rt-used.patch
new file mode 100644 (file)
index 0000000..afc4016
--- /dev/null
@@ -0,0 +1,88 @@
+From 9611f644302c07d21bc8af97e3e06a3d30064253 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Sun, 17 May 2026 19:41:40 -0400
+Subject: ntfs3: cap RESTART_TABLE free-chain walker at rt->used
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 9611f644302c07d21bc8af97e3e06a3d30064253 upstream.
+
+A crafted NTFS3 disk image triggers an in-kernel infinite loop at
+mount time, hanging the mounting thread and firing the soft-lockup
+watchdog within ~22s on multi-CPU hosts (panic with
+kernel.softlockup_panic=1).  The bug is reachable from desktop USB
+auto-mount on distributions where udisks2 routes the NTFS signature
+to the in-tree ntfs3 driver (Arch family and an increasing fraction
+of Fedora / openSUSE / RHEL deployments); CAP_SYS_ADMIN-class manual
+mount elsewhere.
+
+check_rstbl()'s second walker iterates the free-entry singly-linked
+list headed by rt->first_free with no upper bound on iteration count:
+
+  for (off = ff; off;) {
+      if (off == RESTART_ENTRY_ALLOCATED)
+          return false;
+      off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off));
+      if (off > ts - sizeof(__le32))
+          return false;
+  }
+
+The existing guards cover three exits: end-of-list (off == 0), the
+in-use marker (off == RESTART_ENTRY_ALLOCATED), and out-of-bounds
+(off > ts - sizeof(__le32)).  None of the three prevents an
+in-bounds cycle.
+
+A crafted on-disk RESTART_TABLE whose free chain contains a
+self-loop or A->B->A cycle whose offsets satisfy:
+
+  - in range [sizeof(struct RESTART_TABLE), ts - sizeof(__le32)]
+  - (off - sizeof(struct RESTART_TABLE)) % rsize == 0
+
+passes all existing guards and spins the mount-time thread forever.
+Reproduced in UML by hand-forging a 2 MB NTFS3 image whose journal
+RESTART_TABLE first_free = 0x18 and whose entry at offset 0x18
+stores 0x18 as its next pointer; mount of the forged image with
+the in-tree ntfs3 driver never returns.
+
+Bound the walker by rt->used.  Each entry on a legitimate free
+chain is unique, and the total slot count is ne = le16_to_cpu
+(rt->used).  A traversal that visits more than ne slots is by
+construction malformed; reject it as a corrupt RESTART_TABLE.
+
+After this patch, mount of the forged image returns with -EINVAL
+and a log_replay failure message, and mkntfs-produced legitimate
+images mount cleanly (verified in the same UML harness).
+
+Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
+Cc: stable@vger.kernel.org
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/fslog.c |   13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+--- a/fs/ntfs3/fslog.c
++++ b/fs/ntfs3/fslog.c
+@@ -764,8 +764,19 @@ static bool check_rstbl(const struct RES
+       /*
+        * Walk through the list headed by the first entry to make
+        * sure none of the entries are currently being used.
++       *
++       * Bound traversal by ne (rt->used) to defeat a crafted on-disk
++       * cycle in the free chain.  Each entry in a legitimate free
++       * list is unique, so a chain that visits more than ne slots
++       * is malformed.  Without this guard, an attacker-controlled
++       * RESTART_TABLE with a self-loop or A->B->A cycle whose
++       * offsets satisfy the existing alignment + in-bounds guards
++       * spins forever at mount time.
+        */
+-      for (off = ff; off;) {
++      for (off = ff, i = 0; off; i++) {
++              if (i > ne)
++                      return false;
++
+               if (off == RESTART_ENTRY_ALLOCATED)
+                       return false;
diff --git a/queue-7.1/ntfs3-fix-out-of-bounds-read-in-decompress_lznt.patch b/queue-7.1/ntfs3-fix-out-of-bounds-read-in-decompress_lznt.patch
new file mode 100644 (file)
index 0000000..63eb5e9
--- /dev/null
@@ -0,0 +1,35 @@
+From 7160a57192fb16d7a6fa9b7f5c7ac341d2444a89 Mon Sep 17 00:00:00 2001
+From: Tristan Madani <tristan@talencesecurity.com>
+Date: Sat, 18 Apr 2026 13:11:18 +0000
+Subject: ntfs3: fix out-of-bounds read in decompress_lznt
+
+From: Tristan Madani <tristan@talencesecurity.com>
+
+commit 7160a57192fb16d7a6fa9b7f5c7ac341d2444a89 upstream.
+
+decompress_lznt() does not validate array index bounds before accessing
+the decompression table. A corrupted NTFS3 image with invalid compressed
+data can trigger an out-of-bounds read.
+
+Add index bounds checking to prevent the OOB access.
+
+Reported-by: syzbot+39b2fb0f2638669008ec@syzkaller.appspotmail.com
+Cc: stable@vger.kernel.org
+Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/lznt.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/ntfs3/lznt.c
++++ b/fs/ntfs3/lznt.c
+@@ -240,7 +240,7 @@ static inline ssize_t decompress_chunk(u
+               if (up - unc > LZNT_CHUNK_SIZE)
+                       return -EINVAL;
+               /* Correct index */
+-              while (unc + s_max_off[index] < up)
++              while (index < ARRAY_SIZE(s_max_off) - 1 && unc + s_max_off[index] < up)
+                       index += 1;
+               /* Check the current flag for zero. */
diff --git a/queue-7.1/ntfs3-validate-split-point-offset-in-indx_insert_into_buffer.patch b/queue-7.1/ntfs3-validate-split-point-offset-in-indx_insert_into_buffer.patch
new file mode 100644 (file)
index 0000000..0f3e333
--- /dev/null
@@ -0,0 +1,82 @@
+From f1df9d771df47aa40de6d70949c28720ae1e430d Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Fri, 17 Apr 2026 18:57:12 -0400
+Subject: ntfs3: validate split-point offset in indx_insert_into_buffer
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit f1df9d771df47aa40de6d70949c28720ae1e430d upstream.
+
+indx_insert_into_buffer() computes
+
+    used = used1 - to_copy - sp_size;
+    memmove(de_t, Add2Ptr(sp, sp_size), used - le32_to_cpu(hdr1->de_off));
+
+where sp and sp_size come from hdr_find_split().  hdr_find_split()
+walks entries by le16_to_cpu(e->size) without validating that each
+step stays within hdr->used or that the size field is at least
+sizeof(struct NTFS_DE).  index_hdr_check(), the on-load gatekeeper,
+only validates header-level fields (used, total, de_off) and does
+not walk per-entry sizes.
+
+A crafted NTFS image whose leaf INDEX_HDR reports used == total but
+contains one interior NTFS_DE with size = 0xFFF0 therefore passes
+validation, descends to indx_insert_into_buffer() through the
+ntfs_create() -> indx_insert_entry() path, and makes hdr_find_split()
+return an sp whose sp_size (0xFFF0) greatly exceeds the remaining
+bytes in the buffer.  The u32 subtraction underflows and the memmove
+count becomes a near-4-GiB value, producing an out-of-bounds kernel
+write that corrupts adjacent allocations and panics the kernel.
+
+Reproduced on 7.0.0-rc7 with UML + KASAN via a crafted image and a
+single 'touch' inside the mounted directory; crash site resolves to
+fs/ntfs3/index.c at the memmove.  Trigger requires only local mount
+of an attacker-supplied filesystem image (USB, loopback, or removable
+media auto-mount).
+
+Reject the split whenever the chosen sp plus its declared size
+already extends past hdr1->used.  This is the minimal fix; it
+preserves the existing hdr_find_split() contract and relies on the
+same out: cleanup path as the pre-existing error returns.
+
+A prior OOB read in the very same indx_insert_into_buffer() memmove
+was fixed in commit b8c44949044e ("fs/ntfs3: Fix OOB read in
+indx_insert_into_buffer") by tightening hdr_find_e(), but that fix
+does not cover the split-point size field path addressed here: sp is
+returned by hdr_find_split(), not hdr_find_e(), and the underflow is
+driven by sp->size rather than hdr->used exceeding hdr->total.
+
+Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block")
+Cc: stable@vger.kernel.org
+Reported-by: Michael Bommarito <michael.bommarito@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ntfs3/index.c |   14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/fs/ntfs3/index.c
++++ b/fs/ntfs3/index.c
+@@ -1862,6 +1862,20 @@ indx_insert_into_buffer(struct ntfs_inde
+       memcpy(up_e, sp, sp_size);
+       used1 = le32_to_cpu(hdr1->used);
++
++      /*
++       * hdr_find_split does not validate per-entry sizes, so a crafted
++       * NTFS_DE whose le16 size field is out of range can place sp such
++       * that (PtrOffset(hdr1, sp) + sp_size) exceeds used1. Without this
++       * guard the u32 'used = used1 - to_copy - sp_size' underflows and
++       * the subsequent memmove count becomes a near-4-GiB value,
++       * triggering an out-of-bounds kernel write.
++       */
++      if (PtrOffset(hdr1, sp) + sp_size > used1) {
++              err = -EINVAL;
++              goto out;
++      }
++
+       hdr1_saved = kmemdup(hdr1, used1, GFP_NOFS);
+       if (!hdr1_saved) {
+               err = -ENOMEM;
diff --git a/queue-7.1/ocfs2-add-journal-null-check-in-ocfs2_checkpoint_inode.patch b/queue-7.1/ocfs2-add-journal-null-check-in-ocfs2_checkpoint_inode.patch
new file mode 100644 (file)
index 0000000..ad19452
--- /dev/null
@@ -0,0 +1,50 @@
+From a291c77c034b7a81849ce9b71cc9ecda9e587d89 Mon Sep 17 00:00:00 2001
+From: Joseph Qi <joseph.qi@linux.alibaba.com>
+Date: Sun, 31 May 2026 21:16:45 +0800
+Subject: ocfs2: add journal NULL check in ocfs2_checkpoint_inode()
+
+From: Joseph Qi <joseph.qi@linux.alibaba.com>
+
+commit a291c77c034b7a81849ce9b71cc9ecda9e587d89 upstream.
+
+During unmount, ocfs2_journal_shutdown() frees the journal and sets
+osb->journal to NULL. Later, when VFS evicts remaining cached inodes,
+ocfs2_evict_inode() -> ocfs2_clear_inode() -> ocfs2_checkpoint_inode()
+-> ocfs2_ci_fully_checkpointed() dereferences osb->journal, causing a
+NULL pointer dereference.
+
+Fix this by adding a NULL check for osb->journal in
+ocfs2_checkpoint_inode(). If the journal is NULL, it has already been
+fully flushed and destroyed during shutdown, so there is nothing to
+checkpoint.
+
+Link: https://lore.kernel.org/20260531131645.3650299-1-joseph.qi@linux.alibaba.com
+Reported-by: Farhad Alemi <farhad.alemi@berkeley.edu>
+Fixes: da5e7c87827e ("ocfs2: cleanup journal init and shutdown")
+Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
+Tested-by: Farhad Alemi <farhad.alemi@berkeley.edu>
+Reviewed-by: Heming Zhao <heming.zhao@suse.com>
+Cc: Mark Fasheh <mark@fasheh.com>
+Cc: Joel Becker <jlbec@evilplan.org>
+Cc: Junxiao Bi <junxiao.bi@oracle.com>
+Cc: Changwei Ge <gechangwei@live.cn>
+Cc: Jun Piao <piaojun@huawei.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ocfs2/journal.h |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/fs/ocfs2/journal.h
++++ b/fs/ocfs2/journal.h
+@@ -196,6 +196,9 @@ static inline void ocfs2_checkpoint_inod
+       if (ocfs2_mount_local(osb))
+               return;
++      if (!osb->journal)
++              return;
++
+       if (!ocfs2_ci_fully_checkpointed(INODE_CACHE(inode))) {
+               /* WARNING: This only kicks off a single
+                * checkpoint. If someone races you and adds more
diff --git a/queue-7.1/ocfs2-avoid-moving-extents-to-occupied-clusters.patch b/queue-7.1/ocfs2-avoid-moving-extents-to-occupied-clusters.patch
new file mode 100644 (file)
index 0000000..97250ea
--- /dev/null
@@ -0,0 +1,67 @@
+From 22920541c35a9f23f219038ba5874c843a7c4419 Mon Sep 17 00:00:00 2001
+From: Kyle Zeng <kylebot@openai.com>
+Date: Thu, 11 Jun 2026 14:35:10 -0700
+Subject: ocfs2: avoid moving extents to occupied clusters
+
+From: Kyle Zeng <kylebot@openai.com>
+
+commit 22920541c35a9f23f219038ba5874c843a7c4419 upstream.
+
+For non-auto OCFS2_IOC_MOVE_EXT operations, userspace supplies a physical
+me_goal.  ocfs2_move_extent() initializes new_phys_cpos from that goal and
+expects ocfs2_probe_alloc_group() to replace it with a free run in the
+target block group.
+
+The probe currently leaves *phys_cpos unchanged if the scan reaches the
+end of the group without finding a free run.  An occupied goal at the last
+bit can therefore survive the probe and be passed to
+__ocfs2_move_extent(), which copies file data into a cluster still owned
+by another inode before the bitmap is updated.
+
+When the probe does find a free run, it also subtracts move_len from the
+ending bit.  The start of an N-bit run ending at i is i - N + 1, so the
+current calculation can report the bit immediately before the free run.
+
+Clear *phys_cpos before scanning and use the correct free-run start.
+Callers already treat a zero result as -ENOSPC, so failed probes no longer
+continue with an occupied caller-controlled goal.
+
+Link: https://lore.kernel.org/20260611213510.16956-1-kylebot@openai.com
+Fixes: e6b5859cccfa ("Ocfs2/move_extents: helper to probe a proper region to move in an alloc group.")
+Fixes: 236b9254f8d1 ("ocfs2: fix non-auto defrag path not working issue")
+Assisted-by: Codex:gpt-5.5
+Signed-off-by: Kyle Zeng <kylebot@openai.com>
+Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
+Cc: Mark Fasheh <mark@fasheh.com>
+Cc: Joel Becker <jlbec@evilplan.org>
+Cc: Junxiao Bi <junxiao.bi@oracle.com>
+Cc: Changwei Ge <gechangwei@live.cn>
+Cc: Jun Piao <piaojun@huawei.com>
+Cc: Heming Zhao <heming.zhao@suse.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ocfs2/move_extents.c |    4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/fs/ocfs2/move_extents.c
++++ b/fs/ocfs2/move_extents.c
+@@ -534,6 +534,8 @@ static void ocfs2_probe_alloc_group(stru
+       u32 base_cpos = ocfs2_blocks_to_clusters(inode->i_sb,
+                                                le64_to_cpu(gd->bg_blkno));
++      *phys_cpos = 0;
++
+       for (i = base_bit; i < le16_to_cpu(gd->bg_bits); i++) {
+               used = ocfs2_test_bit(i, (unsigned long *)gd->bg_bitmap);
+@@ -555,7 +557,7 @@ static void ocfs2_probe_alloc_group(stru
+                       last_free_bits++;
+               if (last_free_bits == move_len) {
+-                      i -= move_len;
++                      i = i - move_len + 1;
+                       *goal_bit = i;
+                       *phys_cpos = base_cpos + i;
+                       break;
diff --git a/queue-7.1/ocfs2-fix-null-h_transaction-deref-in-ocfs2_assure_trans_credits.patch b/queue-7.1/ocfs2-fix-null-h_transaction-deref-in-ocfs2_assure_trans_credits.patch
new file mode 100644 (file)
index 0000000..1b69dd4
--- /dev/null
@@ -0,0 +1,67 @@
+From f9ab30c96b0f00c20c6dac93681bdae3a033d229 Mon Sep 17 00:00:00 2001
+From: Ian Bridges <icb@fastmail.org>
+Date: Thu, 11 Jun 2026 09:46:38 -0500
+Subject: ocfs2: fix NULL h_transaction deref in ocfs2_assure_trans_credits
+
+From: Ian Bridges <icb@fastmail.org>
+
+commit f9ab30c96b0f00c20c6dac93681bdae3a033d229 upstream.
+
+[BUG]
+A direct write over unwritten extents can panic the kernel in
+ocfs2_assure_trans_credits() when the journal aborts during DIO
+completion. The crash is a general protection fault from a NULL pointer
+dereference.
+
+[CAUSE]
+ocfs2_dio_end_io_write() loops over a direct write's unwritten extents,
+marking each written under a single journal handle. If the journal
+aborts (for example after an I/O error) while the extent tree is being
+updated, the handle is left aborted with its transaction pointer
+cleared. The extent merge treats that failure as not critical and
+reports success, so the loop keeps using the handle.
+ocfs2_assure_trans_credits() reads the handle's remaining credits
+without first checking whether the handle is aborted, and that read
+dereferences the cleared transaction pointer.
+
+[FIX]
+A journal abort is recorded in the handle itself, so callers are
+expected to test the handle rather than rely on a returned error.
+Make ocfs2_assure_trans_credits() do that, as the other ocfs2 journal
+helpers already do, and return -EROFS when the handle is aborted.
+
+Link: https://lore.kernel.org/airKTsM1fRVN-Wj7@dev
+Fixes: be346c1a6eeb ("ocfs2: fix DIO failure due to insufficient transaction credits")
+Signed-off-by: Ian Bridges <icb@fastmail.org>
+Reported-by: syzbot+e9c15ff790cea6a0cfae@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=e9c15ff790cea6a0cfae
+Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
+Cc: Mark Fasheh <mark@fasheh.com>
+Cc: Joel Becker <jlbec@evilplan.org>
+Cc: Junxiao Bi <junxiao.bi@oracle.com>
+Cc: Changwei Ge <gechangwei@live.cn>
+Cc: Jun Piao <piaojun@huawei.com>
+Cc: Heming Zhao <heming.zhao@suse.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ocfs2/journal.c |    6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/fs/ocfs2/journal.c
++++ b/fs/ocfs2/journal.c
+@@ -473,8 +473,12 @@ bail:
+  */
+ int ocfs2_assure_trans_credits(handle_t *handle, int nblocks)
+ {
+-      int old_nblks = jbd2_handle_buffer_credits(handle);
++      int old_nblks;
++      if (is_handle_aborted(handle))
++              return -EROFS;
++
++      old_nblks = jbd2_handle_buffer_credits(handle);
+       trace_ocfs2_assure_trans_credits(old_nblks);
+       if (old_nblks >= nblocks)
+               return 0;
diff --git a/queue-7.1/ocfs2-fix-ubsan-array-index-out-of-bounds-in-ocfs2_sum_rightmost_rec.patch b/queue-7.1/ocfs2-fix-ubsan-array-index-out-of-bounds-in-ocfs2_sum_rightmost_rec.patch
new file mode 100644 (file)
index 0000000..b64b889
--- /dev/null
@@ -0,0 +1,87 @@
+From 452a8467be8143747292218212671deeb186d2ae Mon Sep 17 00:00:00 2001
+From: Ian Bridges <icb@fastmail.org>
+Date: Wed, 10 Jun 2026 19:23:11 -0500
+Subject: ocfs2: fix UBSAN array-index-out-of-bounds in ocfs2_sum_rightmost_rec
+
+From: Ian Bridges <icb@fastmail.org>
+
+commit 452a8467be8143747292218212671deeb186d2ae upstream.
+
+[BUG]
+On-disk corruption setting l_next_free_rec to 0 in an inode's embedded
+extent list triggers a UBSAN panic on the next write to that file.
+
+[CAUSE]
+ocfs2_sum_rightmost_rec() computes
+i = le16_to_cpu(el->l_next_free_rec) - 1
+and accesses el->l_recs[i] without validating i. When l_next_free_rec
+is 0, i becomes -1; when l_next_free_rec exceeds l_count, i falls
+past the end of the array. Either case violates the
+__counted_by_le(l_count) annotation on l_recs[] and triggers UBSAN.
+
+[FIX]
+Validate the inode's embedded extent list when the inode is read, in
+ocfs2_validate_inode_block(): l_count must be non-zero and no larger
+than the inode block can hold, and l_next_free_rec must not exceed
+l_count. A corrupt list is rejected at read time, before the b-tree
+code can index l_recs[] out of bounds.
+
+Link: https://lore.kernel.org/ain_780qc0P4ypNd@dev
+Signed-off-by: Ian Bridges <icb@fastmail.org>
+Reported-by: syzbot+be16e33db01e6644db7a@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=be16e33db01e6644db7a
+Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
+Cc: Mark Fasheh <mark@fasheh.com>
+Cc: Joel Becker <jlbec@evilplan.org>
+Cc: Junxiao Bi <junxiao.bi@oracle.com>
+Cc: Changwei Ge <gechangwei@live.cn>
+Cc: Jun Piao <piaojun@huawei.com>
+Cc: Heming Zhao <heming.zhao@suse.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ocfs2/inode.c |   32 ++++++++++++++++++++++++++++++++
+ 1 file changed, 32 insertions(+)
+
+--- a/fs/ocfs2/inode.c
++++ b/fs/ocfs2/inode.c
+@@ -1582,6 +1582,38 @@ int ocfs2_validate_inode_block(struct su
+               goto bail;
+       }
++      if (ocfs2_dinode_has_extents(di)) {
++              struct ocfs2_extent_list *el = &di->id2.i_list;
++              u16 count = le16_to_cpu(el->l_count);
++              u16 next_free = le16_to_cpu(el->l_next_free_rec);
++
++              if (count == 0) {
++                      rc = ocfs2_error(sb,
++                                       "Invalid dinode %llu: extent list l_count is zero\n",
++                                       (unsigned long long)bh->b_blocknr);
++                      goto bail;
++              }
++              /*
++               * The exact capacity depends on i_xattr_inline_size, another
++               * unvalidated on-disk field. Inline xattrs only shrink the
++               * list, so the no-xattr maximum is a safe upper bound that a
++               * valid l_count never exceeds.
++               */
++              if (count > ocfs2_extent_recs_per_inode(sb)) {
++                      rc = ocfs2_error(sb,
++                                       "Invalid dinode %llu: extent list l_count %u exceeds max %u\n",
++                                       (unsigned long long)bh->b_blocknr, count,
++                                       ocfs2_extent_recs_per_inode(sb));
++                      goto bail;
++              }
++              if (next_free > count) {
++                      rc = ocfs2_error(sb,
++                                       "Invalid dinode %llu: extent list l_next_free_rec %u exceeds l_count %u\n",
++                                       (unsigned long long)bh->b_blocknr, next_free, count);
++                      goto bail;
++              }
++      }
++
+       rc = 0;
+ bail:
diff --git a/queue-7.1/ocfs2-reject-dinodes-whose-i_rdev-disagrees-with-the-file-type.patch b/queue-7.1/ocfs2-reject-dinodes-whose-i_rdev-disagrees-with-the-file-type.patch
new file mode 100644 (file)
index 0000000..e3d50e3
--- /dev/null
@@ -0,0 +1,120 @@
+From 51407c2d249987a466416890a5c931a2354a46aa Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Tue, 19 May 2026 07:04:03 -0400
+Subject: ocfs2: reject dinodes whose i_rdev disagrees with the file type
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 51407c2d249987a466416890a5c931a2354a46aa upstream.
+
+id1.dev1.i_rdev is the device-number arm of the ocfs2_dinode id1 union.
+It is only meaningful for character and block device inodes.  For any
+other user-visible file type the on-disk value must be zero.
+
+ocfs2_populate_inode() currently copies id1.dev1.i_rdev into inode->i_rdev
+before the S_IFMT switch decides whether the inode is a special file.  A
+non-device inode with a non-zero i_rdev can therefore publish stale or
+attacker-controlled device state into the in-core inode.
+
+System inodes legitimately use other arms of the same union, so keep the
+cross-check restricted to non-system inodes.  Factor that predicate into a
+helper and use it in both the normal validator and online filecheck path;
+filecheck reports the malformed dinode through
+OCFS2_FILECHECK_ERR_INVALIDINO instead of ocfs2_error().
+
+Link: https://lore.kernel.org/20260519110404.1803902-3-michael.bommarito@gmail.com
+Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.")
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Assisted-by: Claude:claude-opus-4-7
+Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
+Cc: Changwei Ge <gechangwei@live.cn>
+Cc: Heming Zhao <heming.zhao@suse.com>
+Cc: Joel Becker <jlbec@evilplan.org>
+Cc: Jun Piao <piaojun@huawei.com>
+Cc: Junxiao Bi <junxiao.bi@oracle.com>
+Cc: Mark Fasheh <mark@fasheh.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ocfs2/inode.c |   55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+
+--- a/fs/ocfs2/inode.c
++++ b/fs/ocfs2/inode.c
+@@ -72,6 +72,16 @@ static bool ocfs2_valid_inode_mode(umode
+       return fs_umode_to_ftype(mode) != FT_UNKNOWN;
+ }
++static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di)
++{
++      umode_t mode = le16_to_cpu(di->i_mode);
++
++      if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)
++              return false;
++
++      return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0;
++}
++
+ void ocfs2_set_inode_flags(struct inode *inode)
+ {
+       unsigned int flags = OCFS2_I(inode)->ip_attr;
+@@ -1518,6 +1528,41 @@ int ocfs2_validate_inode_block(struct su
+               goto bail;
+       }
++      /*
++       * id1.dev1.i_rdev is the device-number arm of the id1 union and
++       * is only meaningful for character and block device inodes.  For
++       * any other regular user-visible file type the on-disk value
++       * must be zero.  ocfs2_populate_inode() currently runs
++       *
++       *     inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev));
++       *
++       * unconditionally, before the S_IFMT switch decides whether the
++       * inode is a special file.  As a result, an i_rdev value present
++       * on a non-device inode is silently published into the in-core
++       * inode; a subsequent forced re-read or in-core mode mutation
++       * (cluster peer with raw write access to the shared LUN,
++       * on-disk corruption, or a separately forged dinode) can then
++       * expose the attacker-controlled device number to
++       * init_special_inode() without ever showing an unusual i_mode
++       * at validation time.
++       *
++       * System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1
++       * and journal1 arms of the same union (allocator i_used /
++       * i_total counters and the journal ij_flags /
++       * ij_recovery_generation pair); those bytes are not an i_rdev
++       * and must not be checked here.  Restrict the cross-check to
++       * non-system inodes, which is the full attacker-controllable
++       * surface.
++       */
++      if (ocfs2_dinode_has_unexpected_rdev(di)) {
++              rc = ocfs2_error(sb,
++                               "Invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n",
++                               (unsigned long long)bh->b_blocknr,
++                               le16_to_cpu(di->i_mode),
++                               (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev));
++              goto bail;
++      }
++
+       if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) {
+               struct ocfs2_inline_data *data = &di->id2.i_data;
+@@ -1712,6 +1757,16 @@ static int ocfs2_filecheck_validate_inod
+                    (unsigned long long)bh->b_blocknr,
+                    le16_to_cpu(di->i_mode));
+               rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
++              goto bail;
++      }
++
++      if (ocfs2_dinode_has_unexpected_rdev(di)) {
++              mlog(ML_ERROR,
++                   "Filecheck: invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n",
++                   (unsigned long long)bh->b_blocknr,
++                   le16_to_cpu(di->i_mode),
++                   (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev));
++              rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
+       }
+ bail:
diff --git a/queue-7.1/ocfs2-reject-dinodes-with-non-canonical-i_mode-type.patch b/queue-7.1/ocfs2-reject-dinodes-with-non-canonical-i_mode-type.patch
new file mode 100644 (file)
index 0000000..90c8135
--- /dev/null
@@ -0,0 +1,141 @@
+From 5366a017099c6a3c443be908a05f26fd72af12a1 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Tue, 19 May 2026 07:04:02 -0400
+Subject: ocfs2: reject dinodes with non-canonical i_mode type
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 5366a017099c6a3c443be908a05f26fd72af12a1 upstream.
+
+Patch series "ocfs2: harden inode validators against forged metadata", v2.
+
+This series adds three structural checks to OCFS2 dinode validation so
+malformed on-disk fields are rejected before ocfs2_populate_inode() copies
+them into the in-core inode.
+
+The checks cover:
+
+  - i_mode values whose type bits do not name a canonical POSIX file
+    type;
+  - non-device dinodes whose id1.dev1.i_rdev field is non-zero; and
+  - non-inline dinodes that claim non-zero i_size while i_clusters is
+    zero, covering directories unconditionally and regular files on
+    non-sparse volumes.
+
+The normal read path reports these through ocfs2_error(), matching the
+existing suballoc-slot, inline-data, chain-list, and refcount checks.  The
+online filecheck path uses the same structural predicates but keeps its
+own reporting contract, returning OCFS2_FILECHECK_ERR_INVALIDINO instead
+of calling ocfs2_error().
+
+
+This patch (of 3):
+
+ocfs2_validate_inode_block() currently accepts any non-zero i_mode value.
+ocfs2_populate_inode() then copies that mode verbatim into inode->i_mode
+and dispatches on i_mode & S_IFMT to the file/dir/symlink/special_file
+iops; an unrecognised type falls through to ocfs2_special_file_iops and
+init_special_inode().
+
+Reject dinodes whose type bits do not name one of the seven canonical
+POSIX file types.  Use fs_umode_to_ftype(), the same generic file-type
+conversion helper OCFS2 already uses for directory entries, so the
+accepted inode type set matches the kernel file-type vocabulary instead of
+open-coding a local switch.
+
+Apply the same structural check to the online filecheck read path.
+filecheck keeps its own error namespace, so it reports malformed i_mode
+through the filecheck logger and OCFS2_FILECHECK_ERR_INVALIDINO instead of
+calling ocfs2_error(), but it must not allow a malformed dinode to proceed
+into ocfs2_populate_inode().
+
+Link: https://lore.kernel.org/20260519110404.1803902-1-michael.bommarito@gmail.com
+Link: https://lore.kernel.org/20260519110404.1803902-2-michael.bommarito@gmail.com
+Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.")
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com
+Assisted-by: Claude:claude-opus-4-7
+Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
+Cc: Changwei Ge <gechangwei@live.cn>
+Cc: Heming Zhao <heming.zhao@suse.com>
+Cc: Joel Becker <jlbec@evilplan.org>
+Cc: Jun Piao <piaojun@huawei.com>
+Cc: Junxiao Bi <junxiao.bi@oracle.com>
+Cc: Mark Fasheh <mark@fasheh.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ocfs2/inode.c |   36 ++++++++++++++++++++++++++++++++++--
+ 1 file changed, 34 insertions(+), 2 deletions(-)
+
+--- a/fs/ocfs2/inode.c
++++ b/fs/ocfs2/inode.c
+@@ -13,6 +13,7 @@
+ #include <linux/pagemap.h>
+ #include <linux/quotaops.h>
+ #include <linux/iversion.h>
++#include <linux/fs_dirent.h>
+ #include <asm/byteorder.h>
+@@ -64,7 +65,12 @@ static int ocfs2_filecheck_read_inode_bl
+ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb,
+                                               struct buffer_head *bh);
+ static int ocfs2_filecheck_repair_inode_block(struct super_block *sb,
+-                                            struct buffer_head *bh);
++                                                    struct buffer_head *bh);
++
++static bool ocfs2_valid_inode_mode(umode_t mode)
++{
++      return fs_umode_to_ftype(mode) != FT_UNKNOWN;
++}
+ void ocfs2_set_inode_flags(struct inode *inode)
+ {
+@@ -1494,6 +1500,24 @@ int ocfs2_validate_inode_block(struct su
+               goto bail;
+       }
++      /*
++       * Reject dinodes whose i_mode does not name one of the seven
++       * canonical POSIX file types.  ocfs2_populate_inode() copies
++       * i_mode verbatim into inode->i_mode and then dispatches via
++       * switch (mode & S_IFMT) to file/dir/symlink/special_file iops;
++       * an unrecognised type falls into ocfs2_special_file_iops with
++       * init_special_inode(), which interprets i_rdev.  Constrain the
++       * type here so the dispatch only ever sees a value mkfs.ocfs2 /
++       * VFS can produce.
++       */
++      if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) {
++              rc = ocfs2_error(sb,
++                               "Invalid dinode #%llu: mode 0%o has unknown file type\n",
++                               (unsigned long long)bh->b_blocknr,
++                               le16_to_cpu(di->i_mode));
++              goto bail;
++      }
++
+       if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) {
+               struct ocfs2_inline_data *data = &di->id2.i_data;
+@@ -1679,6 +1703,15 @@ static int ocfs2_filecheck_validate_inod
+                    (unsigned long long)bh->b_blocknr,
+                    le32_to_cpu(di->i_fs_generation));
+               rc = -OCFS2_FILECHECK_ERR_GENERATION;
++              goto bail;
++      }
++
++      if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) {
++              mlog(ML_ERROR,
++                   "Filecheck: invalid dinode #%llu: mode 0%o has unknown file type\n",
++                   (unsigned long long)bh->b_blocknr,
++                   le16_to_cpu(di->i_mode));
++              rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
+       }
+ bail:
+@@ -1867,4 +1900,3 @@ const struct ocfs2_caching_operations oc
+       .co_io_lock             = ocfs2_inode_cache_io_lock,
+       .co_io_unlock           = ocfs2_inode_cache_io_unlock,
+ };
+-
diff --git a/queue-7.1/ocfs2-reject-non-inline-dinodes-with-i_size-and-zero-i_clusters.patch b/queue-7.1/ocfs2-reject-non-inline-dinodes-with-i_size-and-zero-i_clusters.patch
new file mode 100644 (file)
index 0000000..fd1cdf5
--- /dev/null
@@ -0,0 +1,135 @@
+From 7ebc672fab7a76e1e47e0f2fc1ee48118d27fde4 Mon Sep 17 00:00:00 2001
+From: Michael Bommarito <michael.bommarito@gmail.com>
+Date: Tue, 19 May 2026 07:04:04 -0400
+Subject: ocfs2: reject non-inline dinodes with i_size and zero i_clusters
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+commit 7ebc672fab7a76e1e47e0f2fc1ee48118d27fde4 upstream.
+
+On a volume mounted without OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, a
+non-inline regular file with non-zero i_size and zero i_clusters is
+structurally malformed: the extent map declares no allocated clusters yet
+the size header claims content exists.  Keep rejecting that shape, but
+express it through a shared predicate so the same invariant is available
+to normal inode reads and online filecheck.
+
+The same zero-cluster shape is also malformed for non-inline directories.
+ocfs2 directory growth allocates backing storage before advancing i_size,
+and ocfs2_dir_foreach_blk_el() later walks until ctx->pos reaches
+i_size_read(inode).  A forged directory dinode with a huge i_size and no
+clusters would repeatedly fail on holes while advancing through the
+claimed size.
+
+Sparse regular files remain exempt: on sparse-alloc volumes, truncate can
+legitimately grow i_size without allocating clusters.  System inodes and
+inline-data dinodes also retain their separate storage rules.
+
+Mirror the check in ocfs2_filecheck_validate_inode_block() as well.
+filecheck reports through its own error namespace, so malformed
+size/cluster state is logged as a filecheck invalid-inode result rather
+than via ocfs2_error(), but it must not proceed into
+ocfs2_populate_inode().
+
+Link: https://lore.kernel.org/20260519110404.1803902-4-michael.bommarito@gmail.com
+Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.")
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com
+Assisted-by: Claude:claude-opus-4-7
+Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
+Cc: Mark Fasheh <mark@fasheh.com>
+Cc: Joel Becker <jlbec@evilplan.org>
+Cc: Junxiao Bi <junxiao.bi@oracle.com>
+Cc: Changwei Ge <gechangwei@live.cn>
+Cc: Jun Piao <piaojun@huawei.com>
+Cc: Heming Zhao <heming.zhao@suse.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ocfs2/inode.c |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 60 insertions(+)
+
+--- a/fs/ocfs2/inode.c
++++ b/fs/ocfs2/inode.c
+@@ -82,6 +82,24 @@ static bool ocfs2_dinode_has_unexpected_
+       return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0;
+ }
++static bool ocfs2_dinode_has_size_without_clusters(struct super_block *sb,
++                                                 struct ocfs2_dinode *di)
++{
++      umode_t mode = le16_to_cpu(di->i_mode);
++
++      if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)
++              return false;
++      if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL)
++              return false;
++      if (!le64_to_cpu(di->i_size) || le32_to_cpu(di->i_clusters))
++              return false;
++
++      if (S_ISDIR(mode))
++              return true;
++
++      return !ocfs2_sparse_alloc(OCFS2_SB(sb)) && S_ISREG(mode);
++}
++
+ void ocfs2_set_inode_flags(struct inode *inode)
+ {
+       unsigned int flags = OCFS2_I(inode)->ip_attr;
+@@ -1563,6 +1581,33 @@ int ocfs2_validate_inode_block(struct su
+               goto bail;
+       }
++      /*
++       * Non-inline directories must not have i_size without allocated
++       * clusters: directory growth adds storage before advancing i_size,
++       * and readdir walks i_size block-by-block.  A forged directory
++       * with zero clusters and a huge i_size would repeatedly fault on
++       * holes while advancing through the claimed size.
++       *
++       * Non-inline regular files have the same invariant on non-sparse
++       * volumes.  Sparse regular files are different: truncate can
++       * legitimately grow i_size without allocating clusters, so keep
++       * the sparse-alloc carveout for S_IFREG only.  System inodes and
++       * inline-data dinodes have their own storage rules.
++       */
++      if (ocfs2_dinode_has_size_without_clusters(sb, di)) {
++              if (S_ISDIR(le16_to_cpu(di->i_mode)))
++                      rc = ocfs2_error(sb,
++                                       "Invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n",
++                                       (unsigned long long)bh->b_blocknr,
++                                       (unsigned long long)le64_to_cpu(di->i_size));
++              else
++                      rc = ocfs2_error(sb,
++                                       "Invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n",
++                                       (unsigned long long)bh->b_blocknr,
++                                       (unsigned long long)le64_to_cpu(di->i_size));
++              goto bail;
++      }
++
+       if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) {
+               struct ocfs2_inline_data *data = &di->id2.i_data;
+@@ -1767,6 +1812,21 @@ static int ocfs2_filecheck_validate_inod
+                    le16_to_cpu(di->i_mode),
+                    (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev));
+               rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
++              goto bail;
++      }
++
++      if (ocfs2_dinode_has_size_without_clusters(sb, di)) {
++              if (S_ISDIR(le16_to_cpu(di->i_mode)))
++                      mlog(ML_ERROR,
++                           "Filecheck: invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n",
++                           (unsigned long long)bh->b_blocknr,
++                           (unsigned long long)le64_to_cpu(di->i_size));
++              else
++                      mlog(ML_ERROR,
++                           "Filecheck: invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n",
++                           (unsigned long long)bh->b_blocknr,
++                           (unsigned long long)le64_to_cpu(di->i_size));
++              rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
+       }
+ bail:
diff --git a/queue-7.1/ocfs2-use-kzalloc-for-quota-recovery-bitmap-allocation.patch b/queue-7.1/ocfs2-use-kzalloc-for-quota-recovery-bitmap-allocation.patch
new file mode 100644 (file)
index 0000000..5a19dd2
--- /dev/null
@@ -0,0 +1,43 @@
+From 93c8c6ea90be9e9df8fe14048ad4e3caad0770a6 Mon Sep 17 00:00:00 2001
+From: Tristan Madani <tristan@talencesecurity.com>
+Date: Sat, 18 Apr 2026 13:10:48 +0000
+Subject: ocfs2: use kzalloc for quota recovery bitmap allocation
+
+From: Tristan Madani <tristan@talencesecurity.com>
+
+commit 93c8c6ea90be9e9df8fe14048ad4e3caad0770a6 upstream.
+
+ocfs2 quota recovery allocates a bitmap buffer with kmalloc and does not
+fully initialize it.  This can lead to use of uninitialized bits during
+quota recovery from a corrupted filesystem image.
+
+Use kzalloc instead to ensure the bitmap is zero-initialized.
+
+Link: https://lore.kernel.org/20260418131048.1052507-1-tristmd@gmail.com
+Reported-by: syzbot+7ea0b96c4ddb49fd1a70@syzkaller.appspotmail.com
+Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
+Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
+Cc: Mark Fasheh <mark@fasheh.com>
+Cc: Joel Becker <jlbec@evilplan.org>
+Cc: Junxiao Bi <junxiao.bi@oracle.com>
+Cc: Changwei Ge <gechangwei@live.cn>
+Cc: Jun Piao <piaojun@huawei.com>
+Cc: Heming Zhao <heming.zhao@suse.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ocfs2/quota_local.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/ocfs2/quota_local.c
++++ b/fs/ocfs2/quota_local.c
+@@ -302,7 +302,7 @@ static int ocfs2_add_recovery_chunk(stru
+       if (!rc)
+               return -ENOMEM;
+       rc->rc_chunk = chunk;
+-      rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS);
++      rc->rc_bitmap = kzalloc(sb->s_blocksize, GFP_NOFS);
+       if (!rc->rc_bitmap) {
+               kfree(rc);
+               return -ENOMEM;
diff --git a/queue-7.1/openrisc-add-full-instruction-cache-invalidate-functions.patch b/queue-7.1/openrisc-add-full-instruction-cache-invalidate-functions.patch
new file mode 100644 (file)
index 0000000..11614f0
--- /dev/null
@@ -0,0 +1,105 @@
+From 1be031de802ba486a7605b52eda825f128b026e2 Mon Sep 17 00:00:00 2001
+From: Stafford Horne <shorne@gmail.com>
+Date: Fri, 22 May 2026 16:56:03 +0100
+Subject: openrisc: Add full instruction cache invalidate functions
+
+From: Stafford Horne <shorne@gmail.com>
+
+commit 1be031de802ba486a7605b52eda825f128b026e2 upstream.
+
+Add functions to invalidate all cache lines which we will use for
+static_key patching.
+
+On OpenRISC there is no instruction to invalidate an entire cache so we
+loop and invalidate cache lines one by one.  This is not extremely
+expensive on OpenRISC as we usually have only a few hundred cache lines.
+
+I considered using the invalidate cache page or range functions.
+However, tracking which ranges need invalidation would have been more
+expensive than flushing all pages.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Stafford Horne <shorne@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/openrisc/include/asm/cacheflush.h |    4 ++++
+ arch/openrisc/kernel/smp.c             |   21 +++++++++++++++++++++
+ arch/openrisc/mm/cache.c               |   16 ++++++++++++++++
+ 3 files changed, 41 insertions(+)
+
+--- a/arch/openrisc/include/asm/cacheflush.h
++++ b/arch/openrisc/include/asm/cacheflush.h
+@@ -26,6 +26,7 @@ extern void local_icache_page_inv(struct
+ extern void local_dcache_range_flush(unsigned long start, unsigned long end);
+ extern void local_dcache_range_inv(unsigned long start, unsigned long end);
+ extern void local_icache_range_inv(unsigned long start, unsigned long end);
++extern void local_icache_all_inv(void);
+ /*
+  * Data cache flushing always happen on the local cpu. Instruction cache
+@@ -35,10 +36,13 @@ extern void local_icache_range_inv(unsig
+ #ifndef CONFIG_SMP
+ #define dcache_page_flush(page)      local_dcache_page_flush(page)
+ #define icache_page_inv(page)        local_icache_page_inv(page)
++#define icache_all_inv()             local_icache_all_inv()
+ #else  /* CONFIG_SMP */
+ #define dcache_page_flush(page)      local_dcache_page_flush(page)
+ #define icache_page_inv(page)        smp_icache_page_inv(page)
++#define icache_all_inv()             smp_icache_all_inv()
+ extern void smp_icache_page_inv(struct page *page);
++extern void smp_icache_all_inv(void);
+ #endif /* CONFIG_SMP */
+ /*
+--- a/arch/openrisc/kernel/smp.c
++++ b/arch/openrisc/kernel/smp.c
+@@ -346,3 +346,24 @@ void smp_icache_page_inv(struct page *pa
+       on_each_cpu(ipi_icache_page_inv, page, 1);
+ }
+ EXPORT_SYMBOL(smp_icache_page_inv);
++
++static void ipi_icache_all_inv(void *arg)
++{
++      local_icache_all_inv();
++}
++
++void smp_icache_all_inv(void)
++{
++      if (num_online_cpus() < 2) {
++              local_icache_all_inv();
++              return;
++      }
++
++      /*
++       * Ensure stores complete before we request remote icaches
++       * to invalidate.
++       */
++      mb();
++
++      on_each_cpu(ipi_icache_all_inv, NULL, 1);
++}
+--- a/arch/openrisc/mm/cache.c
++++ b/arch/openrisc/mm/cache.c
+@@ -63,6 +63,22 @@ void local_icache_page_inv(struct page *
+ }
+ EXPORT_SYMBOL(local_icache_page_inv);
++void local_icache_all_inv(void)
++{
++      if (cpu_cache_is_present(SPR_UPR_ICP)) {
++              unsigned long iccfgr = mfspr(SPR_ICCFGR);
++              unsigned long sets = 1 << ((iccfgr & SPR_ICCFGR_NCS) >> 3);
++              unsigned long block_size = 16 << ((iccfgr & SPR_ICCFGR_CBS) >> 7);
++              unsigned long paddr = 0;
++              unsigned long end = sets * block_size;
++
++              while (paddr < end) {
++                      mtspr(SPR_ICBIR, paddr);
++                      paddr += block_size;
++              }
++      }
++}
++
+ void local_dcache_range_flush(unsigned long start, unsigned long end)
+ {
+       cache_loop(start, end, SPR_DCBFR, SPR_UPR_DCP);
diff --git a/queue-7.1/power-supply-bq257xx-fix-vsysmin-clamping-logic.patch b/queue-7.1/power-supply-bq257xx-fix-vsysmin-clamping-logic.patch
new file mode 100644 (file)
index 0000000..656861d
--- /dev/null
@@ -0,0 +1,51 @@
+From b6c6b9260a92dacef6edef8e93bc2767b86b1dfe Mon Sep 17 00:00:00 2001
+From: Alexey Charkov <alchark@flipper.net>
+Date: Wed, 3 Jun 2026 00:10:50 +0400
+Subject: power: supply: bq257xx: Fix VSYSMIN clamping logic
+
+From: Alexey Charkov <alchark@flipper.net>
+
+commit b6c6b9260a92dacef6edef8e93bc2767b86b1dfe upstream.
+
+The minimal system voltage (VSYSMIN) is meant to protect the battery from
+dangerous over-discharge. When the device tree provides a value for the
+minimum design voltage of the battery, the user should not be allowed to
+set a lower VSYSMIN, as that would defeat the purpose of this protection.
+
+Flip the clamping logic when setting VSYSMIN to ensure that battery design
+voltage is respected.
+
+Cc: stable@vger.kernel.org
+Fixes: 1cc017b7f9c7 ("power: supply: bq257xx: Add support for BQ257XX charger")
+Tested-by: Chris Morgan <macromorgan@hotmail.com>
+Signed-off-by: Alexey Charkov <alchark@flipper.net>
+Link: https://patch.msgid.link/20260603-bq25792-v7-2-d487bed276d0@flipper.net
+Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/power/supply/bq257xx_charger.c |    7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/drivers/power/supply/bq257xx_charger.c
++++ b/drivers/power/supply/bq257xx_charger.c
+@@ -128,9 +128,8 @@ static int bq25703_get_min_vsys(struct b
+  * @vsys: voltage value to set in uV.
+  *
+  * This function takes a requested minimum system voltage value, clamps
+- * it between the minimum supported value by the charger and a user
+- * defined minimum system value, and then writes the value to the
+- * appropriate register.
++ * it between the user defined minimum system value and the maximum supported
++ * value by the charger, and then writes the value to the appropriate register.
+  *
+  * Return: Returns 0 on success or error if an error occurs.
+  */
+@@ -139,7 +138,7 @@ static int bq25703_set_min_vsys(struct b
+       unsigned int reg;
+       int vsys_min = pdata->vsys_min;
+-      vsys = clamp(vsys, BQ25703_MINVSYS_MIN_UV, vsys_min);
++      vsys = clamp(vsys, vsys_min, BQ25703_MINVSYS_MAX_UV);
+       reg = ((vsys - BQ25703_MINVSYS_MIN_UV) / BQ25703_MINVSYS_STEP_UV);
+       reg = FIELD_PREP(BQ25703_MINVSYS_MASK, reg);
diff --git a/queue-7.1/power-supply-charger-manager-fix-refcount-leak-in-is_full_charged.patch b/queue-7.1/power-supply-charger-manager-fix-refcount-leak-in-is_full_charged.patch
new file mode 100644 (file)
index 0000000..88865da
--- /dev/null
@@ -0,0 +1,43 @@
+From 4373cfa38ead58f980362c841b0d0bdf8c4d956c Mon Sep 17 00:00:00 2001
+From: WenTao Liang <vulab@iscas.ac.cn>
+Date: Thu, 11 Jun 2026 08:53:21 +0800
+Subject: power: supply: charger-manager: fix refcount leak in is_full_charged()
+
+From: WenTao Liang <vulab@iscas.ac.cn>
+
+commit 4373cfa38ead58f980362c841b0d0bdf8c4d956c upstream.
+
+In is_full_charged(), power_supply_get_by_name() is called to
+obtain a reference to the fuel_gauge power supply. If the
+voltage check (uV >= desc->fullbatt_uV) succeeds, the function
+returns true directly without releasing the reference, leaking
+the refcount.
+
+Fix this by setting a flag and jumping to the out label where
+power_supply_put() properly drops the reference.
+
+Cc: stable@vger.kernel.org
+Fixes: e132fc6bb89b ("power: supply: charger-manager: Make decisions focussed on battery status")
+Signed-off-by: WenTao Liang <vulab@iscas.ac.cn>
+Link: https://patch.msgid.link/20260611005322.53096-1-vulab@iscas.ac.cn
+Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/power/supply/charger-manager.c |    6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/power/supply/charger-manager.c
++++ b/drivers/power/supply/charger-manager.c
+@@ -303,8 +303,10 @@ static bool is_full_charged(struct charg
+                       if (cm->battery_status == POWER_SUPPLY_STATUS_FULL
+                                       && desc->fullbatt_vchkdrop_uV)
+                               uV += desc->fullbatt_vchkdrop_uV;
+-                      if (uV >= desc->fullbatt_uV)
+-                              return true;
++                      if (uV >= desc->fullbatt_uV) {
++                              is_full = true;
++                              goto out;
++                      }
+               }
+       }
diff --git a/queue-7.1/power-supply-cpcap-battery-fix-missing-nvmem_device_put-causing-reference-leak.patch b/queue-7.1/power-supply-cpcap-battery-fix-missing-nvmem_device_put-causing-reference-leak.patch
new file mode 100644 (file)
index 0000000..e27f457
--- /dev/null
@@ -0,0 +1,51 @@
+From a2c14ff63e0e02e3c832385e523e9cc81301171c Mon Sep 17 00:00:00 2001
+From: Ma Ke <make24@iscas.ac.cn>
+Date: Fri, 24 Apr 2026 09:10:13 +0800
+Subject: power: supply: cpcap-battery: Fix missing nvmem_device_put() causing reference leak
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ma Ke <make24@iscas.ac.cn>
+
+commit a2c14ff63e0e02e3c832385e523e9cc81301171c upstream.
+
+In cpcap_battery_detect_battery_type(), the reference to an nvmem
+device obtained via nvmem_device_find() is not released with
+nvmem_device_put() on the success or read-failure paths, causing a
+permanent reference leak. The driver’s retry logic on subsequent
+battery property reads can compound this leak, preventing the nvmem
+device from ever being freed.
+
+Found by code review.
+
+Signed-off-by: Ma Ke <make24@iscas.ac.cn>
+Cc: stable@vger.kernel.org
+Fixes: fd46821e85de ("power: supply: cpcap-battery: Add battery type auto detection for mapphone devices")
+Link: https://patch.msgid.link/20260424011013.879639-1-make24@iscas.ac.cn
+Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/power/supply/cpcap-battery.c |   11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/power/supply/cpcap-battery.c
++++ b/drivers/power/supply/cpcap-battery.c
+@@ -439,10 +439,13 @@ static void cpcap_battery_detect_battery
+       if (IS_ERR_OR_NULL(nvmem)) {
+               ddata->check_nvmem = true;
+               dev_info_once(ddata->dev, "Can not find battery nvmem device. Assuming generic lipo battery\n");
+-      } else if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) {
+-              battery_id = 0;
+-              ddata->check_nvmem = true;
+-              dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n");
++      } else {
++              if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) {
++                      battery_id = 0;
++                      ddata->check_nvmem = true;
++                      dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n");
++              }
++              nvmem_device_put(nvmem);
+       }
+       switch (battery_id) {
diff --git a/queue-7.1/power-supply-max17042-fix-of-node-reference-imbalance.patch b/queue-7.1/power-supply-max17042-fix-of-node-reference-imbalance.patch
new file mode 100644 (file)
index 0000000..09c5ff4
--- /dev/null
@@ -0,0 +1,39 @@
+From 68d234144b7dffd1f50b07ba74d0d6e833ef43a4 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Tue, 7 Apr 2026 14:33:38 +0200
+Subject: power: supply: max17042: fix OF node reference imbalance
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 68d234144b7dffd1f50b07ba74d0d6e833ef43a4 upstream.
+
+The driver reuses the OF node of the parent multi-function device but
+fails to take another reference to balance the one dropped by the
+platform bus code when unbinding the MFD and deregistering the child
+devices.
+
+Fix this by using the intended helper for reusing OF nodes.
+
+Fixes: 0cd4f1f77ad4 ("power: supply: max17042: add platform driver variant")
+Cc: stable@vger.kernel.org     # 6.14
+Cc: Dzmitry Sankouski <dsankouski@gmail.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Link: https://patch.msgid.link/20260407123338.2677375-1-johan@kernel.org
+Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/power/supply/max17042_battery.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/power/supply/max17042_battery.c
++++ b/drivers/power/supply/max17042_battery.c
+@@ -1254,7 +1254,8 @@ static int max17042_platform_probe(struc
+       if (!i2c)
+               return -EINVAL;
+-      dev->of_node = dev->parent->of_node;
++      device_set_of_node_from_dev(dev, dev->parent);
++
+       id = platform_get_device_id(pdev);
+       irq = platform_get_irq(pdev, 0);
diff --git a/queue-7.1/powerpc-dt_cpu_ftrs-set-cpu_ftr_p11_pvr-for-power11-and-later-processors.patch b/queue-7.1/powerpc-dt_cpu_ftrs-set-cpu_ftr_p11_pvr-for-power11-and-later-processors.patch
new file mode 100644 (file)
index 0000000..231334a
--- /dev/null
@@ -0,0 +1,73 @@
+From e4de1b9cb3b5c981e4fe9bca253a7fb9161f5acd Mon Sep 17 00:00:00 2001
+From: Amit Machhiwal <amachhiw@linux.ibm.com>
+Date: Sun, 14 Jun 2026 23:04:37 +0530
+Subject: powerpc/dt_cpu_ftrs: Set CPU_FTR_P11_PVR for Power11 and later processors
+
+From: Amit Machhiwal <amachhiw@linux.ibm.com>
+
+commit e4de1b9cb3b5c981e4fe9bca253a7fb9161f5acd upstream.
+
+When using device tree CPU features (dt-cpu-ftrs), the kernel bypasses
+the traditional cputable-based CPU identification and instead derives
+CPU features from the device tree's "ibm,powerpc-cpu-features" node
+provided by firmware.
+
+However, CPU_FTR_P11_PVR is a kernel-internal feature flag used to
+identify Power11 and later processors, and is not represented in the
+device tree's ISA feature set. While ISA v3.1 support (indicated by
+CPU_FTR_ARCH_31) is present on both Power10 and Power11, the
+CPU_FTR_P11_PVR flag is specifically needed by code that must
+distinguish between Power10 and Power11 processors.
+
+Without this flag set, code that checks for Power11 using
+cpu_has_feature(CPU_FTR_P11_PVR) will incorrectly return false on
+Power11+ systems using dt-cpu-ftrs, leading to incorrect behavior.
+
+This issue manifests specifically in powernv environments (bare-metal
+or QEMU TCG with powernv machine type), where skiboot/OPAL firmware
+provides the "ibm,powerpc-cpu-features" node, causing the kernel to
+use dt-cpu-ftrs. The issue does not affect pseries guests, where SLOF
+firmware does not provide this node, causing the kernel to fall back
+to the traditional cputable path (identify_cpu) which correctly sets
+CPU_FTR_P11_PVR during PVR-based CPU identification.
+
+In powernv TCG guests, the missing flag causes KVM code to trigger
+warnings when attempting to create KVM guests, as cpu_features shows
+0x000c00eb8f4fb187 (missing bit 53) instead of the correct
+0x002c00eb8f4fb187 (with bit 53 set).
+
+Fix this by setting CPU_FTR_P11_PVR for all processors with
+PVR >= PVR_POWER11 when ISA v3.1 support is detected in
+cpufeatures_setup_start(). This approach ensures forward
+compatibility with future processor generations.
+
+Fixes: 96e266e3bcd6 ("KVM: PPC: Book3S HV: Add Power11 capability support for Nested PAPR guests")
+Cc: stable@vger.kernel.org # v6.13+
+Signed-off-by: Amit Machhiwal <amachhiw@linux.ibm.com>
+Reviewed-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
+Reviewed-by: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
+Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
+Link: https://patch.msgid.link/20260614173437.26352-1-amachhiw@linux.ibm.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/powerpc/kernel/dt_cpu_ftrs.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/arch/powerpc/kernel/dt_cpu_ftrs.c
++++ b/arch/powerpc/kernel/dt_cpu_ftrs.c
+@@ -704,6 +704,15 @@ static void __init cpufeatures_setup_sta
+       if (isa >= ISA_V3_1) {
+               cur_cpu_spec->cpu_features |= CPU_FTR_ARCH_31;
+               cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_1;
++
++              /*
++               * CPU_FTR_P11_PVR is a kernel-internal flag to identify
++               * Power11 and later processors. While ISA v3.1 is supported
++               * by Power10+, this flag specifically indicates Power11+
++               * for code that needs to distinguish between P10 and P11.
++               */
++              if (PVR_VER(mfspr(SPRN_PVR)) >= PVR_POWER11)
++                      cur_cpu_spec->cpu_features |= CPU_FTR_P11_PVR;
+       }
+ }
diff --git a/queue-7.1/proc-only-bump-parent-nlink-when-registering-directories.patch b/queue-7.1/proc-only-bump-parent-nlink-when-registering-directories.patch
new file mode 100644 (file)
index 0000000..334c437
--- /dev/null
@@ -0,0 +1,90 @@
+From 16b02eb4b9b272c221255c20d34ccd5db53a3ed3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= <kwilczynski@kernel.org>
+Date: Sat, 13 Jun 2026 21:10:05 +0000
+Subject: proc: only bump parent nlink when registering directories
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Krzysztof Wilczyński <kwilczynski@kernel.org>
+
+commit 16b02eb4b9b272c221255c20d34ccd5db53a3ed3 upstream.
+
+proc_register() increments the parent directory's link count for every
+entry it registers, while remove_proc_entry() and remove_proc_subtree()
+decrement it only when the removed entry is a directory.  Regular files
+thus inflate the parent's count while they exist, and leak one link
+permanently on every create and remove cycle.
+
+For example, /proc/bus/pci/00 with twenty-two device files and no
+subdirectories reports nlink 24 instead of 2, and SR-IOV VF enable
+and disable cycles, each creating and removing the VF config space
+entries under /proc/bus/pci/<bus>, inflate the link count of that
+directory without bound.
+
+Before commit e06689bf5701 ("proc: change ->nlink under
+proc_subdir_lock"), the increment lived in proc_mkdir_data() and
+proc_create_mount_point(), and was therefore applied only to
+directories.  Moving it into proc_register() to bring it under
+proc_subdir_lock dropped the S_ISDIR check.
+
+Thus, move the nlink accounting into pde_subdir_insert() and
+pde_erase(), only updating it for directories in both, so the link
+count is always changed together with the directory entry itself.
+
+Fixes: e06689bf5701 ("proc: change ->nlink under proc_subdir_lock")
+Cc: stable@vger.kernel.org # v5.5+
+Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
+Link: https://patch.msgid.link/20260613211005.921692-1-kwilczynski@kernel.org
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/generic.c |    9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+--- a/fs/proc/generic.c
++++ b/fs/proc/generic.c
+@@ -112,6 +112,8 @@ static bool pde_subdir_insert(struct pro
+       /* Add new node and rebalance tree. */
+       rb_link_node(&de->subdir_node, parent, new);
+       rb_insert_color(&de->subdir_node, root);
++      if (S_ISDIR(de->mode))
++              dir->nlink++;
+       return true;
+ }
+@@ -404,7 +406,6 @@ struct proc_dir_entry *proc_register(str
+               write_unlock(&proc_subdir_lock);
+               goto out_free_inum;
+       }
+-      dir->nlink++;
+       write_unlock(&proc_subdir_lock);
+       return dp;
+@@ -702,6 +703,8 @@ static void pde_erase(struct proc_dir_en
+ {
+       rb_erase(&pde->subdir_node, &parent->subdir);
+       RB_CLEAR_NODE(&pde->subdir_node);
++      if (S_ISDIR(pde->mode))
++              parent->nlink--;
+ }
+ /*
+@@ -727,8 +730,6 @@ void remove_proc_entry(const char *name,
+                       de = NULL;
+               } else {
+                       pde_erase(de, parent);
+-                      if (S_ISDIR(de->mode))
+-                              parent->nlink--;
+               }
+       }
+       write_unlock(&proc_subdir_lock);
+@@ -787,8 +788,6 @@ int remove_proc_subtree(const char *name
+                       continue;
+               }
+               next = de->parent;
+-              if (S_ISDIR(de->mode))
+-                      next->nlink--;
+               write_unlock(&proc_subdir_lock);
+               proc_entry_rundown(de);
diff --git a/queue-7.1/remoteproc-qcom-fix-leak-when-custom-dump_segments-addition-fails.patch b/queue-7.1/remoteproc-qcom-fix-leak-when-custom-dump_segments-addition-fails.patch
new file mode 100644 (file)
index 0000000..a70f264
--- /dev/null
@@ -0,0 +1,63 @@
+From ecf9fc18e62c58eae1ceb65dab2bccb8a724de2d Mon Sep 17 00:00:00 2001
+From: Wasim Nazir <wasim.nazir@oss.qualcomm.com>
+Date: Wed, 18 Mar 2026 17:19:16 +0530
+Subject: remoteproc: qcom: Fix leak when custom dump_segments addition fails
+
+From: Wasim Nazir <wasim.nazir@oss.qualcomm.com>
+
+commit ecf9fc18e62c58eae1ceb65dab2bccb8a724de2d upstream.
+
+Free allocated minidump_region 'name' in qcom_add_minidump_segments()
+when failing before adding the region to 'dump_segments'. Otherwise,
+the 'name' is not tracked and is never freed by qcom_minidump_cleanup().
+
+Return error when adding to 'dump_segments' fails.
+
+Cc: stable@vger.kernel.org # v5.11
+Fixes: 8ed8485c4f05 ("remoteproc: qcom: Add capability to collect minidumps")
+Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
+Signed-off-by: Wasim Nazir <wasim.nazir@oss.qualcomm.com>
+Link: https://lore.kernel.org/r/20260318-rproc-memleak-v2-1-ade70ab858f2@oss.qualcomm.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/remoteproc/qcom_common.c |   14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+--- a/drivers/remoteproc/qcom_common.c
++++ b/drivers/remoteproc/qcom_common.c
+@@ -109,6 +109,7 @@ static int qcom_add_minidump_segments(st
+       struct minidump_region __iomem *ptr;
+       struct minidump_region region;
+       int seg_cnt, i;
++      int ret = 0;
+       dma_addr_t da;
+       size_t size;
+       char *name;
+@@ -129,17 +130,22 @@ static int qcom_add_minidump_segments(st
+               if (le32_to_cpu(region.valid) == MINIDUMP_REGION_VALID) {
+                       name = kstrndup(region.name, MAX_REGION_NAME_LENGTH - 1, GFP_KERNEL);
+                       if (!name) {
+-                              iounmap(ptr);
+-                              return -ENOMEM;
++                              ret = -ENOMEM;
++                              break;
+                       }
+                       da = le64_to_cpu(region.address);
+                       size = le64_to_cpu(region.size);
+-                      rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, name);
++                      ret = rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t,
++                                                              name);
++                      if (ret) {
++                              kfree(name);
++                              break;
++                      }
+               }
+       }
+       iounmap(ptr);
+-      return 0;
++      return ret;
+ }
+ void qcom_minidump(struct rproc *rproc, unsigned int minidump_id,
diff --git a/queue-7.1/remoteproc-xlnx-check-remote-core-state.patch b/queue-7.1/remoteproc-xlnx-check-remote-core-state.patch
new file mode 100644 (file)
index 0000000..e963ccf
--- /dev/null
@@ -0,0 +1,180 @@
+From a48df51d23138388900995add2854cda4aa68e55 Mon Sep 17 00:00:00 2001
+From: Tanmay Shah <tanmay.shah@amd.com>
+Date: Tue, 28 Apr 2026 15:18:56 -0700
+Subject: remoteproc: xlnx: Check remote core state
+
+From: Tanmay Shah <tanmay.shah@amd.com>
+
+commit a48df51d23138388900995add2854cda4aa68e55 upstream.
+
+The remote state is set to RPROC_DETACHED if the resource table is found
+in the memory. However, this can be wrong if the remote is not started,
+but firmware is still loaded in the memory. Use PM_GET_NODE_STATUS call
+to the firmware to request the state of the RPU node. If the RPU is
+actually out of reset and running, only then move the remote state to
+RPROC_DETACHED, otherwise keep the remote state to RPROC_OFFLINE.
+
+Signed-off-by: Tanmay Shah <tanmay.shah@amd.com>
+Fixes: bca4b02ef92e ("remoteproc: xlnx: Add attach detach support")
+Reviewed-by: Beleswar Padhi <b-padhi@ti.com>
+Acked-by: Michal Simek <michal.simek@amd.com>
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/r/20260428221855.313752-1-tanmay.shah@amd.com
+Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/firmware/xilinx/zynqmp.c        |   28 +++++++++++++++++++
+ drivers/remoteproc/xlnx_r5_remoteproc.c |   46 +++++++++++++++++++++++++-------
+ include/linux/firmware/xlnx-zynqmp.h    |   21 ++++++++++++++
+ 3 files changed, 85 insertions(+), 10 deletions(-)
+
+--- a/drivers/firmware/xilinx/zynqmp.c
++++ b/drivers/firmware/xilinx/zynqmp.c
+@@ -1451,6 +1451,34 @@ int zynqmp_pm_get_node_status(const u32
+ EXPORT_SYMBOL_GPL(zynqmp_pm_get_node_status);
+ /**
++ * zynqmp_pm_get_rpu_node_status - PM call to request a RPU node's current power state
++ * @node:             ID of the RPU component or sub-system in question
++ * @status:           Current operating state of the requested RPU node.
++ * @requirements:     Current requirements asserted on the RPU node.
++ * @usage:            Usage information, used for RPU slave nodes only:
++ *                    PM_USAGE_NO_MASTER      - No master is currently using
++ *                                              the node
++ *                    PM_USAGE_CURRENT_MASTER - Only requesting master is
++ *                                              currently using the node
++ *                    PM_USAGE_OTHER_MASTER   - Only other masters are
++ *                                              currently using the node
++ *                    PM_USAGE_BOTH_MASTERS   - Both the current and at least
++ *                                              one other master is currently
++ *                                              using the node
++ *
++ * Return:            Returns status, either success or error+reason
++ */
++int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status,
++                                u32 *const requirements, u32 *const usage)
++{
++      if (zynqmp_pm_feature(PM_GET_NODE_STATUS) < PM_API_VERSION_2)
++              return -EOPNOTSUPP;
++
++      return zynqmp_pm_get_node_status(node, status, requirements, usage);
++}
++EXPORT_SYMBOL_GPL(zynqmp_pm_get_rpu_node_status);
++
++/**
+  * zynqmp_pm_force_pwrdwn - PM call to request for another PU or subsystem to
+  *             be powered down forcefully
+  * @node:  Node ID of the targeted PU or subsystem
+--- a/drivers/remoteproc/xlnx_r5_remoteproc.c
++++ b/drivers/remoteproc/xlnx_r5_remoteproc.c
+@@ -948,16 +948,6 @@ static struct zynqmp_r5_core *zynqmp_r5_
+               goto free_rproc;
+       }
+-      /*
+-       * If firmware is already available in the memory then move rproc state
+-       * to DETACHED. Firmware can be preloaded via debugger or by any other
+-       * agent (processors) in the system.
+-       * If firmware isn't available in the memory and resource table isn't
+-       * found, then rproc state remains OFFLINE.
+-       */
+-      if (!zynqmp_r5_get_rsc_table_va(r5_core))
+-              r5_rproc->state = RPROC_DETACHED;
+-
+       r5_core->rproc = r5_rproc;
+       return r5_core;
+@@ -1210,6 +1200,7 @@ static int zynqmp_r5_core_init(struct zy
+ {
+       struct device *dev = cluster->dev;
+       struct zynqmp_r5_core *r5_core;
++      u32 req, usage, status;
+       int ret = -EINVAL, i;
+       r5_core = cluster->r5_cores[0];
+@@ -1255,6 +1246,41 @@ static int zynqmp_r5_core_init(struct zy
+               ret = zynqmp_r5_get_sram_banks(r5_core);
+               if (ret)
+                       return ret;
++
++              /*
++               * It is possible that firmware is loaded into the memory, but
++               * RPU (remote) is not running. In such case, RPU state will be
++               * moved to RPROC_DETACHED wrongfully. To avoid it first make
++               * sure RPU is power-on and out of reset before parsing for the
++               * resource table.
++               */
++              ret = zynqmp_pm_get_rpu_node_status(r5_core->pm_domain_id,
++                                                  &status, &req, &usage);
++              if (ret) {
++                      dev_warn(r5_core->dev,
++                               "failed to get rpu node status, err %d\n", ret);
++                      continue;
++              }
++
++              /*
++               * If RPU state is power on and out of reset i.e. running, then
++               * assign RPROC_DETACHED state. If the RPU is not out of reset
++               * then do not attempt to attach to the remote processor.
++               */
++              if (status == PM_NODE_RUNNING) {
++                      /*
++                       * Not all the firmware that is running on the remote
++                       * core is expected to have the resource table. The
++                       * firmware might not use RPMsg at all, and in that case
++                       * resource table becomes irrelevant. However, we still
++                       * need to make sure that running core is not reported
++                       * as offline. so do not decide remote core state based
++                       * on the resource table availability
++                       */
++                      if (zynqmp_r5_get_rsc_table_va(r5_core))
++                              dev_dbg(r5_core->dev, "rsc tbl not found\n");
++                      r5_core->rproc->state = RPROC_DETACHED;
++              }
+       }
+       return 0;
+--- a/include/linux/firmware/xlnx-zynqmp.h
++++ b/include/linux/firmware/xlnx-zynqmp.h
+@@ -543,6 +543,18 @@ enum pm_gem_config_type {
+ };
+ /**
++ * enum pm_node_status - Device node status provided by xilpm fw
++ * @PM_NODE_UNUSED: Device is not used
++ * @PM_NODE_RUNNING: Device is power-on and out of reset
++ * @PM_NODE_HALT: Device is power-on but in the reset state
++ */
++enum pm_node_status {
++      PM_NODE_UNUSED = 0,
++      PM_NODE_RUNNING = 1,
++      PM_NODE_HALT = 12,
++};
++
++/**
+  * struct zynqmp_pm_query_data - PM query data
+  * @qid:      query ID
+  * @arg1:     Argument 1 of query data
+@@ -630,6 +642,8 @@ int zynqmp_pm_set_rpu_mode(u32 node_id,
+ int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode);
+ int zynqmp_pm_get_node_status(const u32 node, u32 *const status,
+                             u32 *const requirements, u32 *const usage);
++int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status,
++                                u32 *const requirements, u32 *const usage);
+ int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value);
+ int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config,
+                            u32 value);
+@@ -938,6 +952,13 @@ static inline int zynqmp_pm_get_node_sta
+ {
+       return -ENODEV;
+ }
++
++static inline int zynqmp_pm_get_rpu_node_status(const u32 node, u32 *const status,
++                                              u32 *const requirements,
++                                              u32 *const usage)
++{
++      return -ENODEV;
++}
+ static inline int zynqmp_pm_set_sd_config(u32 node,
+                                         enum pm_sd_config_type config,
diff --git a/queue-7.1/riscv-cacheinfo-fix-node-reference-leak-in-populate_cache_leaves.patch b/queue-7.1/riscv-cacheinfo-fix-node-reference-leak-in-populate_cache_leaves.patch
new file mode 100644 (file)
index 0000000..d88de7b
--- /dev/null
@@ -0,0 +1,41 @@
+From bf4a195f063b0a0805c1417f6aad1dd32ea48f0f Mon Sep 17 00:00:00 2001
+From: Zishun Yi <vulab@iscas.ac.cn>
+Date: Sat, 6 Jun 2026 20:17:58 -0600
+Subject: riscv: cacheinfo: Fix node reference leak in populate_cache_leaves
+
+From: Zishun Yi <vulab@iscas.ac.cn>
+
+commit bf4a195f063b0a0805c1417f6aad1dd32ea48f0f upstream.
+
+Currently, the while loop drops the reference to prev in each iteration.
+If the loop terminates early due to a break, the final of_node_put(np)
+correctly drops the reference to the current node.
+
+However, if the loop terminates naturally because np == NULL, calling
+of_node_put(np) is a no-op. This leaves the last valid node stored in
+prev without its reference dropped, resulting in a node reference leak.
+
+Fix this by changing the final `of_node_put(np)` to `of_node_put(prev)`.
+
+Fixes: 94f9bf118f1e ("RISC-V: Fix of_node_* refcount")
+Cc: stable@vger.kernel.org
+Assisted-by: Gemini:gemini-3.1-pro
+Signed-off-by: Zishun Yi <vulab@iscas.ac.cn>
+Link: https://patch.msgid.link/20260509074040.1747800-1-vulab@iscas.ac.cn
+Signed-off-by: Paul Walmsley <pjw@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/riscv/kernel/cacheinfo.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/riscv/kernel/cacheinfo.c
++++ b/arch/riscv/kernel/cacheinfo.c
+@@ -133,7 +133,7 @@ int populate_cache_leaves(unsigned int c
+                       ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+               levels = level;
+       }
+-      of_node_put(np);
++      of_node_put(prev);
+       return 0;
+ }
diff --git a/queue-7.1/scsi-sas-skip-opt_sectors-when-dma-reports-no-real-optimization-hint.patch b/queue-7.1/scsi-sas-skip-opt_sectors-when-dma-reports-no-real-optimization-hint.patch
new file mode 100644 (file)
index 0000000..8447dc4
--- /dev/null
@@ -0,0 +1,143 @@
+From be8fcd4a8217a916344c88a4b1b84f5736dda17e Mon Sep 17 00:00:00 2001
+From: Ionut Nechita <ionut.nechita@windriver.com>
+Date: Tue, 19 May 2026 16:52:33 +0300
+Subject: scsi: sas: Skip opt_sectors when DMA reports no real optimization hint
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ionut Nechita <ionut.nechita@windriver.com>
+
+commit be8fcd4a8217a916344c88a4b1b84f5736dda17e upstream.
+
+sas_host_setup() unconditionally sets shost->opt_sectors from
+dma_opt_mapping_size().
+
+When the IOMMU is disabled or in passthrough mode and no DMA ops provide
+an opt_mapping_size callback, dma_opt_mapping_size() returns
+min(dma_max_mapping_size(), SIZE_MAX) which equals
+dma_max_mapping_size() — a hard upper bound, not an optimization hint.
+
+On a Dell PowerEdge R750 with mpt3sas (Broadcom SAS3816, FW 33.15.00.00)
+and intel_iommu=off the following values are observed:
+
+  dma_opt_mapping_size()  = dma_max_mapping_size() (no real hint)
+  shost->max_sectors      = 32767
+  opt_sectors             = min(32767, huge >> 9) = 32767
+  optimal_io_size         = 32767 << 9 = 16776704
+                          → round_down(16776704, 4096) = 16773120
+
+The SAS disk (SAMSUNG MZILT800HBHQ0D3) does not report an Optimal
+Transfer Length in VPD page B0, so sdkp->opt_xfer_blocks remains 0.
+
+sd_revalidate_disk() then uses min_not_zero(0, opt_sectors) =
+opt_sectors, propagating the bogus value into the block device's
+optimal_io_size (visible as OPT-IO = 16773120 in lsblk --topology).
+
+mkfs.xfs picks up optimal_io_size and minimum_io_size and computes:
+
+  swidth = 16773120 / 4096 = 4095
+  sunit  = 8192 / 4096     = 2
+
+Since 4095 % 2 != 0, XFS rejects the geometry:
+
+  SB stripe unit sanity check failed
+
+This makes it impossible to create XFS filesystems (e.g. for
+/var/lib/docker) during system bootstrap.
+
+Fix this by introducing a sas_dma_setup_opt_sectors() helper that sets
+opt_sectors only when dma_opt_mapping_size() is strictly less than
+dma_max_mapping_size(), indicating a genuine DMA optimization
+constraint.
+
+The helper computes min(opt_sectors, max_sectors) first, then rounds
+down to a power of two so that filesystem geometry calculations always
+produce clean results.
+
+When the two DMA values are equal, no backend provided a real hint, so
+opt_sectors stays at 0 ("no preference").
+
+[mkp: implemented hch's suggestion]
+
+Fixes: 4cbfca5f7750 ("scsi: scsi_transport_sas: cap shost opt_sectors according to DMA optimal limit")
+Cc: stable@vger.kernel.org
+Reviewed-by: John Garry <john.g.garry@oracle.com>
+Signed-off-by: Ionut Nechita <ionut.nechita@windriver.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260519135238.373784-2-ionut.nechita@windriver.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/scsi/scsi_transport_sas.c |   41 +++++++++++++++++++++++++++++++++-----
+ 1 file changed, 36 insertions(+), 5 deletions(-)
+
+--- a/drivers/scsi/scsi_transport_sas.c
++++ b/drivers/scsi/scsi_transport_sas.c
+@@ -27,6 +27,7 @@
+ #include <linux/module.h>
+ #include <linux/jiffies.h>
+ #include <linux/err.h>
++#include <linux/log2.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
+ #include <linux/blkdev.h>
+@@ -220,12 +221,45 @@ static int sas_bsg_initialize(struct Scs
+  * SAS host attributes
+  */
++/*
++ * Set shost->opt_sectors from the DMA optimal mapping size, but only
++ * when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(),
++ * indicating a genuine optimization hint from an IOMMU or DMA backend.
++ * When the two are equal (e.g. IOMMU disabled / passthrough), no real
++ * hint exists, so leave opt_sectors at 0 to avoid bogus optimal_io_size
++ * values that break filesystem geometry (e.g. mkfs.xfs stripe alignment).
++ */
++static void sas_dma_setup_opt_sectors(struct Scsi_Host *shost)
++{
++      struct device *dma_dev = shost->dma_dev;
++      size_t opt = dma_opt_mapping_size(dma_dev);
++      size_t max = dma_max_mapping_size(dma_dev);
++      unsigned int opt_sectors;
++
++      /* opt >= max means no real hint was provided by the DMA layer */
++      if (opt >= max)
++              return;
++
++      /* Clamp to max_sectors to avoid overflow in sector arithmetic */
++      opt_sectors = min_t(unsigned int, opt >> SECTOR_SHIFT,
++                          shost->max_sectors);
++
++      /* Guard against zero before rounddown_pow_of_two() */
++      if (!opt_sectors)
++              return;
++
++      /*
++       * Round down to power-of-two so filesystem geometry calculations
++       * (e.g. XFS stripe width/unit) always produce clean divisors.
++       */
++      shost->opt_sectors = rounddown_pow_of_two(opt_sectors);
++}
++
+ static int sas_host_setup(struct transport_container *tc, struct device *dev,
+                         struct device *cdev)
+ {
+       struct Scsi_Host *shost = dev_to_shost(dev);
+       struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
+-      struct device *dma_dev = shost->dma_dev;
+       INIT_LIST_HEAD(&sas_host->rphy_list);
+       mutex_init(&sas_host->lock);
+@@ -237,10 +271,7 @@ static int sas_host_setup(struct transpo
+               dev_printk(KERN_ERR, dev, "fail to a bsg device %d\n",
+                          shost->host_no);
+-      if (dma_dev->dma_mask) {
+-              shost->opt_sectors = min_t(unsigned int, shost->max_sectors,
+-                              dma_opt_mapping_size(dma_dev) >> SECTOR_SHIFT);
+-      }
++      sas_dma_setup_opt_sectors(shost);
+       return 0;
+ }
diff --git a/queue-7.1/scsi-smartpqi-use-shost_to_hba-in-pqi_scan_finished.patch b/queue-7.1/scsi-smartpqi-use-shost_to_hba-in-pqi_scan_finished.patch
new file mode 100644 (file)
index 0000000..4f86cc3
--- /dev/null
@@ -0,0 +1,41 @@
+From 57db1307afb1f83d45f5ff53b93f8d040100d13e Mon Sep 17 00:00:00 2001
+From: Martin Wilck <martin.wilck@suse.com>
+Date: Wed, 13 May 2026 19:42:35 +0200
+Subject: scsi: smartpqi: Use shost_to_hba() in pqi_scan_finished()
+
+From: Martin Wilck <martin.wilck@suse.com>
+
+commit 57db1307afb1f83d45f5ff53b93f8d040100d13e upstream.
+
+shost_to_hba() is used everywhere except to obtain pqi_ctrl_info from
+shosti, except in pqi_scan_finished(), where shost_priv() is used.  This
+causes one pointer dereference to be missed, as shost->hostdata is a
+pointer in smartpqi. Fix it.
+
+Fixes: 6c223761eb54 ("smartpqi: initial commit of Microsemi smartpqi driver")
+Signed-off-by: Martin Wilck <martin.wilck@suse.com>
+Reviewed-by: Don Brace <don.brace@microchip.com>
+Cc: Don Brace <don.brace@microchip.com>
+Cc: storagedev@microchip.com
+Cc: stable@vger.kernel.org
+Reviewed-by: Hannes Reinecke <hare@kernel.org>
+Reviewed-by: Hannes Reinecke <hare@suse.de>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260513174236.430465-2-mwilck@suse.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/scsi/smartpqi/smartpqi_init.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/scsi/smartpqi/smartpqi_init.c
++++ b/drivers/scsi/smartpqi/smartpqi_init.c
+@@ -2642,7 +2642,7 @@ static int pqi_scan_finished(struct Scsi
+ {
+       struct pqi_ctrl_info *ctrl_info;
+-      ctrl_info = shost_priv(shost);
++      ctrl_info = shost_to_hba(shost);
+       return !mutex_is_locked(&ctrl_info->scan_mutex);
+ }
diff --git a/queue-7.1/selftests-landlock-test-scope_signal-on-the-sigio-fowner-pgid-path.patch b/queue-7.1/selftests-landlock-test-scope_signal-on-the-sigio-fowner-pgid-path.patch
new file mode 100644 (file)
index 0000000..4e48a40
--- /dev/null
@@ -0,0 +1,231 @@
+From 76579d09beedaffe7fe76e9c05644f73983e1ceb Mon Sep 17 00:00:00 2001
+From: Bryam Vargas <hexlabsecurity@proton.me>
+Date: Thu, 4 Jun 2026 23:17:05 +0000
+Subject: selftests/landlock: Test SCOPE_SIGNAL on the SIGIO/fowner pgid path
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Bryam Vargas <hexlabsecurity@proton.me>
+
+commit 76579d09beedaffe7fe76e9c05644f73983e1ceb upstream.
+
+Add regression tests for the LANDLOCK_SCOPE_SIGNAL handling of the
+asynchronous SIGIO delivery path (fcntl(F_SETOWN)) with a process-group
+owner.
+
+sigio_to_pgid_members covers the bypass: a sandboxed process at the head
+of its process group's PGID hlist (the default after fork()) arms
+F_SETOWN(-pgrp) + O_ASYNC and triggers the fan-out; the in-domain owner
+must be signaled (proving the trigger fired) while the non-sandboxed
+member of the group, outside the domain, must not.
+
+sigio_to_pgid_self covers the same-process guarantee: the owner is
+registered from a sandboxed non-leader thread, whose domain differs from
+the thread-group leader the kernel signals for a process-group owner.
+That leader belongs to the owner's own process and must still be
+signaled.
+
+Without the fix the first test sees the out-of-domain member signaled
+and the second sees the owner's own leader denied.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
+Reviewed-by: Günther Noack <gnoack3000@gmail.com>
+Link: https://patch.msgid.link/43370e89f7a896a583bf33d1cd171d02630e61bf.1780614610.git.hexlabsecurity@proton.me
+[mic: Fix comment]
+Signed-off-by: Mickaël Salaün <mic@digikod.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ tools/testing/selftests/landlock/scoped_signal_test.c |  182 ++++++++++++++++++
+ 1 file changed, 182 insertions(+)
+
+--- a/tools/testing/selftests/landlock/scoped_signal_test.c
++++ b/tools/testing/selftests/landlock/scoped_signal_test.c
+@@ -559,4 +559,186 @@ TEST_F(fown, sigurg_socket)
+               _metadata->exit_code = KSFT_FAIL;
+ }
++/*
++ * Checks that LANDLOCK_SCOPE_SIGNAL is enforced on the asynchronous SIGIO
++ * delivery path (fcntl(F_SETOWN)) when the file owner is a process group.
++ *
++ * A sandboxed process sitting at the head of its process group's PID hlist (the
++ * default position right after fork()) used to escape the fcntl(F_SETOWN,
++ * -pgrp) domain recording: pid_task(pgrp, PIDTYPE_PGID) resolved to the process
++ * itself, so the same-thread-group exemption skipped recording its Landlock
++ * domain.  At SIGIO time that domain was then unset and the signal fanned out
++ * to every group member, including non-sandboxed processes outside the domain.
++ */
++TEST(sigio_to_pgid_members)
++{
++      int trigger[2], sync_child[2];
++      char buf;
++      pid_t child;
++      int status, i;
++
++      drop_caps(_metadata);
++
++      /*
++       * Isolates the test in its own process group so the SIGIO fan-out stays
++       * bounded to this parent and the child forked below.
++       */
++      ASSERT_EQ(0, setpgid(0, 0));
++
++      /* The non-sandboxed parent is the protected (out-of-domain) target. */
++      ASSERT_EQ(0, setup_signal_handler(SIGURG));
++      signal_received = 0;
++
++      ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC));
++      ASSERT_EQ(0, pipe2(sync_child, O_CLOEXEC));
++
++      child = fork();
++      ASSERT_LE(0, child);
++      if (child == 0) {
++              /*
++               * The child inherits the parent's new process group and, just
++               * attached with hlist_add_head_rcu(), is now the head of the
++               * pgid hlist: this is the case that used to skip the recording.
++               */
++              EXPECT_EQ(0, close(sync_child[0]));
++
++              /* In-domain positive control: the child must be signaled. */
++              ASSERT_EQ(0, setup_signal_handler(SIGURG));
++              signal_received = 0;
++
++              create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
++
++              /* Owns the SIGIO source for the whole process group. */
++              ASSERT_EQ(0, fcntl(trigger[0], F_SETSIG, SIGURG));
++              ASSERT_EQ(0, fcntl(trigger[0], F_SETOWN, -getpgrp()));
++              ASSERT_EQ(0, fcntl(trigger[0], F_SETFL, O_ASYNC));
++
++              /* Fans SIGURG out to every member of the process group. */
++              ASSERT_EQ(1, write(trigger[1], ".", 1));
++
++              /*
++               * The sandboxed child is in its own domain and must always be
++               * signaled: this proves the SIGIO actually fired.
++               */
++              for (i = 0; i < 1000 && !signal_received; i++)
++                      usleep(1000);
++              EXPECT_EQ(1, signal_received);
++
++              ASSERT_EQ(1, write(sync_child[1], ".", 1));
++              EXPECT_EQ(0, close(sync_child[1]));
++
++              _exit(_metadata->exit_code);
++              return;
++      }
++      EXPECT_EQ(0, close(sync_child[1]));
++      EXPECT_EQ(0, close(trigger[0]));
++      EXPECT_EQ(0, close(trigger[1]));
++
++      /* Waits for the child to generate the SIGIO. */
++      ASSERT_EQ(1, read(sync_child[0], &buf, 1));
++      EXPECT_EQ(0, close(sync_child[0]));
++
++      /* Lets a delivered-but-pending signal run our handler, if any. */
++      for (i = 0; i < 100 && !signal_received; i++)
++              usleep(1000);
++
++      /*
++       * SCOPE_SIGNAL must block the fan-out to this non-sandboxed parent,
++       * which is outside the child's Landlock domain.  Before the fix the
++       * parent was signaled here.
++       */
++      EXPECT_EQ(0, signal_received);
++
++      ASSERT_EQ(child, waitpid(child, &status, 0));
++      if (WIFSIGNALED(status) || !WIFEXITED(status) ||
++          WEXITSTATUS(status) != EXIT_SUCCESS)
++              _metadata->exit_code = KSFT_FAIL;
++}
++
++static void *thread_setown_scoped(void *arg)
++{
++      const int fd = *(int *)arg;
++      int ruleset_fd;
++      const struct landlock_ruleset_attr ruleset_attr = {
++              .scoped = LANDLOCK_SCOPE_SIGNAL,
++      };
++
++      /* Sandboxes only this non-leader thread (no thread syncing). */
++      ruleset_fd =
++              landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
++      if (ruleset_fd < 0)
++              return (void *)THREAD_ERROR;
++      if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) ||
++          landlock_restrict_self(ruleset_fd, 0)) {
++              close(ruleset_fd);
++              return (void *)THREAD_ERROR;
++      }
++      close(ruleset_fd);
++
++      /* Makes this process group own the SIGIO source. */
++      if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) ||
++          fcntl(fd, F_SETFL, O_ASYNC))
++              return (void *)THREAD_ERROR;
++
++      return (void *)THREAD_SUCCESS;
++}
++
++/*
++ * Checks that the SIGIO fan-out is still delivered to the file owner's own
++ * process when fcntl(F_SETOWN, -pgrp) was issued from a sandboxed non-leader
++ * thread.
++ *
++ * The Landlock domain is recorded for a process-group owner (so out-of-domain
++ * members stay blocked, see sigio_to_pgid_members), but the kernel signals a
++ * process group through its members' thread-group leaders.  Here the leader is
++ * not sandboxed and thus has a different domain than the registering thread, so
++ * the registration-time check cannot tell that it belongs to the owner's own
++ * process.  hook_file_send_sigiotask() must recognize it through the recorded
++ * thread group and allow the delivery, matching the same-process guarantee of
++ * commit 18eb75f3af40.  Without that exemption the leader is wrongly denied and
++ * never signaled.
++ */
++TEST(sigio_to_pgid_self)
++{
++      int trigger[2];
++      pthread_t thread;
++      enum thread_return ret = THREAD_INVALID;
++      int i;
++
++      drop_caps(_metadata);
++
++      /* Bounds the SIGIO fan-out to this process. */
++      ASSERT_EQ(0, setpgid(0, 0));
++
++      /* The non-sandboxed thread-group leader is the SIGIO target. */
++      ASSERT_EQ(0, setup_signal_handler(SIGURG));
++      signal_received = 0;
++
++      ASSERT_EQ(0, pipe2(trigger, O_CLOEXEC));
++
++      /*
++       * Registers the process-group fowner from a sibling thread that
++       * sandboxes only itself, so its domain differs from the leader's.
++       */
++      ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped,
++                                  &trigger[0]));
++      ASSERT_EQ(0, pthread_join(thread, (void **)&ret));
++      ASSERT_EQ(THREAD_SUCCESS, ret);
++
++      /* Fans SIGURG out to the process group. */
++      ASSERT_EQ(1, write(trigger[1], ".", 1));
++
++      for (i = 0; i < 1000 && !signal_received; i++)
++              usleep(1000);
++
++      /*
++       * Same-process delivery must always be allowed, even though the owner
++       * was registered from a sandboxed sibling thread.
++       */
++      EXPECT_EQ(1, signal_received);
++
++      EXPECT_EQ(0, close(trigger[0]));
++      EXPECT_EQ(0, close(trigger[1]));
++}
++
+ TEST_HARNESS_MAIN
index 81e93372c8df054564f44239d1d0f0bfd72901ae..3f63ae530dd2d51e5393b80a27a2d4778b008dae 100644 (file)
@@ -1676,3 +1676,92 @@ sunrpc-harden-rq_procinfo-lifecycle-to-prevent-double-free.patch
 lockd-plug-nlm_file-leak-when-nlm_do_fopen-fails.patch
 lockd-plug-nlm_file-refcount-leak-on-cached-nlm_do_fopen-failure.patch
 sunrpc-bound-check-xdr_buf_to_bvec-stores-before-writing.patch
+remoteproc-qcom-fix-leak-when-custom-dump_segments-addition-fails.patch
+remoteproc-xlnx-check-remote-core-state.patch
+mm-sparse-vmemmap-fix-vmemmap-accounting-underflow.patch
+mips-ip22-gio-fix-gio-device-memory-leak.patch
+mips-ip22-gio-fix-kfree-of-static-object.patch
+mips-ip22-gio-fix-device-reference-leak-in-probe.patch
+mips-dec-ensure-32-bit-stack-location-for-o32-prom_printf.patch
+mm-mm_init-fix-pageblock-migratetype-for-zone_device-compound-pages.patch
+power-supply-cpcap-battery-fix-missing-nvmem_device_put-causing-reference-leak.patch
+power-supply-max17042-fix-of-node-reference-imbalance.patch
+mm-hugetlb-fix-hugetlb-cgroup-rsvd-charge-uncharge-mismatch.patch
+mm-memory_hotplug-fix-incorrect-altmap-passing-in-error-path.patch
+mm-damon-core-make-charge_addr_from-aware-of-end-address-exclusivity.patch
+ntfs-grow-index-root-value-before-reparent-header-update.patch
+ntfs-fix-incorrect-size-of-symbolic-link.patch
+ntfs-fix-off-by-one-in-mapping-pairs-decoding-bounds-checks.patch
+ntfs-add-wq_percpu-to-alloc_workqueue-users.patch
+ntfs-validate-attribute-values-on-lookup.patch
+ntfs-add-bounds-check-before-accessing-ea-entries.patch
+ntfs-not-change-0-byte-data-attribute-to-non-resident.patch
+ntfs-validate-index-block-header-more-strictly.patch
+ntfs-free-volume-wide-resources-on-fill_super-failure.patch
+ntfs-update-index-root-allocated-size-before-shrink.patch
+ntfs-centalize-index_root-header-validation.patch
+ntfs-skip-extent-mft-records-in-writeback-to-prevent-deadlock.patch
+ntfs-reinit-search-context-before-volume-information-lookup.patch
+ntfs-only-alias-volume-upcase-to-default-on-exact-match.patch
+ntfs-avoid-heap-allocation-for-free-cluster-readahead-state.patch
+ntfs-validate-index-entries-on-reading.patch
+ntfs-detect-mapping-pairs-lcn-accumulator-overflow.patch
+ntfs-do-not-replace-volume-name-after-lookup-errors.patch
+ntfs-validate-resident-volume-name-values-on-lookup.patch
+ntfs-validate-resident-index-root-values-on-lookup.patch
+ntfs-reject-non-resident-records-for-resident-only-attributes.patch
+fs-ntfs3-fix-syncing-wrong-inode-on-dirsync-cross-directory-rename.patch
+fs-ntfs3-bound-deleteindexentryallocation-memmove-length.patch
+fs-ntfs3-bound-copy_lcns-dp-page_lcns-index-in-analysis-pass.patch
+fs-ntfs3-bound-attr_off-in-updateresidentvalue-against-data_off.patch
+fs-ntfs3-validate-lcns_follow-in-log_replay-conversion.patch
+fs-ntfs3-add-depth-limit-to-indx_find_buffer-to-prevent-stack-overflow.patch
+fs-ntfs3-bound-ntfs_de-view.data_off-in-updaterecorddata-root-allocation.patch
+ntfs3-cap-restart_table-free-chain-walker-at-rt-used.patch
+ntfs3-bound-to_move-in-indx_insert_into_root-before-hdr_insert_head.patch
+ntfs3-validate-split-point-offset-in-indx_insert_into_buffer.patch
+ntfs3-fix-out-of-bounds-read-in-decompress_lznt.patch
+ntfs-fix-mrec_lock-abba-deadlock-in-rename.patch
+ntfs-fail-attrlist-updates-when-the-superblock-is-inactive.patch
+ntfs-sanitize-mft-references-returned-from-ntfs_lookup_inode_by_name.patch
+ntfs-avoid-self-deadlock-during-inode-eviction.patch
+ntfs-make-system-files-immutable-to-prevent-corruption.patch
+ntfs-fix-warn_on-for-resident-attribute-in-ntfs_map_runlist_nolock.patch
+ntfs-fix-hole-runlist-memory-leak-in-insert-range-error-path.patch
+landlock-fix-landlock_scope_signal-bypass-on-the-sigio-path.patch
+power-supply-charger-manager-fix-refcount-leak-in-is_full_charged.patch
+selftests-landlock-test-scope_signal-on-the-sigio-fowner-pgid-path.patch
+mips-sched-fix-cpumask_offstack-memory-corruption.patch
+mm-huge_memory-preserve-pmd_swp_uffd_wp-on-device-private-pmd-downgrade.patch
+riscv-cacheinfo-fix-node-reference-leak-in-populate_cache_leaves.patch
+mm-damon-core-always-put-unsuccessfully-committed-target-pids.patch
+mm-damon-sysfs-schemes-fix-dir-put-orders-in-access_pattern_add_dirs.patch
+mm-damon-sysfs-schemes-put-stats-for-scheme_add_dirs-internal-error.patch
+fs-proc-task_mmu-fix-make_uffd_wp_huge_pte-prot-update-race.patch
+fs-proc-task_mmu-fix-hugetlb-self-deadlock-in-pagemap_scan_pte_hole.patch
+fs-proc-task_mmu-use-huge_page_size-in-pagemap_scan_hugetlb_entry.patch
+fs-proc-task_mmu-do-not-warn-on-seeing-non-migration-pmd-entry.patch
+proc-only-bump-parent-nlink-when-registering-directories.patch
+fs-proc-fix-kpf_ksm-reported-for-all-anonymous-pages.patch
+powerpc-dt_cpu_ftrs-set-cpu_ftr_p11_pvr-for-power11-and-later-processors.patch
+mm-mm_init-fix-uninitialized-struct-pages-for-zone_device.patch
+kcov-use-write_once-for-selftest-mode-stores.patch
+mtd-slram-remove-failed-entries-from-the-device-list.patch
+9p-skip-nlink-update-in-cacheless-mode-to-fix-warn_on.patch
+power-supply-bq257xx-fix-vsysmin-clamping-logic.patch
+scsi-smartpqi-use-shost_to_hba-in-pqi_scan_finished.patch
+kho-make-sure-scratch-size-is-always-aligned-by-cma_min_alignment_bytes.patch
+scsi-sas-skip-opt_sectors-when-dma-reports-no-real-optimization-hint.patch
+openrisc-add-full-instruction-cache-invalidate-functions.patch
+mtd-maps-vmu-flash-fix-fault-in-unaligned-fixup.patch
+ocfs2-use-kzalloc-for-quota-recovery-bitmap-allocation.patch
+mtd-rawnand-pl353-fix-probe-resource-allocation.patch
+net-9p-fix-infinite-loop-in-p9_client_rpc-on-fatal-signal.patch
+mtd-rawnand-fix-condition-in-nand_select_target.patch
+ocfs2-avoid-moving-extents-to-occupied-clusters.patch
+ocfs2-fix-null-h_transaction-deref-in-ocfs2_assure_trans_credits.patch
+ocfs2-fix-ubsan-array-index-out-of-bounds-in-ocfs2_sum_rightmost_rec.patch
+ocfs2-add-journal-null-check-in-ocfs2_checkpoint_inode.patch
+ocfs2-reject-dinodes-with-non-canonical-i_mode-type.patch
+ocfs2-reject-dinodes-whose-i_rdev-disagrees-with-the-file-type.patch
+ocfs2-reject-non-inline-dinodes-with-i_size-and-zero-i_clusters.patch