All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] sh: mm: fix hugetlb on SH-4
@ 2026-09-26 18:39 Karl Mehltretter
  2026-09-26 18:39 ` [PATCH 1/2] sh: mm: replace the page size bits in pte_mkhuge() Karl Mehltretter
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Karl Mehltretter @ 2026-09-26 18:39 UTC (permalink / raw)
  To: Yoshinori Sato, Rich Felker, John Paul Adrian Glaubitz
  Cc: linux-sh, Muchun Song, Oscar Salvador, David Hildenbrand,
	Andrew Morton, linux-mm, linux-kernel, Karl Mehltretter

Muchun Song noted in the review of the Arm hugetlb alignment fix [1]
that SuperH has the same arch_get_unmapped_area() gap. Reproducing it
in QEMU turned up a second, older bug that corrupts memory once the
mappings are aligned.

Patch 1 fixes the page size in hugetlb PTEs. pte_mkhuge() ORs the huge
size into the base page size bits, so 64 KiB huge pages over 4 KiB
pages are loaded into the TLB as 1 MiB pages. User writes then land in
unrelated kernel memory. This dates back to the start of git history.

Patch 2 aligns hugetlb mappings to the huge page size. Without it
mmap(MAP_HUGETLB) can return an address that is not aligned to the huge
page size, and process exit hits the BUG_ON() in
__unmap_hugepage_range(). This is a 6.13
regression.

Patch 1 goes first because patch 2 alone makes the corruption reachable
through a plain mmap(). Both are marked for stable.

Tested on QEMU r2d (SH7751R, rts7751r2dplus_defconfig plus HUGETLBFS
with 64 KiB huge pages, gcc 15.2) on top of fddfc3ec3179. A test init
maps two huge pages with the default and legacy layouts and with
MAP_FIXED, writes and reads back every 4 KiB, mprotect()s the mapping
read-only and back, and checks a COW write in a forked child:

  kernel          MAP_FIXED       default/legacy mmap
  base            memory corrupt  misaligned, BUG at mm/hugetlb.c:5215
  patch 1         pass            misaligned, BUG
  patches 1+2     pass            pass

SH-X2 (X2TLB) is only build tested (r7785rp_defconfig with 64 KiB huge
pages).

QEMU's SH-4 TLB emulation flushes only the first 4 KiB of a 64 KiB or
1 MiB entry when the entry is dropped [2]. Keep that in mind when
testing hugetlb on QEMU.

[1] https://lore.kernel.org/r/5AB6FE85-4CB9-41EF-A700-BC5E071F29F5@linux.dev
[2] https://gitlab.com/qemu-project/qemu/-/issues/4598

Karl Mehltretter (2):
  sh: mm: replace the page size bits in pte_mkhuge()
  sh: mm: align hugetlb mappings to the huge page size

 arch/sh/include/asm/pgtable_32.h |  7 +++++--
 arch/sh/mm/mmap.c                | 11 +++++++++--
 2 files changed, 14 insertions(+), 4 deletions(-)


base-commit: fddfc3ec31799a932bb92f1b8a84cb3d1f963be9
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] sh: mm: replace the page size bits in pte_mkhuge()
  2026-09-26 18:39 [PATCH 0/2] sh: mm: fix hugetlb on SH-4 Karl Mehltretter
@ 2026-09-26 18:39 ` Karl Mehltretter
  2026-09-26 18:39 ` [PATCH 2/2] sh: mm: align hugetlb mappings to the huge page size Karl Mehltretter
  2026-09-26 18:43 ` [PATCH 0/2] sh: mm: fix hugetlb on SH-4 John Paul Adrian Glaubitz
  2 siblings, 0 replies; 6+ messages in thread
From: Karl Mehltretter @ 2026-09-26 18:39 UTC (permalink / raw)
  To: Yoshinori Sato, Rich Felker, John Paul Adrian Glaubitz
  Cc: linux-sh, Muchun Song, Oscar Salvador, David Hildenbrand,
	Andrew Morton, linux-mm, linux-kernel, Karl Mehltretter

pte_mkhuge() ORs _PAGE_SZHUGE into a PTE that already carries the base
page size from _PAGE_FLAGS_HARD, which every PAGE_* protection
includes. The PTEL size field is an encoding, not a set of flags: on
SH-4 with 4 KiB pages and 64 KiB huge pages, _PAGE_SZ0 | _PAGE_SZ1
selects a 1 MiB page. Every hugetlb mapping is therefore loaded into
the UTLB as a 1 MiB page whose physical base is the huge page's address
rounded down to 1 MiB, so user accesses land in unrelated kernel
memory.

In a QEMU r2d guest, after touching a MAP_HUGETLB mapping at
0x30000000, QEMU's "info tlb" shows

  vpn=c0000 ppn=32e00 sz=3 size=1048576

and writing the two huge pages overwrites kernel data, for example
page tables ("bad pgd 5a5a5a5a" on munmap) or a struct file:

  Fault in unaligned fixup: 0000 [#1]
  PC is at file_tty_write.isra.0+0x24/0x220
  R1  : 5a5a5a5a

The SH-X2 extended size field has the same problem: a 4 KiB base page
(ESZ0) combined with 64 KiB huge pages (ESZ2) encodes 256 KiB. Only the
configurations whose huge size encoding happens to contain the base
encoding, such as 1 MiB over 4 KiB on SH-4, have worked.

Clear the size field before setting the huge page size. All hugetlb
PTE constructors, including huge_pte_modify() on mprotect() and the
fork/COW paths, go through arch_make_huge_pte() and so pte_mkhuge().
With this change the same entry is loaded as

  vpn=c0000 ppn=32e00 sz=2 size=65536

and the mappings read back correctly after mprotect() and fork()/COW.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
 arch/sh/include/asm/pgtable_32.h | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/arch/sh/include/asm/pgtable_32.h b/arch/sh/include/asm/pgtable_32.h
index 5f51af18997b..1e6821a23eac 100644
--- a/arch/sh/include/asm/pgtable_32.h
+++ b/arch/sh/include/asm/pgtable_32.h
@@ -145,6 +145,8 @@ static inline unsigned long copy_ptea_attributes(unsigned long x)
 # elif defined(CONFIG_HUGETLB_PAGE_SIZE_64MB)
 #  define _PAGE_SZHUGE	(_PAGE_EXT_ESZ2 | _PAGE_EXT_ESZ3)
 # endif
+# define _PAGE_SZHUGE_MASK	(_PAGE_EXT_ESZ0 | _PAGE_EXT_ESZ1 | \
+				 _PAGE_EXT_ESZ2 | _PAGE_EXT_ESZ3)
 # define _PAGE_WIRED	(_PAGE_EXT(_PAGE_EXT_WIRED))
 #else
 # if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
@@ -152,6 +154,7 @@ static inline unsigned long copy_ptea_attributes(unsigned long x)
 # elif defined(CONFIG_HUGETLB_PAGE_SIZE_1MB)
 #  define _PAGE_SZHUGE	(_PAGE_SZ0 | _PAGE_SZ1)
 # endif
+# define _PAGE_SZHUGE_MASK	(_PAGE_SZ_MASK)
 # define _PAGE_WIRED	(0)
 #endif
 
@@ -359,11 +362,11 @@ static inline pte_t pte_##fn(pte_t pte) { pte.pte_##h op; return pte; }
  */
 PTE_BIT_FUNC(high, wrprotect, &= ~(_PAGE_EXT_USER_WRITE | _PAGE_EXT_KERN_WRITE));
 PTE_BIT_FUNC(high, mkwrite_novma, |= _PAGE_EXT_USER_WRITE | _PAGE_EXT_KERN_WRITE);
-PTE_BIT_FUNC(high, mkhuge, |= _PAGE_SZHUGE);
+PTE_BIT_FUNC(high, mkhuge, = (pte.pte_high & ~_PAGE_SZHUGE_MASK) | _PAGE_SZHUGE);
 #else
 PTE_BIT_FUNC(low, wrprotect, &= ~_PAGE_RW);
 PTE_BIT_FUNC(low, mkwrite_novma, |= _PAGE_RW);
-PTE_BIT_FUNC(low, mkhuge, |= _PAGE_SZHUGE);
+PTE_BIT_FUNC(low, mkhuge, = (pte.pte_low & ~_PAGE_SZHUGE_MASK) | _PAGE_SZHUGE);
 #endif
 
 PTE_BIT_FUNC(low, mkclean, &= ~_PAGE_DIRTY);

base-commit: fddfc3ec31799a932bb92f1b8a84cb3d1f963be9
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] sh: mm: align hugetlb mappings to the huge page size
  2026-09-26 18:39 [PATCH 0/2] sh: mm: fix hugetlb on SH-4 Karl Mehltretter
  2026-09-26 18:39 ` [PATCH 1/2] sh: mm: replace the page size bits in pte_mkhuge() Karl Mehltretter
@ 2026-09-26 18:39 ` Karl Mehltretter
  2026-09-26 18:43 ` [PATCH 0/2] sh: mm: fix hugetlb on SH-4 John Paul Adrian Glaubitz
  2 siblings, 0 replies; 6+ messages in thread
From: Karl Mehltretter @ 2026-09-26 18:39 UTC (permalink / raw)
  To: Yoshinori Sato, Rich Felker, John Paul Adrian Glaubitz
  Cc: linux-sh, Muchun Song, Oscar Salvador, David Hildenbrand,
	Andrew Morton, linux-mm, linux-kernel, Karl Mehltretter

mmap(MAP_HUGETLB) on SH4 can return an address which is not aligned to
the huge page size. In a QEMU r2d guest with 64 KiB huge pages, a
two-page mapping placed after a single 4 KiB mapping lands at
0x29558000, and process exit then hits the alignment check in
__unmap_hugepage_range():

  kernel BUG at mm/hugetlb.c:5215!
  Kernel BUG: 003e [#1]
  PC is at __unmap_hugepage_range+0x3a4/0x424
  PR is at __zap_vma_range+0xa28/0xa80

Commit 7bd3f1e1a9ae ("mm: make hugetlb mappings go through
mm_get_unmapped_area_vmflags") made hugetlb mappings use the
architecture's arch_get_unmapped_area(). The generic implementations
handle hugetlb alignment, but the SH implementations only account for
the cache-colouring constraint in shm_align_mask, so the mapping ends
up aligned to at most the D-cache alias size.

Set the alignment mask from the file's hstate for hugetlb mappings in
both SH implementations, as LoongArch did in commit 3109d5ff484b
("LoongArch: Set hugetlb mmap base address aligned with pmd size").
Huge-page alignment also satisfies the colouring constraint. SH only
uses the bottom-up layout today, but the top-down variant gets the same
change so the two stay in sync.

Fixes: 7bd3f1e1a9ae ("mm: make hugetlb mappings go through mm_get_unmapped_area_vmflags")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
 arch/sh/mm/mmap.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/arch/sh/mm/mmap.c b/arch/sh/mm/mmap.c
index c442734d9b0c..4df807e0df36 100644
--- a/arch/sh/mm/mmap.c
+++ b/arch/sh/mm/mmap.c
@@ -7,6 +7,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
+#include <linux/hugetlb.h>
 #include <linux/io.h>
 #include <linux/mm.h>
 #include <linux/sched/mm.h>
@@ -92,7 +93,10 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
 	info.length = len;
 	info.low_limit = TASK_UNMAPPED_BASE;
 	info.high_limit = TASK_SIZE;
-	info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
+	if (filp && is_file_hugepages(filp))
+		info.align_mask = huge_page_mask_align(filp);
+	else
+		info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
 	info.align_offset = pgoff << PAGE_SHIFT;
 	return vm_unmapped_area(&info);
 }
@@ -142,7 +146,10 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
 	info.length = len;
 	info.low_limit = PAGE_SIZE;
 	info.high_limit = mm->mmap_base;
-	info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
+	if (filp && is_file_hugepages(filp))
+		info.align_mask = huge_page_mask_align(filp);
+	else
+		info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
 	info.align_offset = pgoff << PAGE_SHIFT;
 	addr = vm_unmapped_area(&info);
 
-- 
2.39.5 (Apple Git-154)


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/2] sh: mm: fix hugetlb on SH-4
  2026-09-26 18:39 [PATCH 0/2] sh: mm: fix hugetlb on SH-4 Karl Mehltretter
  2026-09-26 18:39 ` [PATCH 1/2] sh: mm: replace the page size bits in pte_mkhuge() Karl Mehltretter
  2026-09-26 18:39 ` [PATCH 2/2] sh: mm: align hugetlb mappings to the huge page size Karl Mehltretter
@ 2026-09-26 18:43 ` John Paul Adrian Glaubitz
  2026-09-27 18:35   ` Karl Mehltretter
  2 siblings, 1 reply; 6+ messages in thread
From: John Paul Adrian Glaubitz @ 2026-09-26 18:43 UTC (permalink / raw)
  To: Karl Mehltretter, Yoshinori Sato, Rich Felker
  Cc: linux-sh, Muchun Song, Oscar Salvador, David Hildenbrand,
	Andrew Morton, linux-mm, linux-kernel

Hi Karl,

On Sat, 2026-09-26 at 20:39 +0200, Karl Mehltretter wrote:
> Muchun Song noted in the review of the Arm hugetlb alignment fix [1]
> that SuperH has the same arch_get_unmapped_area() gap. Reproducing it
> in QEMU turned up a second, older bug that corrupts memory once the
> mappings are aligned.
> 
> Patch 1 fixes the page size in hugetlb PTEs. pte_mkhuge() ORs the huge
> size into the base page size bits, so 64 KiB huge pages over 4 KiB
> pages are loaded into the TLB as 1 MiB pages. User writes then land in
> unrelated kernel memory. This dates back to the start of git history.
> 
> Patch 2 aligns hugetlb mappings to the huge page size. Without it
> mmap(MAP_HUGETLB) can return an address that is not aligned to the huge
> page size, and process exit hits the BUG_ON() in
> __unmap_hugepage_range(). This is a 6.13
> regression.
> 
> Patch 1 goes first because patch 2 alone makes the corruption reachable
> through a plain mmap(). Both are marked for stable.
> 
> Tested on QEMU r2d (SH7751R, rts7751r2dplus_defconfig plus HUGETLBFS
> with 64 KiB huge pages, gcc 15.2) on top of fddfc3ec3179. A test init
> maps two huge pages with the default and legacy layouts and with
> MAP_FIXED, writes and reads back every 4 KiB, mprotect()s the mapping
> read-only and back, and checks a COW write in a forked child:
> 
>   kernel          MAP_FIXED       default/legacy mmap
>   base            memory corrupt  misaligned, BUG at mm/hugetlb.c:5215
>   patch 1         pass            misaligned, BUG
>   patches 1+2     pass            pass
> 
> SH-X2 (X2TLB) is only build tested (r7785rp_defconfig with 64 KiB huge
> pages).
> 
> QEMU's SH-4 TLB emulation flushes only the first 4 KiB of a 64 KiB or
> 1 MiB entry when the entry is dropped [2]. Keep that in mind when
> testing hugetlb on QEMU.
> 
> [1] https://lore.kernel.org/r/5AB6FE85-4CB9-41EF-A700-BC5E071F29F5@linux.dev
> [2] https://gitlab.com/qemu-project/qemu/-/issues/4598
> 
> Karl Mehltretter (2):
>   sh: mm: replace the page size bits in pte_mkhuge()
>   sh: mm: align hugetlb mappings to the huge page size
> 
>  arch/sh/include/asm/pgtable_32.h |  7 +++++--
>  arch/sh/mm/mmap.c                | 11 +++++++++--
>  2 files changed, 14 insertions(+), 4 deletions(-)

Thanks for the patch! This should definitely be tested on real hardware
and I think it's a good reason to test this on my SH7785LCR which wouldn't
boot on anything newer than 6.5.0.

Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/2] sh: mm: fix hugetlb on SH-4
  2026-09-26 18:43 ` [PATCH 0/2] sh: mm: fix hugetlb on SH-4 John Paul Adrian Glaubitz
