* FAILED: patch "[PATCH] mm/hugetlb: unmap the page where the other VMA maps it" failed to apply to 6.12-stable tree
@ 2026-11-07 10:26 Sasha Levin
2026-11-11 4:10 ` [PATCH 6.12.y] mm/hugetlb: unmap the page where the other VMA maps it Sasha Levin
0 siblings, 1 reply; 2+ messages in thread
From: Sasha Levin @ 2026-11-07 10:26 UTC (permalink / raw)
To: josef, david, akpm, mason, ljs, muchun.song, lance.yang, liam,
vbabka, jannh, pfalcato, osalvador, linmiaohe, nao.horiguchi, agl,
mgorman, riel, peterx, kas, rppt
Cc: stable
The patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x ae99c503eb362b42cd5d24d80bc6cba669acc4c2
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '20261107052603-failed-6.12-ae99c503eb36@kernel.org' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Thanks,
Sasha
------------------ original commit in Linus's tree ------------------
From ae99c503eb362b42cd5d24d80bc6cba669acc4c2 Mon Sep 17 00:00:00 2001
From: Josef Bacik <josef@toxicpanda.com>
Date: Thu, 15 Oct 2026 18:07:10 +0200
Subject: [PATCH] mm/hugetlb: unmap the page where the other VMA maps it
If the owner of a MAP_PRIVATE hugetlb mapping fails to allocate a page
for CoW, unmap_ref_private() unmaps the old page from all other private
VMAs that have no reservation of their own. It finds them by the offset
in the file, but then zaps each of them at the address of the faulting
VMA. Nothing says that another VMA maps the offset at that address: a
child can have moved its copy of the VMA with mremap(), and a second
mapping of the file is wherever mmap() has put it.
So __unmap_hugepage_range() walks the page tables of the other mm at an
address outside of iter_vma, where i_mmap_rwsem of this file protects
nothing and where anything can be mapped, and the page stays mapped where
it really is.
All of the following was seen on v7.3-rc6+ (g6c377d19d4a5) in KVM guests,
as a user without any privilege. It takes a pool of 2 MiB pages with no
free page that is not reserved, so that the allocation fails: the user
can take or reserve what is free itself.
- The child moves its VMA away. The page is never unmapped, the
mapcount stays 2, and the owner goes round retry_avoidcopy in
hugetlb_wp() until the child is gone or a page becomes free. SIGKILL
to the owner does not end it. 5 of 5 runs.
- The owner's mm has a second private mapping of the offset, made with
MAP_NORESERVE. The owner zaps its own entry and reads zeroes where
its data was. 5 of 5 runs.
- The child moves its VMA away and maps a 1 GiB hugetlb page at the old
address, so this one takes a pool with a 1 GiB page as well.
huge_pte_offset() for the 2 MiB size steps through the PUD
leaf, so the "PMD table" is the 1 GiB page itself and its "page table
lock" is folio->private of that page, which holds 2. Kernels with
lock debugging oops in __unmap_hugepage_range(), 7 of 7 runs. Others
spin on that word for ever with preemption disabled, 5 of 5 runs:
watchdog: BUG: soft lockup - CPU#0 stuck for 26s! [u_det3:130]
RIP: 0010:queued_spin_lock_slowpath+0x8a/0x290
RAX: 0000000000000002 RBX: ffffea0001000028
Call Trace:
__unmap_hugepage_range+0x154/0x6d0
unmap_hugepage_range+0xc1/0x140
hugetlb_wp+0x55c/0x700
hugetlb_fault+0x5f3/0x8f0
RCU stalls follow, and killing all tasks of the user changes nothing.
Four such owners, whose children share a single 1 GiB page, took down
the whole guest with its four CPUs, 2 of 2 runs: one reported soft
lockups on all four, the other gave no sign of life any more.
i_mmap_rwsem stays write locked. With a MAP_PRIVATE mapping of a
hugetlbfs file that belongs to another user, and that the attacker
can only read, the next mmap() of that file by its owner sleeps
uninterruptibly in __mmap_region(), 4 of 4 runs.
Compute the address from the offset for each VMA. The tree walk returns
only VMAs that cover pgoff, and vm_start, vm_end and vm_pgoff of a hugetlb
VMA are aligned to the huge page size, as pgoff is, so the whole range
lies inside iter_vma. Its page tables can not go away there, since the
VMA is unlinked from the file under i_mmap_rwsem first.
With the right address the page is unmapped from all VMAs but the
owner's, so the retry in hugetlb_wp() finds a mapcount of 1 and reuses the
page.
A child that has moved its copy of the VMA now loses the page, and gets
SIGBUS when it faults on a missing page of that VMA. That is what the
comment above the function has always promised to those who do not own
the reservation, and what already happens to a child that has left the
VMA in place.
The address has been wrong from the start, and the second case needs
only mmap() and fork(). mremap() of hugetlb VMAs, which the other cases
use, came with commit 550a7d60bd5e ("mm, hugepages: add mremap() support
for hugepage backed vma") in v5.16.
Original fix generated by an LLM and sent in private by Josef.
[david@kernel.org: switch to vma_filebacked_address, add VM_WARN_ON_ONCE, refine comments]
Link: https://lore.kernel.org/20261015-urgent_fixes-v1-4-42796911043b@kernel.org
Fixes: 04f2cbe35699 ("hugetlb: guarantee that COW faults for a process that called mmap(MAP_PRIVATE) on hugetlbfs will succeed")
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reported-by: Chris Mason <mason@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Acked-by: Muchun Song <muchun.song@linux.dev>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Miaohe Lin <linmiaohe@huawei.com>
Cc: Naoya Horiguchi <nao.horiguchi@gmail.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Rik van Riel <riel@surriel.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Kiryl Shutsemau <kas@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: <stable@vger.kernel.org>
---
mm/hugetlb.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 5db64fa984041..4fca859bbd7ea 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -5482,6 +5482,8 @@ static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
struct address_space *mapping;
pgoff_t pgoff;
+ VM_WARN_ON_ONCE(!folio_test_anon(folio));
+
/*
* vm_pgoff is in PAGE_SIZE units, hence the different calculation
* from page cache lookup which is in HPAGE_SIZE units.
@@ -5516,10 +5518,17 @@ static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
* could insert a zeroed page instead of the data existing
* from the time of fork. This would look like data corruption
*/
- if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER))
- unmap_hugepage_range(iter_vma, address,
- address + huge_page_size(h),
- folio, 0);
+ if (is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER))
+ continue;
+
+ /*
+ * The address in other VMAs can differ. Even though our old
+ * folio is anon, we always operate on the underlying hugetlb
+ * file.
+ */
+ address = vma_filebacked_address(iter_vma, pgoff, 1);
+ unmap_hugepage_range(iter_vma, address,
+ address + huge_page_size(h), folio, 0);
}
i_mmap_unlock_write(mapping);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* [PATCH 6.12.y] mm/hugetlb: unmap the page where the other VMA maps it
2026-11-07 10:26 FAILED: patch "[PATCH] mm/hugetlb: unmap the page where the other VMA maps it" failed to apply to 6.12-stable tree Sasha Levin
@ 2026-11-11 4:10 ` Sasha Levin
0 siblings, 0 replies; 2+ messages in thread
From: Sasha Levin @ 2026-11-11 4:10 UTC (permalink / raw)
To: stable
Cc: Josef Bacik, David Hildenbrand (Arm), Andrew Morton, Chris Mason,
Lorenzo Stoakes (ARM), Muchun Song, Lance Yang, Liam R. Howlett,
Vlastimil Babka, Jann Horn, Pedro Falcato, Oscar Salvador,
Miaohe Lin, Naoya Horiguchi, Adam Litke, Mel Gorman, Rik van Riel,
Peter Xu, Kiryl Shutsemau, Mike Rapoport, Sasha Levin
From: Josef Bacik <josef@toxicpanda.com>
[ Upstream commit ae99c503eb362b42cd5d24d80bc6cba669acc4c2 ]
If the owner of a MAP_PRIVATE hugetlb mapping fails to allocate a page
for CoW, unmap_ref_private() unmaps the old page from all other private
VMAs that have no reservation of their own. It finds them by the offset
in the file, but then zaps each of them at the address of the faulting
VMA. Nothing says that another VMA maps the offset at that address: a
child can have moved its copy of the VMA with mremap(), and a second
mapping of the file is wherever mmap() has put it.
So __unmap_hugepage_range() walks the page tables of the other mm at an
address outside of iter_vma, where i_mmap_rwsem of this file protects
nothing and where anything can be mapped, and the page stays mapped where
it really is.
All of the following was seen on v7.3-rc6+ (g6c377d19d4a5) in KVM guests,
as a user without any privilege. It takes a pool of 2 MiB pages with no
free page that is not reserved, so that the allocation fails: the user
can take or reserve what is free itself.
- The child moves its VMA away. The page is never unmapped, the
mapcount stays 2, and the owner goes round retry_avoidcopy in
hugetlb_wp() until the child is gone or a page becomes free. SIGKILL
to the owner does not end it. 5 of 5 runs.
- The owner's mm has a second private mapping of the offset, made with
MAP_NORESERVE. The owner zaps its own entry and reads zeroes where
its data was. 5 of 5 runs.
- The child moves its VMA away and maps a 1 GiB hugetlb page at the old
address, so this one takes a pool with a 1 GiB page as well.
huge_pte_offset() for the 2 MiB size steps through the PUD
leaf, so the "PMD table" is the 1 GiB page itself and its "page table
lock" is folio->private of that page, which holds 2. Kernels with
lock debugging oops in __unmap_hugepage_range(), 7 of 7 runs. Others
spin on that word for ever with preemption disabled, 5 of 5 runs:
watchdog: BUG: soft lockup - CPU#0 stuck for 26s! [u_det3:130]
RIP: 0010:queued_spin_lock_slowpath+0x8a/0x290
RAX: 0000000000000002 RBX: ffffea0001000028
Call Trace:
__unmap_hugepage_range+0x154/0x6d0
unmap_hugepage_range+0xc1/0x140
hugetlb_wp+0x55c/0x700
hugetlb_fault+0x5f3/0x8f0
RCU stalls follow, and killing all tasks of the user changes nothing.
Four such owners, whose children share a single 1 GiB page, took down
the whole guest with its four CPUs, 2 of 2 runs: one reported soft
lockups on all four, the other gave no sign of life any more.
i_mmap_rwsem stays write locked. With a MAP_PRIVATE mapping of a
hugetlbfs file that belongs to another user, and that the attacker
can only read, the next mmap() of that file by its owner sleeps
uninterruptibly in __mmap_region(), 4 of 4 runs.
Compute the address from the offset for each VMA. The tree walk returns
only VMAs that cover pgoff, and vm_start, vm_end and vm_pgoff of a hugetlb
VMA are aligned to the huge page size, as pgoff is, so the whole range
lies inside iter_vma. Its page tables can not go away there, since the
VMA is unlinked from the file under i_mmap_rwsem first.
With the right address the page is unmapped from all VMAs but the
owner's, so the retry in hugetlb_wp() finds a mapcount of 1 and reuses the
page.
A child that has moved its copy of the VMA now loses the page, and gets
SIGBUS when it faults on a missing page of that VMA. That is what the
comment above the function has always promised to those who do not own
the reservation, and what already happens to a child that has left the
VMA in place.
The address has been wrong from the start, and the second case needs
only mmap() and fork(). mremap() of hugetlb VMAs, which the other cases
use, came with commit 550a7d60bd5e ("mm, hugepages: add mremap() support
for hugepage backed vma") in v5.16.
Original fix generated by an LLM and sent in private by Josef.
[david@kernel.org: switch to vma_filebacked_address, add VM_WARN_ON_ONCE, refine comments]
Link: https://lore.kernel.org/20261015-urgent_fixes-v1-4-42796911043b@kernel.org
Fixes: 04f2cbe35699 ("hugetlb: guarantee that COW faults for a process that called mmap(MAP_PRIVATE) on hugetlbfs will succeed")
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reported-by: Chris Mason <mason@kernel.org>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Acked-by: Muchun Song <muchun.song@linux.dev>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Miaohe Lin <linmiaohe@huawei.com>
Cc: Naoya Horiguchi <nao.horiguchi@gmail.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Rik van Riel <riel@surriel.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Kiryl Shutsemau <kas@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: <stable@vger.kernel.org>
[ sashal: adapted folio handling to the existing page interface and replaced vma_filebacked_address() with vma_address() ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
mm/hugetlb.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index d9443bdebc068..1ef7202b2c901 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -5775,6 +5775,8 @@ static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
struct address_space *mapping;
pgoff_t pgoff;
+ VM_WARN_ON_ONCE(!folio_test_anon(page_folio(page)));
+
/*
* vm_pgoff is in PAGE_SIZE units, hence the different calculation
* from page cache lookup which is in HPAGE_SIZE units.
@@ -5810,9 +5812,17 @@ static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
* could insert a zeroed page instead of the data existing
* from the time of fork. This would look like data corruption
*/
- if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER))
- unmap_hugepage_range(iter_vma, address,
- address + huge_page_size(h), page, 0);
+ if (is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER))
+ continue;
+
+ /*
+ * The address in other VMAs can differ. Even though our old
+ * folio is anon, we always operate on the underlying hugetlb
+ * file.
+ */
+ address = vma_address(iter_vma, pgoff, 1);
+ unmap_hugepage_range(iter_vma, address,
+ address + huge_page_size(h), page, 0);
}
i_mmap_unlock_write(mapping);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-11 4:10 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-07 10:26 FAILED: patch "[PATCH] mm/hugetlb: unmap the page where the other VMA maps it" failed to apply to 6.12-stable tree Sasha Levin
2026-11-11 4:10 ` [PATCH 6.12.y] mm/hugetlb: unmap the page where the other VMA maps it Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox