* [PATCH 1/5] mm: Introduce vm_mmap_shadow_stack() as a helper for VM_SHADOW_STACK mappings
2026-02-24 17:57 [PATCH 0/5] mm: arch/shstk: Common shadow stack mapping helper and VM_NOHUGEPAGE Catalin Marinas
@ 2026-02-24 17:57 ` Catalin Marinas
2026-02-24 19:47 ` Edgecombe, Rick P
2026-02-24 17:57 ` [PATCH 2/5] arm64: gcs: Use the new common vm_mmap_shadow_stack() helper Catalin Marinas
` (3 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Catalin Marinas @ 2026-02-24 17:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Mark Brown, Deepak Gupta,
Rick Edgecombe
Cc: Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, linux-arm-kernel, linux-kernel,
linux-riscv, linux-mm
arm64, riscv and x86 use a similar pattern for mapping the user shadow
stack (cloned from x86). Extract this into a helper to facilitate code
reuse.
The call to do_mmap() from the new helper uses PROT_READ|PROT_WRITE prot
bits instead of the PROT_READ with an explicit VM_WRITE vm_flag. The
x86 intent was to avoid PROT_WRITE implying normal write since the
shadow stack is not writable by normal stores. However, from a kernel
perspective, the vma is writeable. Functionally there is no difference.
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: David Hildenbrand <david@kernel.org>
---
include/linux/mm.h | 4 ++++
mm/util.c | 25 +++++++++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 5be3d8a8f806..1f28be975f86 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3908,6 +3908,10 @@ extern int vm_munmap(unsigned long, size_t);
extern unsigned long __must_check vm_mmap(struct file *, unsigned long,
unsigned long, unsigned long,
unsigned long, unsigned long);
+#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
+extern unsigned long __must_check vm_mmap_shadow_stack(unsigned long addr,
+ unsigned long len, unsigned long flags);
+#endif
struct vm_unmapped_area_info {
#define VM_UNMAPPED_AREA_TOPDOWN 1
diff --git a/mm/util.c b/mm/util.c
index b05ab6f97e11..2592291948f0 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -618,6 +618,31 @@ unsigned long vm_mmap(struct file *file, unsigned long addr,
}
EXPORT_SYMBOL(vm_mmap);
+#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
+/*
+ * Perform a userland memory mapping for a shadow stack into the current
+ * process address space. This is intended to be used by architectures that
+ * support user shadow stacks.
+ */
+unsigned long vm_mmap_shadow_stack(unsigned long addr, unsigned long len,
+ unsigned long flags)
+{
+ struct mm_struct *mm = current->mm;
+ unsigned long ret, unused;
+
+ flags |= MAP_ANONYMOUS | MAP_PRIVATE;
+ if (addr)
+ flags |= MAP_FIXED_NOREPLACE;
+
+ mmap_write_lock(mm);
+ ret = do_mmap(NULL, addr, len, PROT_READ | PROT_WRITE, flags,
+ VM_SHADOW_STACK, 0, &unused, NULL);
+ mmap_write_unlock(mm);
+
+ return ret;
+}
+#endif /* CONFIG_ARCH_HAS_USER_SHADOW_STACK */
+
/**
* __vmalloc_array - allocate memory for a virtually contiguous array.
* @n: number of elements.
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 1/5] mm: Introduce vm_mmap_shadow_stack() as a helper for VM_SHADOW_STACK mappings
2026-02-24 17:57 ` [PATCH 1/5] mm: Introduce vm_mmap_shadow_stack() as a helper for VM_SHADOW_STACK mappings Catalin Marinas
@ 2026-02-24 19:47 ` Edgecombe, Rick P
0 siblings, 0 replies; 8+ messages in thread
From: Edgecombe, Rick P @ 2026-02-24 19:47 UTC (permalink / raw)
To: david, broonie, catalin.marinas, akpm, debug
Cc: linux-kernel, linux-riscv, bp, dave.hansen, pjw, alex, aou,
palmer, will, tglx, hpa, mingo, linux-mm, linux-arm-kernel
On Tue, 2026-02-24 at 17:57 +0000, Catalin Marinas wrote:
> arm64, riscv and x86 use a similar pattern for mapping the user shadow
> stack (cloned from x86). Extract this into a helper to facilitate code
> reuse.
>
> The call to do_mmap() from the new helper uses PROT_READ|PROT_WRITE prot
> bits instead of the PROT_READ with an explicit VM_WRITE vm_flag. The
> x86 intent was to avoid PROT_WRITE implying normal write since the
> shadow stack is not writable by normal stores. However, from a kernel
> perspective, the vma is writeable. Functionally there is no difference.
We allow mprotect()ing shadow stack memory with PROT_WRITE to set or clear
shadow stack type of writability. So PROT_WRITE doesn't even imply normal
writable memory in the real mmap() syscall. I agree the code is clearer with
this change.
>
> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: David Hildenbrand <david@kernel.org>
> ---
> include/linux/mm.h | 4 ++++
> mm/util.c | 25 +++++++++++++++++++++++++
> 2 files changed, 29 insertions(+)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 5be3d8a8f806..1f28be975f86 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3908,6 +3908,10 @@ extern int vm_munmap(unsigned long, size_t);
> extern unsigned long __must_check vm_mmap(struct file *, unsigned long,
> unsigned long, unsigned long,
> unsigned long, unsigned long);
> +#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
Why does it need to compile out the declaration here?
> +extern unsigned long __must_check vm_mmap_shadow_stack(unsigned long addr,
> + unsigned long len, unsigned long flags);
> +#endif
>
> struct vm_unmapped_area_info {
> #define VM_UNMAPPED_AREA_TOPDOWN 1
> diff --git a/mm/util.c b/mm/util.c
> index b05ab6f97e11..2592291948f0 100644
> --- a/mm/util.c
> +++ b/mm/util.c
> @@ -618,6 +618,31 @@ unsigned long vm_mmap(struct file *file, unsigned long addr,
> }
> EXPORT_SYMBOL(vm_mmap);
>
> +#ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
> +/*
> + * Perform a userland memory mapping for a shadow stack into the current
> + * process address space. This is intended to be used by architectures that
> + * support user shadow stacks.
> + */
> +unsigned long vm_mmap_shadow_stack(unsigned long addr, unsigned long len,
> + unsigned long flags)
> +{
> + struct mm_struct *mm = current->mm;
> + unsigned long ret, unused;
> +
> + flags |= MAP_ANONYMOUS | MAP_PRIVATE;
> + if (addr)
> + flags |= MAP_FIXED_NOREPLACE;
> +
> + mmap_write_lock(mm);
> + ret = do_mmap(NULL, addr, len, PROT_READ | PROT_WRITE, flags,
> + VM_SHADOW_STACK, 0, &unused, NULL);
> + mmap_write_unlock(mm);
> +
> + return ret;
> +}
> +#endif /* CONFIG_ARCH_HAS_USER_SHADOW_STACK */
> +
> /**
> * __vmalloc_array - allocate memory for a virtually contiguous array.
> * @n: number of elements.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/5] arm64: gcs: Use the new common vm_mmap_shadow_stack() helper
2026-02-24 17:57 [PATCH 0/5] mm: arch/shstk: Common shadow stack mapping helper and VM_NOHUGEPAGE Catalin Marinas
2026-02-24 17:57 ` [PATCH 1/5] mm: Introduce vm_mmap_shadow_stack() as a helper for VM_SHADOW_STACK mappings Catalin Marinas
@ 2026-02-24 17:57 ` Catalin Marinas
2026-02-24 17:57 ` [PATCH 3/5] riscv: shstk: " Catalin Marinas
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2026-02-24 17:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Mark Brown, Deepak Gupta,
Rick Edgecombe
Cc: Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, linux-arm-kernel, linux-kernel,
linux-riscv, linux-mm
Replace the arm64 map_shadow_stack() content with a call to
vm_mmap_shadow_stack().
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
---
arch/arm64/mm/gcs.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/arch/arm64/mm/gcs.c b/arch/arm64/mm/gcs.c
index 04a23a497f20..680749611a9a 100644
--- a/arch/arm64/mm/gcs.c
+++ b/arch/arm64/mm/gcs.c
@@ -12,19 +12,7 @@
static unsigned long alloc_gcs(unsigned long addr, unsigned long size)
{
- int flags = MAP_ANONYMOUS | MAP_PRIVATE;
- struct mm_struct *mm = current->mm;
- unsigned long mapped_addr, unused;
-
- if (addr)
- flags |= MAP_FIXED_NOREPLACE;
-
- mmap_write_lock(mm);
- mapped_addr = do_mmap(NULL, addr, size, PROT_READ, flags,
- VM_SHADOW_STACK | VM_WRITE, 0, &unused, NULL);
- mmap_write_unlock(mm);
-
- return mapped_addr;
+ return vm_mmap_shadow_stack(addr, size, 0);
}
static unsigned long gcs_size(unsigned long size)
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 3/5] riscv: shstk: Use the new common vm_mmap_shadow_stack() helper
2026-02-24 17:57 [PATCH 0/5] mm: arch/shstk: Common shadow stack mapping helper and VM_NOHUGEPAGE Catalin Marinas
2026-02-24 17:57 ` [PATCH 1/5] mm: Introduce vm_mmap_shadow_stack() as a helper for VM_SHADOW_STACK mappings Catalin Marinas
2026-02-24 17:57 ` [PATCH 2/5] arm64: gcs: Use the new common vm_mmap_shadow_stack() helper Catalin Marinas
@ 2026-02-24 17:57 ` Catalin Marinas
2026-02-24 17:57 ` [PATCH 4/5] x86: " Catalin Marinas
2026-02-24 17:57 ` [PATCH 5/5] mm: Do not map the shadow stack as THP Catalin Marinas
4 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2026-02-24 17:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Mark Brown, Deepak Gupta,
Rick Edgecombe
Cc: Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, linux-arm-kernel, linux-kernel,
linux-riscv, linux-mm
Replace part of the allocate_shadow_stack() content with a call to
vm_mmap_shadow_stack().
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Deepak Gupta <debug@rivosinc.com>
---
arch/riscv/kernel/usercfi.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/arch/riscv/kernel/usercfi.c b/arch/riscv/kernel/usercfi.c
index 1adba746f164..7e57f54dc5b2 100644
--- a/arch/riscv/kernel/usercfi.c
+++ b/arch/riscv/kernel/usercfi.c
@@ -230,17 +230,7 @@ int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr)
static unsigned long allocate_shadow_stack(unsigned long addr, unsigned long size,
unsigned long token_offset, bool set_tok)
{
- int flags = MAP_ANONYMOUS | MAP_PRIVATE;
- struct mm_struct *mm = current->mm;
- unsigned long populate;
-
- if (addr)
- flags |= MAP_FIXED_NOREPLACE;
-
- mmap_write_lock(mm);
- addr = do_mmap(NULL, addr, size, PROT_READ, flags,
- VM_SHADOW_STACK | VM_WRITE, 0, &populate, NULL);
- mmap_write_unlock(mm);
+ addr = vm_mmap_shadow_stack(addr, size, 0);
if (!set_tok || IS_ERR_VALUE(addr))
goto out;
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 4/5] x86: shstk: Use the new common vm_mmap_shadow_stack() helper
2026-02-24 17:57 [PATCH 0/5] mm: arch/shstk: Common shadow stack mapping helper and VM_NOHUGEPAGE Catalin Marinas
` (2 preceding siblings ...)
2026-02-24 17:57 ` [PATCH 3/5] riscv: shstk: " Catalin Marinas
@ 2026-02-24 17:57 ` Catalin Marinas
2026-02-24 19:47 ` Edgecombe, Rick P
2026-02-24 17:57 ` [PATCH 5/5] mm: Do not map the shadow stack as THP Catalin Marinas
4 siblings, 1 reply; 8+ messages in thread
From: Catalin Marinas @ 2026-02-24 17:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Mark Brown, Deepak Gupta,
Rick Edgecombe
Cc: Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, linux-arm-kernel, linux-kernel,
linux-riscv, linux-mm
Replace part of the x86 alloc_shstk() content with a call to
vm_mmap_shadow_stack().
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
arch/x86/kernel/shstk.c | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c
index 978232b6d48d..9725e7d89b1e 100644
--- a/arch/x86/kernel/shstk.c
+++ b/arch/x86/kernel/shstk.c
@@ -100,17 +100,9 @@ static int create_rstor_token(unsigned long ssp, unsigned long *token_addr)
static unsigned long alloc_shstk(unsigned long addr, unsigned long size,
unsigned long token_offset, bool set_res_tok)
{
- int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_ABOVE4G;
- struct mm_struct *mm = current->mm;
- unsigned long mapped_addr, unused;
+ unsigned long mapped_addr;
- if (addr)
- flags |= MAP_FIXED_NOREPLACE;
-
- mmap_write_lock(mm);
- mapped_addr = do_mmap(NULL, addr, size, PROT_READ, flags,
- VM_SHADOW_STACK | VM_WRITE, 0, &unused, NULL);
- mmap_write_unlock(mm);
+ mapped_addr = vm_mmap_shadow_stack(addr, size, MAP_ABOVE4G);
if (!set_res_tok || IS_ERR_VALUE(mapped_addr))
goto out;
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 4/5] x86: shstk: Use the new common vm_mmap_shadow_stack() helper
2026-02-24 17:57 ` [PATCH 4/5] x86: " Catalin Marinas
@ 2026-02-24 19:47 ` Edgecombe, Rick P
0 siblings, 0 replies; 8+ messages in thread
From: Edgecombe, Rick P @ 2026-02-24 19:47 UTC (permalink / raw)
To: david, broonie, catalin.marinas, akpm, debug
Cc: linux-kernel, linux-riscv, bp, dave.hansen, pjw, alex, aou,
palmer, will, tglx, hpa, mingo, linux-mm, linux-arm-kernel
On Tue, 2026-02-24 at 17:57 +0000, Catalin Marinas wrote:
> Replace part of the x86 alloc_shstk() content with a call to
> vm_mmap_shadow_stack().
>
I ran it through the selftest. It would be nice to have a bit more information
in the log, like that there is no functional change. Otherwise,
Reviewed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Tested-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: H. Peter Anvin <hpa@zytor.com>
> Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
> ---
> arch/x86/kernel/shstk.c | 12 ++----------
> 1 file changed, 2 insertions(+), 10 deletions(-)
>
> diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c
> index 978232b6d48d..9725e7d89b1e 100644
> --- a/arch/x86/kernel/shstk.c
> +++ b/arch/x86/kernel/shstk.c
> @@ -100,17 +100,9 @@ static int create_rstor_token(unsigned long ssp, unsigned long *token_addr)
> static unsigned long alloc_shstk(unsigned long addr, unsigned long size,
> unsigned long token_offset, bool set_res_tok)
> {
> - int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_ABOVE4G;
> - struct mm_struct *mm = current->mm;
> - unsigned long mapped_addr, unused;
> + unsigned long mapped_addr;
>
> - if (addr)
> - flags |= MAP_FIXED_NOREPLACE;
> -
> - mmap_write_lock(mm);
> - mapped_addr = do_mmap(NULL, addr, size, PROT_READ, flags,
> - VM_SHADOW_STACK | VM_WRITE, 0, &unused, NULL);
> - mmap_write_unlock(mm);
> + mapped_addr = vm_mmap_shadow_stack(addr, size, MAP_ABOVE4G);
>
> if (!set_res_tok || IS_ERR_VALUE(mapped_addr))
> goto out;
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 5/5] mm: Do not map the shadow stack as THP
2026-02-24 17:57 [PATCH 0/5] mm: arch/shstk: Common shadow stack mapping helper and VM_NOHUGEPAGE Catalin Marinas
` (3 preceding siblings ...)
2026-02-24 17:57 ` [PATCH 4/5] x86: " Catalin Marinas
@ 2026-02-24 17:57 ` Catalin Marinas
4 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2026-02-24 17:57 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Mark Brown, Deepak Gupta,
Rick Edgecombe
Cc: Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, linux-arm-kernel, linux-kernel,
linux-riscv, linux-mm
The default shadow stack size allocated on first prctl() for the main
thread or subsequently on clone() is either half of RLIMIT_STACK or half
of a thread's stack size (for arm64). Both of these are likely to be
suitable for a THP allocation and the kernel is more aggressive in
creating such mappings. However, it does not make much sense to use a
huge page. It didn't make sense for the normal stacks either, see commit
c4608d1bf7c6 ("mm: mmap: map MAP_STACK to VM_NOHUGEPAGE").
Force VM_NOHUGEPAGE when allocating/mapping the shadow stack. As per
commit 7190b3c8bd2b ("mm: mmap: map MAP_STACK to VM_NOHUGEPAGE only if
THP is enabled"), only pass this flag if TRANSPARENT_HUGEPAGE is enabled
as not to confuse CRIU tools.
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: David Hildenbrand <david@kernel.org>
---
mm/util.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/mm/util.c b/mm/util.c
index 2592291948f0..9f33c6de6082 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -629,14 +629,18 @@ unsigned long vm_mmap_shadow_stack(unsigned long addr, unsigned long len,
{
struct mm_struct *mm = current->mm;
unsigned long ret, unused;
+ vm_flags_t vm_flags = VM_SHADOW_STACK;
flags |= MAP_ANONYMOUS | MAP_PRIVATE;
if (addr)
flags |= MAP_FIXED_NOREPLACE;
+ if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+ vm_flags |= VM_NOHUGEPAGE;
+
mmap_write_lock(mm);
ret = do_mmap(NULL, addr, len, PROT_READ | PROT_WRITE, flags,
- VM_SHADOW_STACK, 0, &unused, NULL);
+ vm_flags, 0, &unused, NULL);
mmap_write_unlock(mm);
return ret;
^ permalink raw reply [flat|nested] 8+ messages in thread