@ 2026-09-27 18:35   ` Karl Mehltretter
  2026-09-27 19:58     ` John Paul Adrian Glaubitz
  0 siblings, 1 reply; 6+ messages in thread
From: Karl Mehltretter @ 2026-09-27 18:35 UTC (permalink / raw)
  To: John Paul Adrian Glaubitz
  Cc: Yoshinori Sato, Rich Felker, linux-sh, Muchun Song,
	Oscar Salvador, David Hildenbrand, Andrew Morton, linux-mm,
	linux-kernel

On Sat, Sep 26, 2026 at 08:43:45PM +0100, John Paul Adrian Glaubitz wrote:
> Thanks for the patch! This should definitely be tested on real hardware
> and I think it's a good reason to test this on my SH7785LCR which wouldn't
> boot on anything newer than 6.5.0.
> 

Hi Adrian,

Testing with real hardware is very cool! Maybe I will get some SH
hardware myself one day. For now you've motivated me to do a QEMU SuperH
fork with more features, with a lot of help from my LLM agent:

https://github.com/kmehltretter82/qemu-superh

I will send some bug fix patches based on it soon, hopefully also fixing
your 6.5 problem. I believe it is an old heap overflow in the SH intc
code that only started to crash when sh7785lcr_defconfig switched from
SLAB to SLUB in 6.5.

Karl

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/2] sh: mm: fix hugetlb on SH-4
  2026-09-27 18:35   ` Karl Mehltretter
@ 2026-09-27 19:58     ` John Paul Adrian Glaubitz
  0 siblings, 0 replies; 6+ messages in thread
From: John Paul Adrian Glaubitz @ 2026-09-27 19:58 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: Yoshinori Sato, Rich Felker, linux-sh, Muchun Song,
	Oscar Salvador, David Hildenbrand, Andrew Morton, linux-mm,
	linux-kernel

Hi Karl,

On Sun, 2026-09-27 at 20:35 +0200, Karl Mehltretter wrote:
> On Sat, Sep 26, 2026 at 08:43:45PM +0100, John Paul Adrian Glaubitz wrote:
> > Thanks for the patch! This should definitely be tested on real hardware
> > and I think it's a good reason to test this on my SH7785LCR which wouldn't
> > boot on anything newer than 6.5.0.
> > 
> 
> Testing with real hardware is very cool! Maybe I will get some SH
> hardware myself one day. For now you've motivated me to do a QEMU SuperH
> fork with more features, with a lot of help from my LLM agent:
> 
> https://github.com/kmehltretter82/qemu-superh

Thanks, appreciate the patches. I'll try to look at these patches tomorrow.

Since I am reviewing them in detail, it can take me a few hours to properly
review them. Usually, I'm also asking Geert to have a look at them as well
since he is a far more experienced kernel developer than I am.

> I will send some bug fix patches based on it soon, hopefully also fixing
> your 6.5 problem. I believe it is an old heap overflow in the SH intc
> code that only started to crash when sh7785lcr_defconfig switched from
> SLAB to SLUB in 6.5.

Great, I'll give that patch a try tomorrow when I'm back home. Would be
great to unbreak my favorite SH board.

Btw, feel free to join us in #linux-sh on Libera IRC and/or on Discord:

https://discord.gg/WhtqXkYys

Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-27 19:58 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-26 18:39 [PATCH 0/2] sh: mm: fix hugetlb on SH-4 Karl Mehltretter
2026-09-26 18:39 ` [PATCH 1/2] sh: mm: replace the page size bits in pte_mkhuge() Karl Mehltretter
2026-09-26 18:39 ` [PATCH 2/2] sh: mm: align hugetlb mappings to the huge page size Karl Mehltretter
2026-09-26 18:43 ` [PATCH 0/2] sh: mm: fix hugetlb on SH-4 John Paul Adrian Glaubitz
2026-09-27 18:35   ` Karl Mehltretter
2026-09-27 19:58     ` John Paul Adrian Glaubitz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.