* [PATCH v6 1/8] mm: pass the target mm parameter through get_unmapped_area family
2026-07-15 21:19 [PATCH v6 0/8] seccomp: non-cooperative pinned-memfd argument redirect Cong Wang
@ 2026-07-15 21:20 ` Cong Wang
2026-07-15 21:20 ` [PATCH v6 2/8] mm: add __do_mmap() and vm_mmap_remote()/vm_munmap_remote() Cong Wang
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Cong Wang @ 2026-07-15 21:20 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Kees Cook, linux-kernel, Will Drewry, Christian Brauner,
Andrew Morton, linux-mm, Cong Wang
From: Cong Wang <cwang@multikernel.io>
Every placement callback in the mmap path implicitly resolves against
current->mm, so a placement cannot be computed in another task's address
space. vm_mmap_remote() in the next patch needs this: without it a remote
install cannot run the file's own ->get_unmapped_area() and file-specific
placement rules are bypassed or rejected at MAP_FIXED validation.
Make the mm an explicit first parameter of the get_unmapped_area family
across the entire tree. It is difficult and not elegant to avoid such a
big change.
The get_unmapped_area() convenience inline keeps its old signature and
passes current->mm, so ordinary callers do not change. So no functional
change.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang@multikernel.io>
---
Documentation/filesystems/locking.rst | 2 +-
Documentation/filesystems/vfs.rst | 2 +-
arch/alpha/kernel/osf_sys.c | 21 +++---
arch/arc/mm/mmap.c | 7 +-
arch/arm/mm/mmap.c | 19 +++--
arch/csky/abiv1/mmap.c | 7 +-
arch/loongarch/mm/mmap.c | 28 ++++----
arch/mips/mm/mmap.c | 28 ++++----
arch/parisc/kernel/sys_parisc.c | 28 ++++----
arch/powerpc/include/asm/book3s/64/slice.h | 3 +-
arch/powerpc/mm/book3s64/slice.c | 28 ++++----
arch/s390/mm/mmap.c | 25 ++++---
arch/sh/mm/mmap.c | 21 +++---
arch/sparc/include/asm/pgtable_64.h | 6 +-
arch/sparc/kernel/sys_sparc_32.c | 7 +-
arch/sparc/kernel/sys_sparc_64.c | 32 +++++----
arch/x86/include/asm/elf.h | 2 +-
arch/x86/kernel/cpu/sgx/driver.c | 13 ++--
arch/x86/kernel/sys_x86_64.c | 32 +++++----
arch/x86/kernel/uprobes.c | 4 +-
arch/x86/mm/mmap.c | 4 +-
arch/xtensa/kernel/syscall.c | 8 +--
drivers/char/mem.c | 15 ++--
drivers/dax/device.c | 11 +--
drivers/gpu/drm/drm_gem.c | 9 ++-
drivers/gpu/drm/drm_gem_dma_helper.c | 4 +-
drivers/media/v4l2-core/v4l2-dev.c | 3 +-
drivers/mtd/mtdchar.c | 3 +-
drivers/video/fbdev/core/fb_chrdev.c | 3 +-
fs/cramfs/inode.c | 3 +-
fs/hugetlbfs/inode.c | 9 +--
fs/proc/inode.c | 15 ++--
fs/ramfs/file-mmu.c | 5 +-
fs/ramfs/file-nommu.c | 6 +-
fs/romfs/mmap-nommu.c | 3 +-
include/drm/drm_gem.h | 3 +-
include/drm/drm_gem_dma_helper.h | 3 +-
include/linux/bpf.h | 3 +-
include/linux/fs.h | 5 +-
include/linux/huge_mm.h | 18 ++---
include/linux/hugetlb.h | 6 +-
include/linux/mm.h | 12 ++--
include/linux/proc_fs.h | 5 +-
include/linux/sched/mm.h | 37 +++++-----
include/linux/shmem_fs.h | 5 +-
io_uring/memmap.c | 8 ++-
io_uring/memmap.h | 3 +-
ipc/shm.c | 8 +--
kernel/bpf/arena.c | 5 +-
kernel/bpf/syscall.c | 8 ++-
mm/huge_memory.c | 27 +++----
mm/mmap.c | 83 ++++++++++++----------
mm/nommu.c | 5 +-
mm/shmem.c | 13 ++--
mm/vma.c | 16 +++--
mm/vma.h | 6 +-
sound/core/pcm_native.c | 3 +-
57 files changed, 391 insertions(+), 307 deletions(-)
diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst
index 08d01bc62c31..b5defd74e302 100644
--- a/Documentation/filesystems/locking.rst
+++ b/Documentation/filesystems/locking.rst
@@ -471,7 +471,7 @@ prototypes::
int (*fsync) (struct file *, loff_t start, loff_t end, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
- unsigned long (*get_unmapped_area)(struct file *, unsigned long,
+ unsigned long (*get_unmapped_area)(struct mm_struct *, struct file *, unsigned long,
unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index 7c753148af88..4f95a272eab8 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -1023,7 +1023,7 @@ This describes how the VFS can manipulate an open file. As of kernel
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
- unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+ unsigned long (*get_unmapped_area)(struct mm_struct *, struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 7b6543d2cca3..80ae6d03e266 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -1201,21 +1201,22 @@ SYSCALL_DEFINE1(old_adjtimex, struct timex32 __user *, txc_p)
/* Get an address range which is currently unmapped. */
static unsigned long
-arch_get_unmapped_area_1(unsigned long addr, unsigned long len,
- unsigned long limit)
+arch_get_unmapped_area_1(struct mm_struct *mm, unsigned long addr,
+ unsigned long len, unsigned long limit)
{
struct vm_unmapped_area_info info = {};
info.length = len;
info.low_limit = addr;
info.high_limit = limit;
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
unsigned long
-arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
unsigned long limit = TASK_SIZE;
@@ -1236,19 +1237,19 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
this feature should be incorporated into all ports? */
if (addr) {
- addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit);
+ addr = arch_get_unmapped_area_1(mm, PAGE_ALIGN(addr), len, limit);
if (addr != (unsigned long) -ENOMEM)
return addr;
}
/* Next, try allocating at TASK_UNMAPPED_BASE. */
- addr = arch_get_unmapped_area_1 (PAGE_ALIGN(TASK_UNMAPPED_BASE),
- len, limit);
+ addr = arch_get_unmapped_area_1(mm, PAGE_ALIGN(TASK_UNMAPPED_BASE),
+ len, limit);
if (addr != (unsigned long) -ENOMEM)
return addr;
/* Finally, try allocating in low memory. */
- addr = arch_get_unmapped_area_1 (PAGE_SIZE, len, limit);
+ addr = arch_get_unmapped_area_1(mm, PAGE_SIZE, len, limit);
return addr;
}
diff --git a/arch/arc/mm/mmap.c b/arch/arc/mm/mmap.c
index 2185afe8d59f..d33d5443b616 100644
--- a/arch/arc/mm/mmap.c
+++ b/arch/arc/mm/mmap.c
@@ -22,11 +22,10 @@
* SHMLBA bytes.
*/
unsigned long
-arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
+arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
unsigned long flags, vm_flags_t vm_flags)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
struct vm_unmapped_area_info info = {};
@@ -56,7 +55,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
info.low_limit = mm->mmap_base;
info.high_limit = TASK_SIZE;
info.align_offset = pgoff << PAGE_SHIFT;
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
static const pgprot_t protection_map[16] = {
diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c
index 3dbb383c26d5..5d1169539ce3 100644
--- a/arch/arm/mm/mmap.c
+++ b/arch/arm/mm/mmap.c
@@ -27,11 +27,10 @@
* in the VIVT case, we optimise out the alignment rules.
*/
unsigned long
-arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
+arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
unsigned long flags, vm_flags_t vm_flags)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
int do_align = 0;
int aliasing = cache_is_vipt_aliasing();
@@ -74,16 +73,16 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
info.high_limit = TASK_SIZE;
info.align_mask = do_align ? (PAGE_MASK & (SHMLBA - 1)) : 0;
info.align_offset = pgoff << PAGE_SHIFT;
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
unsigned long
-arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
- const unsigned long len, const unsigned long pgoff,
- const unsigned long flags, vm_flags_t vm_flags)
+arch_get_unmapped_area_topdown(struct mm_struct *mm, struct file *filp,
+ const unsigned long addr0, const unsigned long len,
+ const unsigned long pgoff, const unsigned long flags,
+ vm_flags_t vm_flags)
{
struct vm_area_struct *vma;
- struct mm_struct *mm = current->mm;
unsigned long addr = addr0;
int do_align = 0;
int aliasing = cache_is_vipt_aliasing();
@@ -125,7 +124,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
info.high_limit = mm->mmap_base;
info.align_mask = do_align ? (PAGE_MASK & (SHMLBA - 1)) : 0;
info.align_offset = pgoff << PAGE_SHIFT;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
/*
* A failed mmap() very likely causes application failure,
@@ -138,7 +137,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
info.flags = 0;
info.low_limit = mm->mmap_base;
info.high_limit = TASK_SIZE;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
}
return addr;
diff --git a/arch/csky/abiv1/mmap.c b/arch/csky/abiv1/mmap.c
index 1047865e82a9..62eaca7a0d50 100644
--- a/arch/csky/abiv1/mmap.c
+++ b/arch/csky/abiv1/mmap.c
@@ -22,11 +22,10 @@
* We unconditionally provide this function for all cases.
*/
unsigned long
-arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
+arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
unsigned long flags, vm_flags_t vm_flags)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
int do_align = 0;
struct vm_unmapped_area_info info = {
@@ -68,5 +67,5 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
}
info.align_mask = do_align ? (PAGE_MASK & (SHMLBA - 1)) : 0;
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
diff --git a/arch/loongarch/mm/mmap.c b/arch/loongarch/mm/mmap.c
index 1df9e99582cc..600943121362 100644
--- a/arch/loongarch/mm/mmap.c
+++ b/arch/loongarch/mm/mmap.c
@@ -18,11 +18,11 @@
enum mmap_allocation_direction {UP, DOWN};
-static unsigned long arch_get_unmapped_area_common(struct file *filp,
- unsigned long addr0, unsigned long len, unsigned long pgoff,
- unsigned long flags, enum mmap_allocation_direction dir)
+static unsigned long arch_get_unmapped_area_common(struct mm_struct *mm,
+ struct file *filp, unsigned long addr0, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ enum mmap_allocation_direction dir)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
unsigned long addr = addr0;
int do_color_align;
@@ -74,7 +74,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp,
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
info.low_limit = PAGE_SIZE;
info.high_limit = mm->mmap_base;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
if (!(addr & ~PAGE_MASK))
return addr;
@@ -89,14 +89,14 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp,
info.low_limit = mm->mmap_base;
info.high_limit = TASK_SIZE;
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr0,
- unsigned long len, unsigned long pgoff, unsigned long flags,
- vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr0, unsigned long len, unsigned long pgoff,
+ unsigned long flags, vm_flags_t vm_flags)
{
- return arch_get_unmapped_area_common(filp,
+ return arch_get_unmapped_area_common(mm, filp,
addr0, len, pgoff, flags, UP);
}
@@ -104,11 +104,11 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr0,
* There is no need to export this but sched.h declares the function as
* extern so making it static here results in an error.
*/
-unsigned long arch_get_unmapped_area_topdown(struct file *filp,
- unsigned long addr0, unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area_topdown(struct mm_struct *mm,
+ struct file *filp, unsigned long addr0, unsigned long len,
+ unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags)
{
- return arch_get_unmapped_area_common(filp,
+ return arch_get_unmapped_area_common(mm, filp,
addr0, len, pgoff, flags, DOWN);
}
diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c
index 5d2a1225785b..8905bba30aa0 100644
--- a/arch/mips/mm/mmap.c
+++ b/arch/mips/mm/mmap.c
@@ -26,11 +26,11 @@ EXPORT_SYMBOL(shm_align_mask);
enum mmap_allocation_direction {UP, DOWN};
-static unsigned long arch_get_unmapped_area_common(struct file *filp,
- unsigned long addr0, unsigned long len, unsigned long pgoff,
- unsigned long flags, enum mmap_allocation_direction dir)
+static unsigned long arch_get_unmapped_area_common(struct mm_struct *mm,
+ struct file *filp, unsigned long addr0, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ enum mmap_allocation_direction dir)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
unsigned long addr = addr0;
int do_color_align;
@@ -79,7 +79,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp,
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
info.low_limit = PAGE_SIZE;
info.high_limit = mm->mmap_base;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
if (!(addr & ~PAGE_MASK))
return addr;
@@ -94,14 +94,14 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp,
info.low_limit = mm->mmap_base;
info.high_limit = TASK_SIZE;
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr0,
- unsigned long len, unsigned long pgoff, unsigned long flags,
- vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr0, unsigned long len, unsigned long pgoff,
+ unsigned long flags, vm_flags_t vm_flags)
{
- return arch_get_unmapped_area_common(filp,
+ return arch_get_unmapped_area_common(mm, filp,
addr0, len, pgoff, flags, UP);
}
@@ -109,11 +109,11 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr0,
* There is no need to export this but sched.h declares the function as
* extern so making it static here results in an error.
*/
-unsigned long arch_get_unmapped_area_topdown(struct file *filp,
- unsigned long addr0, unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area_topdown(struct mm_struct *mm,
+ struct file *filp, unsigned long addr0, unsigned long len,
+ unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags)
{
- return arch_get_unmapped_area_common(filp,
+ return arch_get_unmapped_area_common(mm, filp,
addr0, len, pgoff, flags, DOWN);
}
diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c
index 1a676a9bf80c..b9bf734cf678 100644
--- a/arch/parisc/kernel/sys_parisc.c
+++ b/arch/parisc/kernel/sys_parisc.c
@@ -100,11 +100,11 @@ unsigned long mmap_upper_limit(const struct rlimit *rlim_stack)
enum mmap_allocation_direction {UP, DOWN};
-static unsigned long arch_get_unmapped_area_common(struct file *filp,
- unsigned long addr, unsigned long len, unsigned long pgoff,
- unsigned long flags, enum mmap_allocation_direction dir)
+static unsigned long arch_get_unmapped_area_common(struct mm_struct *mm,
+ struct file *filp, unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ enum mmap_allocation_direction dir)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma, *prev;
unsigned long filp_pgoff;
int do_color_align;
@@ -152,7 +152,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp,
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
info.low_limit = PAGE_SIZE;
info.high_limit = mm->mmap_base;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
if (!(addr & ~PAGE_MASK))
return addr;
VM_BUG_ON(addr != -ENOMEM);
@@ -167,22 +167,22 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp,
info.low_limit = mm->mmap_base;
info.high_limit = mmap_upper_limit(NULL);
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags,
- vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags, vm_flags_t vm_flags)
{
- return arch_get_unmapped_area_common(filp,
+ return arch_get_unmapped_area_common(mm, filp,
addr, len, pgoff, flags, UP);
}
-unsigned long arch_get_unmapped_area_topdown(struct file *filp,
- unsigned long addr, unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area_topdown(struct mm_struct *mm,
+ struct file *filp, unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags)
{
- return arch_get_unmapped_area_common(filp,
+ return arch_get_unmapped_area_common(mm, filp,
addr, len, pgoff, flags, DOWN);
}
diff --git a/arch/powerpc/include/asm/book3s/64/slice.h b/arch/powerpc/include/asm/book3s/64/slice.h
index 6e2f7a74cd75..d95d9e292443 100644
--- a/arch/powerpc/include/asm/book3s/64/slice.h
+++ b/arch/powerpc/include/asm/book3s/64/slice.h
@@ -25,7 +25,8 @@
struct mm_struct;
-unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
+unsigned long slice_get_unmapped_area(struct mm_struct *mm,
+ unsigned long addr, unsigned long len,
unsigned long flags, unsigned int psize,
int topdown);
diff --git a/arch/powerpc/mm/book3s64/slice.c b/arch/powerpc/mm/book3s64/slice.c
index 28bec5bc7879..ab8b24f6287d 100644
--- a/arch/powerpc/mm/book3s64/slice.c
+++ b/arch/powerpc/mm/book3s64/slice.c
@@ -311,7 +311,7 @@ static unsigned long slice_find_area_bottomup(struct mm_struct *mm,
}
info.high_limit = addr;
- found = vm_unmapped_area(&info);
+ found = vm_unmapped_area(mm, &info);
if (!(found & ~PAGE_MASK))
return found;
}
@@ -362,7 +362,7 @@ static unsigned long slice_find_area_topdown(struct mm_struct *mm,
}
info.low_limit = addr;
- found = vm_unmapped_area(&info);
+ found = vm_unmapped_area(mm, &info);
if (!(found & ~PAGE_MASK))
return found;
}
@@ -422,7 +422,8 @@ static inline void slice_andnot_mask(struct slice_mask *dst,
#define MMU_PAGE_BASE MMU_PAGE_4K
#endif
-unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
+unsigned long slice_get_unmapped_area(struct mm_struct *mm,
+ unsigned long addr, unsigned long len,
unsigned long flags, unsigned int psize,
int topdown)
{
@@ -433,7 +434,6 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
int fixed = (flags & MAP_FIXED);
int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
unsigned long page_size = 1UL << pshift;
- struct mm_struct *mm = current->mm;
unsigned long newaddr;
unsigned long high_limit;
@@ -649,7 +649,8 @@ static int file_to_psize(struct file *file)
}
#endif
-unsigned long arch_get_unmapped_area(struct file *filp,
+unsigned long arch_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
@@ -659,17 +660,19 @@ unsigned long arch_get_unmapped_area(struct file *filp,
unsigned int psize;
if (radix_enabled())
- return generic_get_unmapped_area(filp, addr, len, pgoff, flags, vm_flags);
+ return generic_get_unmapped_area(mm, filp, addr, len, pgoff,
+ flags, vm_flags);
if (filp && is_file_hugepages(filp))
psize = file_to_psize(filp);
else
- psize = mm_ctx_user_psize(¤t->mm->context);
+ psize = mm_ctx_user_psize(&mm->context);
- return slice_get_unmapped_area(addr, len, flags, psize, 0);
+ return slice_get_unmapped_area(mm, addr, len, flags, psize, 0);
}
-unsigned long arch_get_unmapped_area_topdown(struct file *filp,
+unsigned long arch_get_unmapped_area_topdown(struct mm_struct *mm,
+ struct file *filp,
const unsigned long addr0,
const unsigned long len,
const unsigned long pgoff,
@@ -679,14 +682,15 @@ unsigned long arch_get_unmapped_area_topdown(struct file *filp,
unsigned int psize;
if (radix_enabled())
- return generic_get_unmapped_area_topdown(filp, addr0, len, pgoff, flags, vm_flags);
+ return generic_get_unmapped_area_topdown(mm, filp, addr0, len,
+ pgoff, flags, vm_flags);
if (filp && is_file_hugepages(filp))
psize = file_to_psize(filp);
else
- psize = mm_ctx_user_psize(¤t->mm->context);
+ psize = mm_ctx_user_psize(&mm->context);
- return slice_get_unmapped_area(addr0, len, flags, psize, 1);
+ return slice_get_unmapped_area(mm, addr0, len, flags, psize, 1);
}
unsigned int notrace get_slice_psize(struct mm_struct *mm, unsigned long addr)
diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c
index ef7bfc87758c..92332e9f5229 100644
--- a/arch/s390/mm/mmap.c
+++ b/arch/s390/mm/mmap.c
@@ -75,11 +75,11 @@ static unsigned long get_align_mask(struct file *filp, unsigned long flags)
return 0;
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
struct vm_unmapped_area_info info = {};
@@ -103,7 +103,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
info.align_mask = get_align_mask(filp, flags);
if (!(filp && is_file_hugepages(filp)))
info.align_offset = pgoff << PAGE_SHIFT;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
if (offset_in_page(addr))
return addr;
@@ -111,12 +111,15 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
return check_asce_limit(mm, addr, len);
}
-unsigned long arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area_topdown(struct mm_struct *mm,
+ struct file *filp,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags,
+ vm_flags_t vm_flags)
{
struct vm_area_struct *vma;
- struct mm_struct *mm = current->mm;
struct vm_unmapped_area_info info = {};
/* requested length too big for entire address space */
@@ -142,7 +145,7 @@ unsigned long arch_get_unmapped_area_topdown(struct file *filp, unsigned long ad
info.align_mask = get_align_mask(filp, flags);
if (!(filp && is_file_hugepages(filp)))
info.align_offset = pgoff << PAGE_SHIFT;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
/*
* A failed mmap() very likely causes application failure,
@@ -155,7 +158,7 @@ unsigned long arch_get_unmapped_area_topdown(struct file *filp, unsigned long ad
info.flags = 0;
info.low_limit = TASK_UNMAPPED_BASE;
info.high_limit = TASK_SIZE;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
if (offset_in_page(addr))
return addr;
}
diff --git a/arch/sh/mm/mmap.c b/arch/sh/mm/mmap.c
index c442734d9b0c..d7a790521539 100644
--- a/arch/sh/mm/mmap.c
+++ b/arch/sh/mm/mmap.c
@@ -51,11 +51,10 @@ static inline unsigned long COLOUR_ALIGN(unsigned long addr,
return base + off;
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags,
- vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags, vm_flags_t vm_flags)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
int do_colour_align;
struct vm_unmapped_area_info info = {};
@@ -94,16 +93,16 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
info.high_limit = TASK_SIZE;
info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
info.align_offset = pgoff << PAGE_SHIFT;
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
unsigned long
-arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
- const unsigned long len, const unsigned long pgoff,
- const unsigned long flags, vm_flags_t vm_flags)
+arch_get_unmapped_area_topdown(struct mm_struct *mm, struct file *filp,
+ const unsigned long addr0, const unsigned long len,
+ const unsigned long pgoff, const unsigned long flags,
+ vm_flags_t vm_flags)
{
struct vm_area_struct *vma;
- struct mm_struct *mm = current->mm;
unsigned long addr = addr0;
int do_colour_align;
struct vm_unmapped_area_info info = {};
@@ -144,7 +143,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
info.high_limit = mm->mmap_base;
info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
info.align_offset = pgoff << PAGE_SHIFT;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
/*
* A failed mmap() very likely causes application failure,
@@ -157,7 +156,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
info.flags = 0;
info.low_limit = TASK_UNMAPPED_BASE;
info.high_limit = TASK_SIZE;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
}
return addr;
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index 74ede706fb32..b8bed39cddc9 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -1141,9 +1141,9 @@ static inline bool pte_access_permitted(pte_t pte, bool write)
/* We provide a special get_unmapped_area for framebuffer mmaps to try and use
* the largest alignment possible such that larget PTEs can be used.
*/
-unsigned long get_fb_unmapped_area(struct file *filp, unsigned long,
- unsigned long, unsigned long,
- unsigned long);
+unsigned long get_fb_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags);
#define HAVE_ARCH_FB_UNMAPPED_AREA
void sun4v_register_fault_status(void);
diff --git a/arch/sparc/kernel/sys_sparc_32.c b/arch/sparc/kernel/sys_sparc_32.c
index fb31bc0c5b48..930ca7f8a7dc 100644
--- a/arch/sparc/kernel/sys_sparc_32.c
+++ b/arch/sparc/kernel/sys_sparc_32.c
@@ -40,7 +40,10 @@ SYSCALL_DEFINE0(getpagesize)
return PAGE_SIZE; /* Possibly older binaries want 8192 on sun4's? */
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
struct vm_unmapped_area_info info = {};
bool file_hugepage = false;
@@ -74,7 +77,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi
} else {
info.align_mask = huge_page_mask_align(filp);
}
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
/*
diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index ecefcffcf7b1..e9f5e1b37ee9 100644
--- a/arch/sparc/kernel/sys_sparc_64.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
@@ -98,9 +98,11 @@ static unsigned long get_align_mask(struct file *filp, unsigned long flags)
return 0;
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct * vma;
unsigned long task_size = TASK_SIZE;
int do_color_align;
@@ -147,25 +149,25 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi
info.align_mask = get_align_mask(filp, flags);
if (!file_hugepage)
info.align_offset = pgoff << PAGE_SHIFT;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
if ((addr & ~PAGE_MASK) && task_size > VA_EXCLUDE_END) {
VM_BUG_ON(addr != -ENOMEM);
info.low_limit = VA_EXCLUDE_END;
info.high_limit = task_size;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
}
return addr;
}
unsigned long
-arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
- const unsigned long len, const unsigned long pgoff,
- const unsigned long flags, vm_flags_t vm_flags)
+arch_get_unmapped_area_topdown(struct mm_struct *mm, struct file *filp,
+ const unsigned long addr0, const unsigned long len,
+ const unsigned long pgoff, const unsigned long flags,
+ vm_flags_t vm_flags)
{
struct vm_area_struct *vma;
- struct mm_struct *mm = current->mm;
unsigned long task_size = STACK_TOP32;
unsigned long addr = addr0;
int do_color_align;
@@ -215,7 +217,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
info.align_mask = get_align_mask(filp, flags);
if (!file_hugepage)
info.align_offset = pgoff << PAGE_SHIFT;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
/*
* A failed mmap() very likely causes application failure,
@@ -228,20 +230,22 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
info.flags = 0;
info.low_limit = TASK_UNMAPPED_BASE;
info.high_limit = STACK_TOP32;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
}
return addr;
}
/* Try to align mapping such that we align it as much as possible. */
-unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, unsigned long len, unsigned long pgoff, unsigned long flags)
+unsigned long get_fb_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long orig_addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags)
{
unsigned long align_goal, addr = -ENOMEM;
if (flags & MAP_FIXED) {
/* Ok, don't mess with it. */
- return mm_get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, NULL, orig_addr, len, pgoff, flags);
}
flags &= ~MAP_SHARED;
@@ -254,7 +258,7 @@ unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, u
align_goal = (64UL * 1024);
do {
- addr = mm_get_unmapped_area(NULL, orig_addr,
+ addr = mm_get_unmapped_area(mm, NULL, orig_addr,
len + (align_goal - PAGE_SIZE), pgoff, flags);
if (!(addr & ~PAGE_MASK)) {
addr = (addr + (align_goal - 1UL)) & ~(align_goal - 1UL);
@@ -273,7 +277,7 @@ unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, u
* be obtained.
*/
if (addr & ~PAGE_MASK)
- addr = mm_get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
+ addr = mm_get_unmapped_area(mm, NULL, orig_addr, len, pgoff, flags);
return addr;
}
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index 0de9df759c99..12c66609889e 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -307,7 +307,7 @@ static inline int mmap_is_ia32(void)
extern unsigned long task_size_32bit(void);
extern unsigned long task_size_64bit(int full_addr_space);
-extern unsigned long get_mmap_base(int is_legacy);
+extern unsigned long get_mmap_base(struct mm_struct *mm, int is_legacy);
extern bool mmap_address_hint_valid(unsigned long addr, unsigned long len);
extern unsigned long get_sigframe_size(void);
diff --git a/arch/x86/kernel/cpu/sgx/driver.c b/arch/x86/kernel/cpu/sgx/driver.c
index 9268289cd9f9..f81652086909 100644
--- a/arch/x86/kernel/cpu/sgx/driver.c
+++ b/arch/x86/kernel/cpu/sgx/driver.c
@@ -121,11 +121,12 @@ static int sgx_mmap(struct file *file, struct vm_area_struct *vma)
return 0;
}
-static unsigned long sgx_get_unmapped_area(struct file *file,
- unsigned long addr,
- unsigned long len,
- unsigned long pgoff,
- unsigned long flags)
+static unsigned long sgx_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
{
if ((flags & MAP_TYPE) == MAP_PRIVATE)
return -EINVAL;
@@ -133,7 +134,7 @@ static unsigned long sgx_get_unmapped_area(struct file *file,
if (flags & MAP_FIXED)
return addr;
- return mm_get_unmapped_area(file, addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, file, addr, len, pgoff, flags);
}
#ifdef CONFIG_COMPAT
diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c
index 776ae6fa7f2d..d9c4353913a4 100644
--- a/arch/x86/kernel/sys_x86_64.c
+++ b/arch/x86/kernel/sys_x86_64.c
@@ -89,8 +89,9 @@ SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
}
-static void find_start_end(unsigned long addr, unsigned long flags,
- unsigned long *begin, unsigned long *end)
+static void find_start_end(struct mm_struct *mm, unsigned long addr,
+ unsigned long flags, unsigned long *begin,
+ unsigned long *end)
{
if (!in_32bit_syscall() && (flags & MAP_32BIT)) {
/* This is usually used needed to map code in small
@@ -108,7 +109,7 @@ static void find_start_end(unsigned long addr, unsigned long flags,
return;
}
- *begin = get_mmap_base(1);
+ *begin = get_mmap_base(mm, 1);
if (in_32bit_syscall())
*end = task_size_32bit();
else
@@ -124,10 +125,11 @@ static inline unsigned long stack_guard_placement(vm_flags_t vm_flags)
}
unsigned long
-arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len,
- unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags)
+arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
struct vm_unmapped_area_info info = {};
unsigned long begin, end;
@@ -135,7 +137,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len,
if (flags & MAP_FIXED)
return addr;
- find_start_end(addr, flags, &begin, &end);
+ find_start_end(mm, addr, flags, &begin, &end);
if (len > end)
return -ENOMEM;
@@ -160,16 +162,16 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len,
info.align_offset += get_align_bits();
}
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
unsigned long
-arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr0,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+arch_get_unmapped_area_topdown(struct mm_struct *mm, struct file *filp,
+ unsigned long addr0, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
struct vm_area_struct *vma;
- struct mm_struct *mm = current->mm;
unsigned long addr = addr0;
struct vm_unmapped_area_info info = {};
@@ -204,7 +206,7 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr0,
else
info.low_limit = PAGE_SIZE;
- info.high_limit = get_mmap_base(0);
+ info.high_limit = get_mmap_base(mm, 0);
if (!(filp && is_file_hugepages(filp))) {
info.start_gap = stack_guard_placement(vm_flags);
info.align_offset = pgoff << PAGE_SHIFT;
@@ -224,7 +226,7 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr0,
info.align_mask = get_align_mask(filp);
info.align_offset += get_align_bits();
}
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
if (!(addr & ~PAGE_MASK))
return addr;
VM_BUG_ON(addr != -ENOMEM);
@@ -236,5 +238,5 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr0,
* can happen with large stack limits and large mmap()
* allocations.
*/
- return arch_get_unmapped_area(filp, addr0, len, pgoff, flags, 0);
+ return arch_get_unmapped_area(mm, filp, addr0, len, pgoff, flags, 0);
}
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index 3af979fb41d3..417153d02c54 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -661,13 +661,13 @@ static unsigned long find_nearest_trampoline(unsigned long vaddr)
/* Search up from the caller address. */
info.low_limit = call_end;
info.high_limit = min(high_limit, TASK_SIZE);
- high_tramp = vm_unmapped_area(&info);
+ high_tramp = vm_unmapped_area(current->mm, &info);
/* Search down from the caller address. */
info.low_limit = max(low_limit, PAGE_SIZE);
info.high_limit = call_end;
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
- low_tramp = vm_unmapped_area(&info);
+ low_tramp = vm_unmapped_area(current->mm, &info);
if (IS_ERR_VALUE(high_tramp) && IS_ERR_VALUE(low_tramp))
return -ENOMEM;
diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
index 82f3a987f7cf..75c864f3c0d8 100644
--- a/arch/x86/mm/mmap.c
+++ b/arch/x86/mm/mmap.c
@@ -143,10 +143,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm, const struct rlimit *rlim_stack
#endif
}
-unsigned long get_mmap_base(int is_legacy)
+unsigned long get_mmap_base(struct mm_struct *mm, int is_legacy)
{
- struct mm_struct *mm = current->mm;
-
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
if (in_32bit_syscall()) {
return is_legacy ? mm->mmap_compat_legacy_base
diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c
index dc54f854c2f5..51bfe06746d7 100644
--- a/arch/xtensa/kernel/syscall.c
+++ b/arch/xtensa/kernel/syscall.c
@@ -54,9 +54,9 @@ asmlinkage long xtensa_fadvise64_64(int fd, int advice,
}
#ifdef CONFIG_MMU
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags,
- vm_flags_t vm_flags)
+unsigned long arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags, vm_flags_t vm_flags)
{
struct vm_area_struct *vmm;
struct vma_iterator vmi;
@@ -81,7 +81,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
else
addr = PAGE_ALIGN(addr);
- vma_iter_init(&vmi, current->mm, addr);
+ vma_iter_init(&vmi, mm, addr);
for_each_vma(vmi, vmm) {
/* At this point: (addr < vmm->vm_end). */
if (addr + len <= vm_start_gap(vmm))
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 63253d1de5d7..204bf406173d 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -280,7 +280,8 @@ static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
#endif
#ifndef CONFIG_MMU
-static unsigned long get_unmapped_area_mem(struct file *file,
+static unsigned long get_unmapped_area_mem(struct mm_struct *mm,
+ struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
@@ -515,14 +516,16 @@ static int mmap_zero_prepare(struct vm_area_desc *desc)
}
#ifndef CONFIG_MMU
-static unsigned long get_unmapped_area_zero(struct file *file,
+static unsigned long get_unmapped_area_zero(struct mm_struct *mm,
+ struct file *file,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
return -ENOSYS;
}
#else
-static unsigned long get_unmapped_area_zero(struct file *file,
+static unsigned long get_unmapped_area_zero(struct mm_struct *mm,
+ struct file *file,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
@@ -534,7 +537,7 @@ static unsigned long get_unmapped_area_zero(struct file *file,
* get_unmapped_area(), so as not to confuse shmem with our
* handle on "/dev/zero".
*/
- return shmem_get_unmapped_area(NULL, addr, len, pgoff, flags);
+ return shmem_get_unmapped_area(mm, NULL, addr, len, pgoff, flags);
}
/*
@@ -543,9 +546,9 @@ static unsigned long get_unmapped_area_zero(struct file *file,
* fall back to system page size mappings.
*/
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
- return thp_get_unmapped_area(file, addr, len, pgoff, flags);
+ return thp_get_unmapped_area(mm, file, addr, len, pgoff, flags);
#else
- return mm_get_unmapped_area(file, addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, file, addr, len, pgoff, flags);
#endif
}
#endif /* CONFIG_MMU */
diff --git a/drivers/dax/device.c b/drivers/dax/device.c
index d0c9b4e03b47..80df897b6535 100644
--- a/drivers/dax/device.c
+++ b/drivers/dax/device.c
@@ -295,9 +295,9 @@ static int dax_mmap_prepare(struct vm_area_desc *desc)
}
/* return an unmapped area aligned to the dax region specified alignment */
-static unsigned long dax_get_unmapped_area(struct file *filp,
- unsigned long addr, unsigned long len, unsigned long pgoff,
- unsigned long flags)
+static unsigned long dax_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp, unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags)
{
unsigned long off, off_end, off_align, len_align, addr_align, align;
struct dev_dax *dev_dax = filp ? filp->private_data : NULL;
@@ -317,13 +317,14 @@ static unsigned long dax_get_unmapped_area(struct file *filp,
if ((off + len_align) < off)
goto out;
- addr_align = mm_get_unmapped_area(filp, addr, len_align, pgoff, flags);
+ addr_align = mm_get_unmapped_area(mm, filp, addr, len_align, pgoff,
+ flags);
if (!IS_ERR_VALUE(addr_align)) {
addr_align += (off - addr_align) & (align - 1);
return addr_align;
}
out:
- return mm_get_unmapped_area(filp, addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, filp, addr, len, pgoff, flags);
}
static const struct address_space_operations dev_dax_aops = {
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index e3ed684ddcf2..f3d4f71e61d4 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -1316,6 +1316,7 @@ drm_gem_object_lookup_at_offset(struct file *filp, unsigned long start,
#ifdef CONFIG_MMU
/**
* drm_gem_get_unmapped_area - get memory mapping region routine for GEM objects
+ * @mm: mm_struct the mapping is placed in
* @filp: DRM file pointer
* @uaddr: User address hint
* @len: Mapping length
@@ -1336,7 +1337,8 @@ drm_gem_object_lookup_at_offset(struct file *filp, unsigned long start,
* If a GEM object is not available at the given offset or if the caller is not
* granted access to it, fall back to mm_get_unmapped_area().
*/
-unsigned long drm_gem_get_unmapped_area(struct file *filp, unsigned long uaddr,
+unsigned long drm_gem_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp, unsigned long uaddr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
@@ -1348,9 +1350,10 @@ unsigned long drm_gem_get_unmapped_area(struct file *filp, unsigned long uaddr,
obj = NULL;
if (!obj || !obj->filp || !obj->filp->f_op->get_unmapped_area)
- ret = mm_get_unmapped_area(filp, uaddr, len, 0, flags);
+ ret = mm_get_unmapped_area(mm, filp, uaddr, len, 0, flags);
else
- ret = obj->filp->f_op->get_unmapped_area(obj->filp, uaddr, len, 0, flags);
+ ret = obj->filp->f_op->get_unmapped_area(mm, obj->filp, uaddr,
+ len, 0, flags);
drm_gem_object_put(obj);
diff --git a/drivers/gpu/drm/drm_gem_dma_helper.c b/drivers/gpu/drm/drm_gem_dma_helper.c
index 1c00a71ab3c9..59f517a4f283 100644
--- a/drivers/gpu/drm/drm_gem_dma_helper.c
+++ b/drivers/gpu/drm/drm_gem_dma_helper.c
@@ -330,6 +330,7 @@ EXPORT_SYMBOL_GPL(drm_gem_dma_vm_ops);
#ifndef CONFIG_MMU
/**
* drm_gem_dma_get_unmapped_area - propose address for mapping in noMMU cases
+ * @mm: mm_struct the mapping is placed in
* @filp: file object
* @addr: memory address
* @len: buffer size
@@ -344,7 +345,8 @@ EXPORT_SYMBOL_GPL(drm_gem_dma_vm_ops);
* Returns:
* mapping address on success or a negative error code on failure.
*/
-unsigned long drm_gem_dma_get_unmapped_area(struct file *filp,
+unsigned long drm_gem_dma_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 5516b2bbb08f..1cf7c36fc945 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -373,7 +373,8 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
#ifdef CONFIG_MMU
#define v4l2_get_unmapped_area NULL
#else
-static unsigned long v4l2_get_unmapped_area(struct file *filp,
+static unsigned long v4l2_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp,
unsigned long addr, unsigned long len, unsigned long pgoff,
unsigned long flags)
{
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index bf01e6ac7293..e208a8de8716 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1340,7 +1340,8 @@ static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd,
* mappings)
*/
#ifndef CONFIG_MMU
-static unsigned long mtdchar_get_unmapped_area(struct file *file,
+static unsigned long mtdchar_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
diff --git a/drivers/video/fbdev/core/fb_chrdev.c b/drivers/video/fbdev/core/fb_chrdev.c
index ba1d0bc214c5..cf8590b64b54 100644
--- a/drivers/video/fbdev/core/fb_chrdev.c
+++ b/drivers/video/fbdev/core/fb_chrdev.c
@@ -383,7 +383,8 @@ __releases(&info->lock)
}
#if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU)
-static unsigned long get_fb_unmapped_area(struct file *filp,
+static unsigned long get_fb_unmapped_area(struct mm_struct *mm,
+ struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 4edbfccd0bbe..d3aee6705b8d 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -449,7 +449,8 @@ static int cramfs_physmem_mmap(struct file *file, struct vm_area_struct *vma)
return is_nommu_shared_mapping(vma->vm_flags) ? 0 : -ENOSYS;
}
-static unsigned long cramfs_physmem_get_unmapped_area(struct file *file,
+static unsigned long cramfs_physmem_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 216e1a0dd0b2..f5d3c182cc7c 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -170,9 +170,9 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
*/
unsigned long
-hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags)
+hugetlb_get_unmapped_area(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags)
{
unsigned long addr0 = 0;
struct hstate *h = hstate_file(file);
@@ -184,7 +184,8 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
if (addr)
addr0 = ALIGN(addr, huge_page_size(h));
- return mm_get_unmapped_area_vmflags(file, addr0, len, pgoff, flags, 0);
+ return mm_get_unmapped_area_vmflags(mm, file, addr0, len, pgoff,
+ flags, 0);
}
/*
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index b7634f975d98..3e542fe3118d 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -435,22 +435,25 @@ static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma)
}
static unsigned long
-pde_get_unmapped_area(struct proc_dir_entry *pde, struct file *file, unsigned long orig_addr,
+pde_get_unmapped_area(struct proc_dir_entry *pde, struct mm_struct *mm,
+ struct file *file, unsigned long orig_addr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
if (pde->proc_ops->proc_get_unmapped_area)
- return pde->proc_ops->proc_get_unmapped_area(file, orig_addr, len, pgoff, flags);
+ return pde->proc_ops->proc_get_unmapped_area(mm, file,
+ orig_addr, len, pgoff, flags);
#ifdef CONFIG_MMU
- return mm_get_unmapped_area(file, orig_addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, file, orig_addr, len, pgoff, flags);
#endif
return orig_addr;
}
static unsigned long
-proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr,
+proc_reg_get_unmapped_area(struct mm_struct *mm, struct file *file,
+ unsigned long orig_addr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
@@ -458,9 +461,9 @@ proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr,
unsigned long rv = -EIO;
if (pde_is_permanent(pde)) {
- return pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags);
+ return pde_get_unmapped_area(pde, mm, file, orig_addr, len, pgoff, flags);
} else if (use_pde(pde)) {
- rv = pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags);
+ rv = pde_get_unmapped_area(pde, mm, file, orig_addr, len, pgoff, flags);
unuse_pde(pde);
}
return rv;
diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c
index c3ed1c5117b2..e070ca06499b 100644
--- a/fs/ramfs/file-mmu.c
+++ b/fs/ramfs/file-mmu.c
@@ -31,11 +31,12 @@
#include "internal.h"
-static unsigned long ramfs_mmu_get_unmapped_area(struct file *file,
+static unsigned long ramfs_mmu_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long addr, unsigned long len, unsigned long pgoff,
unsigned long flags)
{
- return mm_get_unmapped_area(file, addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, file, addr, len, pgoff, flags);
}
const struct file_operations ramfs_file_operations = {
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
index 2f79bcb89d2e..2e20fc9c0d37 100644
--- a/fs/ramfs/file-nommu.c
+++ b/fs/ramfs/file-nommu.c
@@ -23,7 +23,8 @@
#include "internal.h"
static int ramfs_nommu_setattr(struct mnt_idmap *, struct dentry *, struct iattr *);
-static unsigned long ramfs_nommu_get_unmapped_area(struct file *file,
+static unsigned long ramfs_nommu_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
@@ -199,7 +200,8 @@ static int ramfs_nommu_setattr(struct mnt_idmap *idmap,
* - the pages to be mapped must exist
* - the pages be physically contiguous in sequence
*/
-static unsigned long ramfs_nommu_get_unmapped_area(struct file *file,
+static unsigned long ramfs_nommu_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
diff --git a/fs/romfs/mmap-nommu.c b/fs/romfs/mmap-nommu.c
index 7c3a1a7fecee..18c1e8545b0d 100644
--- a/fs/romfs/mmap-nommu.c
+++ b/fs/romfs/mmap-nommu.c
@@ -15,7 +15,8 @@
* mappings)
* - attempts to map through to the underlying MTD device
*/
-static unsigned long romfs_get_unmapped_area(struct file *file,
+static unsigned long romfs_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
index 8a704f6a65c1..a99d525f0369 100644
--- a/include/drm/drm_gem.h
+++ b/include/drm/drm_gem.h
@@ -536,7 +536,8 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size,
int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
#ifdef CONFIG_MMU
-unsigned long drm_gem_get_unmapped_area(struct file *filp, unsigned long uaddr,
+unsigned long drm_gem_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp, unsigned long uaddr,
unsigned long len, unsigned long pgoff,
unsigned long flags);
#else
diff --git a/include/drm/drm_gem_dma_helper.h b/include/drm/drm_gem_dma_helper.h
index f2678e7ecb98..127f52f192a9 100644
--- a/include/drm/drm_gem_dma_helper.h
+++ b/include/drm/drm_gem_dma_helper.h
@@ -232,7 +232,8 @@ drm_gem_dma_prime_import_sg_table_vmap(struct drm_device *drm,
*/
#ifndef CONFIG_MMU
-unsigned long drm_gem_dma_get_unmapped_area(struct file *filp,
+unsigned long drm_gem_dma_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 7719f6528445..d72a5ba49654 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -147,7 +147,8 @@ struct bpf_map_ops {
int (*map_mmap)(struct bpf_map *map, struct vm_area_struct *vma);
__poll_t (*map_poll)(struct bpf_map *map, struct file *filp,
struct poll_table_struct *pts);
- unsigned long (*map_get_unmapped_area)(struct file *filep, unsigned long addr,
+ unsigned long (*map_get_unmapped_area)(struct mm_struct *mm,
+ struct file *filep, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 50ce731a2b78..58e00b320efd 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1939,7 +1939,10 @@ struct file_operations {
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
- unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+ unsigned long (*get_unmapped_area)(struct mm_struct *mm,
+ struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff,
+ unsigned long flags);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index ad20f7f8c179..5760710c3c5d 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -388,11 +388,12 @@ static inline bool thp_disabled_by_hw(void)
return transparent_hugepage_flags & (1 << TRANSPARENT_HUGEPAGE_UNSUPPORTED);
}
-unsigned long thp_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags);
-unsigned long thp_get_unmapped_area_vmflags(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags,
- vm_flags_t vm_flags);
+unsigned long thp_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags);
+unsigned long thp_get_unmapped_area_vmflags(struct mm_struct *mm,
+ struct file *filp, unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags);
enum split_type {
SPLIT_TYPE_UNIFORM,
@@ -614,9 +615,10 @@ static inline unsigned long thp_vma_allowable_orders(struct vm_area_struct *vma,
#define thp_get_unmapped_area NULL
static inline unsigned long
-thp_get_unmapped_area_vmflags(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+thp_get_unmapped_area_vmflags(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
return 0;
}
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 2abaf99321e9..b2743931f927 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -543,9 +543,9 @@ static inline struct hstate *hstate_inode(struct inode *i)
#endif /* !CONFIG_HUGETLBFS */
unsigned long
-hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags);
+hugetlb_get_unmapped_area(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags);
/*
* huegtlb page specific state flags. These flags are located in page.private
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 485df9c2dbdd..560bc369a54c 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -4138,14 +4138,17 @@ unsigned long randomize_stack_top(unsigned long stack_top);
unsigned long randomize_page(unsigned long start, unsigned long range);
unsigned long
-__get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
- unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags);
+__get_unmapped_area(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags);
static inline unsigned long
get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
- return __get_unmapped_area(file, addr, len, pgoff, flags, 0);
+ return __get_unmapped_area(current->mm, file, addr, len, pgoff,
+ flags, 0);
}
extern unsigned long do_mmap(struct file *file, unsigned long addr,
@@ -4194,7 +4197,8 @@ struct vm_unmapped_area_info {
unsigned long start_gap;
};
-extern unsigned long vm_unmapped_area(struct vm_unmapped_area_info *info);
+extern unsigned long vm_unmapped_area(struct mm_struct *mm,
+ struct vm_unmapped_area_info *info);
/* truncate.c */
void truncate_inode_pages(struct address_space *mapping, loff_t lstart);
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 47d7deaeed8f..e7998a303519 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -47,7 +47,10 @@ struct proc_ops {
long (*proc_compat_ioctl)(struct file *, unsigned int, unsigned long);
#endif
int (*proc_mmap)(struct file *, struct vm_area_struct *);
- unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+ unsigned long (*proc_get_unmapped_area)(struct mm_struct *mm,
+ struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff,
+ unsigned long flags);
} __randomize_layout;
/* definitions for hide_pid field */
diff --git a/include/linux/sched/mm.h b/include/linux/sched/mm.h
index 95d0040df584..adc11009fedf 100644
--- a/include/linux/sched/mm.h
+++ b/include/linux/sched/mm.h
@@ -181,19 +181,22 @@ extern void arch_pick_mmap_layout(struct mm_struct *mm,
const struct rlimit *rlim_stack);
unsigned long
-arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags);
+arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags);
unsigned long
-arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t);
+arch_get_unmapped_area_topdown(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t);
-unsigned long mm_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags);
+unsigned long mm_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags);
-unsigned long mm_get_unmapped_area_vmflags(struct file *filp,
+unsigned long mm_get_unmapped_area_vmflags(struct mm_struct *mm,
+ struct file *filp,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
@@ -201,13 +204,15 @@ unsigned long mm_get_unmapped_area_vmflags(struct file *filp,
vm_flags_t vm_flags);
unsigned long
-generic_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags);
+generic_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags);
unsigned long
-generic_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags);
+generic_get_unmapped_area_topdown(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags);
#else
static inline void arch_pick_mmap_layout(struct mm_struct *mm,
const struct rlimit *rlim_stack) {}
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index e729b9b0e38d..3912836b375f 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -109,8 +109,9 @@ extern struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt,
const char *name, loff_t size, vma_flags_t flags);
int shmem_zero_setup(struct vm_area_struct *vma);
int shmem_zero_setup_desc(struct vm_area_desc *desc);
-extern unsigned long shmem_get_unmapped_area(struct file *, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags);
+extern unsigned long shmem_get_unmapped_area(struct mm_struct *mm,
+ struct file *file, unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags);
extern int shmem_lock(struct file *file, int lock, struct ucounts *ucounts);
#ifdef CONFIG_SHMEM
bool shmem_mapping(const struct address_space *mapping);
diff --git a/io_uring/memmap.c b/io_uring/memmap.c
index 23e8a85111bc..255fd80fd2f2 100644
--- a/io_uring/memmap.c
+++ b/io_uring/memmap.c
@@ -318,7 +318,8 @@ __cold int io_uring_mmap(struct file *file, struct vm_area_struct *vma)
return io_region_mmap(ctx, region, vma, page_limit);
}
-unsigned long io_uring_get_unmapped_area(struct file *filp, unsigned long addr,
+unsigned long io_uring_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
@@ -361,7 +362,7 @@ unsigned long io_uring_get_unmapped_area(struct file *filp, unsigned long addr,
#else
addr = 0UL;
#endif
- return mm_get_unmapped_area(filp, addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, filp, addr, len, pgoff, flags);
}
#else /* !CONFIG_MMU */
@@ -420,7 +421,8 @@ unsigned int io_uring_nommu_mmap_capabilities(struct file *file)
return NOMMU_MAP_DIRECT | NOMMU_MAP_READ | NOMMU_MAP_WRITE;
}
-unsigned long io_uring_get_unmapped_area(struct file *file, unsigned long addr,
+unsigned long io_uring_get_unmapped_area(struct mm_struct *mm,
+ struct file *file, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
diff --git a/io_uring/memmap.h b/io_uring/memmap.h
index f4cfbb6b9a1f..40104ed26dbe 100644
--- a/io_uring/memmap.h
+++ b/io_uring/memmap.h
@@ -12,7 +12,8 @@ struct page **io_pin_pages(unsigned long uaddr, unsigned long len, int *npages);
#ifndef CONFIG_MMU
unsigned int io_uring_nommu_mmap_capabilities(struct file *file);
#endif
-unsigned long io_uring_get_unmapped_area(struct file *file, unsigned long addr,
+unsigned long io_uring_get_unmapped_area(struct mm_struct *mm,
+ struct file *file, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags);
int io_uring_mmap(struct file *file, struct vm_area_struct *vma);
diff --git a/ipc/shm.c b/ipc/shm.c
index b3e8a58e177d..c49e1461ff9c 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -649,13 +649,13 @@ static long shm_fallocate(struct file *file, int mode, loff_t offset,
return sfd->file->f_op->fallocate(file, mode, offset, len);
}
-static unsigned long shm_get_unmapped_area(struct file *file,
- unsigned long addr, unsigned long len, unsigned long pgoff,
- unsigned long flags)
+static unsigned long shm_get_unmapped_area(struct mm_struct *mm,
+ struct file *file, unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags)
{
struct shm_file_data *sfd = shm_file_data(file);
- return sfd->file->f_op->get_unmapped_area(sfd->file, addr, len,
+ return sfd->file->f_op->get_unmapped_area(mm, sfd->file, addr, len,
pgoff, flags);
}
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 80b7b8a69446..839ebaae301d 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -543,7 +543,8 @@ static const struct vm_operations_struct arena_vm_ops = {
.fault = arena_vm_fault,
};
-static unsigned long arena_get_unmapped_area(struct file *filp, unsigned long addr,
+static unsigned long arena_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
@@ -566,7 +567,7 @@ static unsigned long arena_get_unmapped_area(struct file *filp, unsigned long ad
return -EINVAL;
}
- ret = mm_get_unmapped_area(filp, addr, len * 2, 0, flags);
+ ret = mm_get_unmapped_area(mm, filp, addr, len * 2, 0, flags);
if (IS_ERR_VALUE(ret))
return ret;
if ((ret >> 32) == ((ret + len - 1) >> 32))
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 6db306d23b47..5092fc8707c5 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1150,16 +1150,18 @@ static __poll_t bpf_map_poll(struct file *filp, struct poll_table_struct *pts)
return EPOLLERR;
}
-static unsigned long bpf_get_unmapped_area(struct file *filp, unsigned long addr,
+static unsigned long bpf_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags)
{
struct bpf_map *map = filp->private_data;
if (map->ops->map_get_unmapped_area)
- return map->ops->map_get_unmapped_area(filp, addr, len, pgoff, flags);
+ return map->ops->map_get_unmapped_area(mm, filp, addr, len,
+ pgoff, flags);
#ifdef CONFIG_MMU
- return mm_get_unmapped_area(filp, addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, filp, addr, len, pgoff, flags);
#else
return addr;
#endif
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 2bccb0a53a0a..708faf4a0e37 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1193,8 +1193,8 @@ static inline bool is_transparent_hugepage(const struct folio *folio)
folio_test_large_rmappable(folio);
}
-static unsigned long __thp_get_unmapped_area(struct file *filp,
- unsigned long addr, unsigned long len,
+static unsigned long __thp_get_unmapped_area(struct mm_struct *mm,
+ struct file *filp, unsigned long addr, unsigned long len,
loff_t off, unsigned long flags, unsigned long size,
vm_flags_t vm_flags)
{
@@ -1212,7 +1212,7 @@ static unsigned long __thp_get_unmapped_area(struct file *filp,
if (len_pad < len || (off + len_pad) < off)
return 0;
- ret = mm_get_unmapped_area_vmflags(filp, addr, len_pad,
+ ret = mm_get_unmapped_area_vmflags(mm, filp, addr, len_pad,
off >> PAGE_SHIFT, flags, vm_flags);
/*
@@ -1231,32 +1231,35 @@ static unsigned long __thp_get_unmapped_area(struct file *filp,
off_sub = (off - ret) & (size - 1);
- if (mm_flags_test(MMF_TOPDOWN, current->mm) && !off_sub)
+ if (mm_flags_test(MMF_TOPDOWN, mm) && !off_sub)
return ret + size;
ret += off_sub;
return ret;
}
-unsigned long thp_get_unmapped_area_vmflags(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags,
- vm_flags_t vm_flags)
+unsigned long thp_get_unmapped_area_vmflags(struct mm_struct *mm,
+ struct file *filp, unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags)
{
unsigned long ret;
loff_t off = (loff_t)pgoff << PAGE_SHIFT;
- ret = __thp_get_unmapped_area(filp, addr, len, off, flags, PMD_SIZE, vm_flags);
+ ret = __thp_get_unmapped_area(mm, filp, addr, len, off, flags,
+ PMD_SIZE, vm_flags);
if (ret)
return ret;
- return mm_get_unmapped_area_vmflags(filp, addr, len, pgoff, flags,
+ return mm_get_unmapped_area_vmflags(mm, filp, addr, len, pgoff, flags,
vm_flags);
}
-unsigned long thp_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags)
+unsigned long thp_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags)
{
- return thp_get_unmapped_area_vmflags(filp, addr, len, pgoff, flags, 0);
+ return thp_get_unmapped_area_vmflags(mm, filp, addr, len, pgoff,
+ flags, 0);
}
EXPORT_SYMBOL_GPL(thp_get_unmapped_area);
diff --git a/mm/mmap.c b/mm/mmap.c
index 2311ae7c2ff4..54915ac478ab 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -405,7 +405,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
/* Obtain the address to map to. we verify (or select) it and ensure
* that it represents a valid section of the address space.
*/
- addr = __get_unmapped_area(file, addr, len, pgoff, flags, vm_flags);
+ addr = __get_unmapped_area(mm, file, addr, len, pgoff, flags, vm_flags);
if (IS_ERR_VALUE(addr))
return addr;
@@ -662,14 +662,15 @@ static inline unsigned long stack_guard_placement(vm_flags_t vm_flags)
* - is at least the desired size.
* - satisfies (begin_addr & align_mask) == (align_offset & align_mask)
*/
-unsigned long vm_unmapped_area(struct vm_unmapped_area_info *info)
+unsigned long vm_unmapped_area(struct mm_struct *mm,
+ struct vm_unmapped_area_info *info)
{
unsigned long addr;
if (info->flags & VM_UNMAPPED_AREA_TOPDOWN)
- addr = unmapped_area_topdown(info);
+ addr = unmapped_area_topdown(mm, info);
else
- addr = unmapped_area(info);
+ addr = unmapped_area(mm, info);
trace_vm_unmapped_area(addr, info);
return addr;
@@ -687,11 +688,11 @@ unsigned long vm_unmapped_area(struct vm_unmapped_area_info *info)
* This function "knows" that -ENOMEM has the bits set.
*/
unsigned long
-generic_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+generic_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma, *prev;
struct vm_unmapped_area_info info = {};
const unsigned long mmap_end = arch_get_mmap_end(addr, len, flags);
@@ -717,16 +718,17 @@ generic_get_unmapped_area(struct file *filp, unsigned long addr,
info.start_gap = stack_guard_placement(vm_flags);
if (filp && is_file_hugepages(filp))
info.align_mask = huge_page_mask_align(filp);
- return vm_unmapped_area(&info);
+ return vm_unmapped_area(mm, &info);
}
#ifndef HAVE_ARCH_UNMAPPED_AREA
unsigned long
-arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+arch_get_unmapped_area(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
- return generic_get_unmapped_area(filp, addr, len, pgoff, flags,
+ return generic_get_unmapped_area(mm, filp, addr, len, pgoff, flags,
vm_flags);
}
#endif
@@ -736,12 +738,12 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
* stack's low limit (the base):
*/
unsigned long
-generic_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+generic_get_unmapped_area_topdown(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
struct vm_area_struct *vma, *prev;
- struct mm_struct *mm = current->mm;
struct vm_unmapped_area_info info = {};
const unsigned long mmap_end = arch_get_mmap_end(addr, len, flags);
@@ -769,7 +771,7 @@ generic_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
info.start_gap = stack_guard_placement(vm_flags);
if (filp && is_file_hugepages(filp))
info.align_mask = huge_page_mask_align(filp);
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
/*
* A failed mmap() very likely causes application failure,
@@ -782,7 +784,7 @@ generic_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
info.flags = 0;
info.low_limit = TASK_UNMAPPED_BASE;
info.high_limit = mmap_end;
- addr = vm_unmapped_area(&info);
+ addr = vm_unmapped_area(mm, &info);
}
return addr;
@@ -790,31 +792,36 @@ generic_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
#ifndef HAVE_ARCH_UNMAPPED_AREA_TOPDOWN
unsigned long
-arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff,
- unsigned long flags, vm_flags_t vm_flags)
+arch_get_unmapped_area_topdown(struct mm_struct *mm, struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags,
+ vm_flags_t vm_flags)
{
- return generic_get_unmapped_area_topdown(filp, addr, len, pgoff, flags,
- vm_flags);
+ return generic_get_unmapped_area_topdown(mm, filp, addr, len, pgoff,
+ flags, vm_flags);
}
#endif
-unsigned long mm_get_unmapped_area_vmflags(struct file *filp, unsigned long addr,
+unsigned long mm_get_unmapped_area_vmflags(struct mm_struct *mm,
+ struct file *filp, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags, vm_flags_t vm_flags)
{
- if (mm_flags_test(MMF_TOPDOWN, current->mm))
- return arch_get_unmapped_area_topdown(filp, addr, len, pgoff,
- flags, vm_flags);
- return arch_get_unmapped_area(filp, addr, len, pgoff, flags, vm_flags);
+ if (mm_flags_test(MMF_TOPDOWN, mm))
+ return arch_get_unmapped_area_topdown(mm, filp, addr, len,
+ pgoff, flags, vm_flags);
+ return arch_get_unmapped_area(mm, filp, addr, len, pgoff, flags,
+ vm_flags);
}
unsigned long
-__get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
+__get_unmapped_area(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags, vm_flags_t vm_flags)
{
- unsigned long (*get_area)(struct file *, unsigned long,
- unsigned long, unsigned long, unsigned long)
+ unsigned long (*get_area)(struct mm_struct *, struct file *,
+ unsigned long, unsigned long,
+ unsigned long, unsigned long)
= NULL;
unsigned long error = arch_mmap_check(addr, len, flags);
@@ -841,15 +848,15 @@ __get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
pgoff = 0;
if (get_area) {
- addr = get_area(file, addr, len, pgoff, flags);
+ addr = get_area(mm, file, addr, len, pgoff, flags);
} else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && !file
&& !addr /* no hint */
&& IS_ALIGNED(len, PMD_SIZE)) {
/* Ensures that larger anonymous mappings are THP aligned. */
- addr = thp_get_unmapped_area_vmflags(file, addr, len,
+ addr = thp_get_unmapped_area_vmflags(mm, file, addr, len,
pgoff, flags, vm_flags);
} else {
- addr = mm_get_unmapped_area_vmflags(file, addr, len,
+ addr = mm_get_unmapped_area_vmflags(mm, file, addr, len,
pgoff, flags, vm_flags);
}
if (IS_ERR_VALUE(addr))
@@ -865,10 +872,12 @@ __get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
}
unsigned long
-mm_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
+mm_get_unmapped_area(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
- return mm_get_unmapped_area_vmflags(file, addr, len, pgoff, flags, 0);
+ return mm_get_unmapped_area_vmflags(mm, file, addr, len, pgoff,
+ flags, 0);
}
EXPORT_SYMBOL(mm_get_unmapped_area);
diff --git a/mm/nommu.c b/mm/nommu.c
index ed3934bc2de4..d6393bd2844e 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1145,8 +1145,9 @@ unsigned long do_mmap(struct file *file,
* tell us the location of a shared mapping
*/
if (capabilities & NOMMU_MAP_DIRECT) {
- addr = file->f_op->get_unmapped_area(file, addr, len,
- pgoff, flags);
+ addr = file->f_op->get_unmapped_area(current->mm, file,
+ addr, len, pgoff,
+ flags);
if (IS_ERR_VALUE(addr)) {
ret = addr;
if (ret != -ENOSYS)
diff --git a/mm/shmem.c b/mm/shmem.c
index b51f83c970bb..1349e00bc7fd 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2711,7 +2711,8 @@ static vm_fault_t shmem_fault(struct vm_fault *vmf)
return ret;
}
-unsigned long shmem_get_unmapped_area(struct file *file,
+unsigned long shmem_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long uaddr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
@@ -2725,7 +2726,7 @@ unsigned long shmem_get_unmapped_area(struct file *file,
if (len > TASK_SIZE)
return -ENOMEM;
- addr = mm_get_unmapped_area(file, uaddr, len, pgoff, flags);
+ addr = mm_get_unmapped_area(mm, file, uaddr, len, pgoff, flags);
if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
return addr;
@@ -2803,7 +2804,8 @@ unsigned long shmem_get_unmapped_area(struct file *file,
if (inflated_len < len)
return addr;
- inflated_addr = mm_get_unmapped_area(NULL, uaddr, inflated_len, 0, flags);
+ inflated_addr = mm_get_unmapped_area(mm, NULL, uaddr, inflated_len, 0,
+ flags);
if (IS_ERR_VALUE(inflated_addr))
return addr;
if (inflated_addr & ~PAGE_MASK)
@@ -5730,11 +5732,12 @@ void shmem_unlock_mapping(struct address_space *mapping)
}
#ifdef CONFIG_MMU
-unsigned long shmem_get_unmapped_area(struct file *file,
+unsigned long shmem_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
- return mm_get_unmapped_area(file, addr, len, pgoff, flags);
+ return mm_get_unmapped_area(mm, file, addr, len, pgoff, flags);
}
#endif
diff --git a/mm/vma.c b/mm/vma.c
index 9eea2850818a..9a6cb0338ed8 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -2957,20 +2957,22 @@ int do_brk_flags(struct vma_iterator *vmi, struct vm_area_struct *vma,
/**
* unmapped_area() - Find an area between the low_limit and the high_limit with
- * the correct alignment and offset, all from @info. Note: current->mm is used
+ * the correct alignment and offset, all from @info. Note: @mm is used
* for the search.
*
+ * @mm: The mm_struct to search.
* @info: The unmapped area information including the range [low_limit -
* high_limit), the alignment offset and mask.
*
* Return: A memory address or -ENOMEM.
*/
-unsigned long unmapped_area(struct vm_unmapped_area_info *info)
+unsigned long unmapped_area(struct mm_struct *mm,
+ struct vm_unmapped_area_info *info)
{
unsigned long length, gap;
unsigned long low_limit, high_limit;
struct vm_area_struct *tmp;
- VMA_ITERATOR(vmi, current->mm, 0);
+ VMA_ITERATOR(vmi, mm, 0);
/* Adjust search length to account for worst case alignment overhead */
length = info->length + info->align_mask + info->start_gap;
@@ -3016,19 +3018,21 @@ unsigned long unmapped_area(struct vm_unmapped_area_info *info)
/**
* unmapped_area_topdown() - Find an area between the low_limit and the
* high_limit with the correct alignment and offset at the highest available
- * address, all from @info. Note: current->mm is used for the search.
+ * address, all from @info. Note: @mm is used for the search.
*
+ * @mm: The mm_struct to search.
* @info: The unmapped area information including the range [low_limit -
* high_limit), the alignment offset and mask.
*
* Return: A memory address or -ENOMEM.
*/
-unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info)
+unsigned long unmapped_area_topdown(struct mm_struct *mm,
+ struct vm_unmapped_area_info *info)
{
unsigned long length, gap, gap_end;
unsigned long low_limit, high_limit;
struct vm_area_struct *tmp;
- VMA_ITERATOR(vmi, current->mm, 0);
+ VMA_ITERATOR(vmi, mm, 0);
/* Adjust search length to account for worst case alignment overhead */
length = info->length + info->align_mask + info->start_gap;
diff --git a/mm/vma.h b/mm/vma.h
index 8e4b61a7304c..d0a45718deb9 100644
--- a/mm/vma.h
+++ b/mm/vma.h
@@ -467,8 +467,10 @@ int do_brk_flags(struct vma_iterator *vmi, struct vm_area_struct *brkvma,
unsigned long addr, unsigned long request,
vma_flags_t vma_flags);
-unsigned long unmapped_area(struct vm_unmapped_area_info *info);
-unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info);
+unsigned long unmapped_area(struct mm_struct *mm,
+ struct vm_unmapped_area_info *info);
+unsigned long unmapped_area_topdown(struct mm_struct *mm,
+ struct vm_unmapped_area_info *info);
static inline bool vma_wants_manual_pte_write_upgrade(struct vm_area_struct *vma)
{
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 7dc0060617f1..f01465a70ddd 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -4202,7 +4202,8 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
#endif /* CONFIG_SND_SUPPORT_OLD_API */
#ifndef CONFIG_MMU
-static unsigned long snd_pcm_get_unmapped_area(struct file *file,
+static unsigned long snd_pcm_get_unmapped_area(struct mm_struct *mm,
+ struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v6 2/8] mm: add __do_mmap() and vm_mmap_remote()/vm_munmap_remote()
2026-07-15 21:19 [PATCH v6 0/8] seccomp: non-cooperative pinned-memfd argument redirect Cong Wang
2026-07-15 21:20 ` [PATCH v6 1/8] mm: pass the target mm parameter through get_unmapped_area family Cong Wang
@ 2026-07-15 21:20 ` Cong Wang
2026-07-15 21:20 ` [PATCH v6 3/8] seccomp: introduce SECCOMP_IOCTL_NOTIF_PIN_INSTALL Cong Wang
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Cong Wang @ 2026-07-15 21:20 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Kees Cook, linux-kernel, Will Drewry, Christian Brauner,
Andrew Morton, linux-mm, Cong Wang
From: Cong Wang <cwang@multikernel.io>
Add __do_mmap(), a variant of do_mmap() that installs the mapping into
a caller-supplied mm rather than current->mm; do_mmap() becomes a
wrapper passing current->mm and keeps the current-task READ_IMPLIES_EXEC
handling. mmap_region()/__mmap_region() gain an mm argument so the
target mm reaches VMA insertion.
On top of that, add vm_mmap_remote() and vm_munmap_remote() in mm/util.c:
entry points that install / remove a mapping in a caller-specified mm
without target-side cooperation. The intended user is the seccomp
unotify syscall redirect.
Kernel-chosen placement resolves through __get_unmapped_area() against
the target mm, so (via the previous patch) the file's own
->get_unmapped_area() runs and file-specific placement is honored. The
result is bounds-checked against the target's mm->task_size, and the
munmap machinery is likewise threaded with the target mm so counter
bookkeeping and range checks apply to it rather than current->mm.
vm_munmap_remote() delivers the target's UFFD_EVENT_UNMAP.
Both are MMU-only and guarded with CONFIG_MMU, with -EOPNOTSUPP stubs so
NOMMU keeps linking. LSM/fsnotify hooks run against current, the
supervisor, paralleling pidfd_getfd(); cross-task authorization is left
to the caller.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang@multikernel.io>
---
include/linux/mm.h | 19 ++++++++
mm/internal.h | 6 +++
mm/mmap.c | 49 +++++++++++++--------
mm/mprotect.c | 2 +-
mm/nommu.c | 22 +++++++---
mm/util.c | 106 +++++++++++++++++++++++++++++++++++++++++++++
mm/vma.c | 46 ++++++++++++--------
mm/vma.h | 12 ++---
8 files changed, 212 insertions(+), 50 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 560bc369a54c..fa1d96bc379d 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -4155,6 +4155,25 @@ extern unsigned long do_mmap(struct file *file, unsigned long addr,
unsigned long len, unsigned long prot, unsigned long flags,
vm_flags_t vm_flags, unsigned long pgoff, unsigned long *populate,
struct list_head *uf);
+#ifdef CONFIG_MMU
+unsigned long vm_mmap_remote(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long pgoff, vm_flags_t vm_flags);
+int vm_munmap_remote(struct mm_struct *mm, unsigned long start, size_t len);
+#else
+static inline unsigned long vm_mmap_remote(struct mm_struct *mm,
+ struct file *file, unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags, unsigned long pgoff,
+ vm_flags_t vm_flags)
+{
+ return -EOPNOTSUPP;
+}
+static inline int vm_munmap_remote(struct mm_struct *mm, unsigned long start,
+ size_t len)
+{
+ return -EOPNOTSUPP;
+}
+#endif
extern int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm,
unsigned long start, size_t len, struct list_head *uf,
bool unlock);
diff --git a/mm/internal.h b/mm/internal.h
index 181e79f1d6a2..fd3d39f6b4c4 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1436,6 +1436,12 @@ extern unsigned long __must_check vm_mmap_pgoff(struct file *, unsigned long,
unsigned long, unsigned long,
unsigned long, unsigned long);
+unsigned long __do_mmap(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, vm_flags_t vm_flags, unsigned long pgoff,
+ unsigned long *populate, struct list_head *uf);
+
+
extern void set_pageblock_order(void);
unsigned long reclaim_pages(struct list_head *folio_list);
unsigned int reclaim_clean_pages_from_list(struct zone *zone,
diff --git a/mm/mmap.c b/mm/mmap.c
index 54915ac478ab..f379a065a8e6 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -277,7 +277,7 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode,
}
/**
- * do_mmap() - Perform a userland memory mapping into the current process
+ * __do_mmap() - Perform a userland memory mapping into @mm's
* address space of length @len with protection bits @prot, mmap flags @flags
* (from which VMA flags will be inferred), and any additional VMA flags to
* apply @vm_flags. If this is a file-backed mapping then the file is specified
@@ -307,8 +307,11 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode,
* start of a VMA, rather only the start of a valid mapped range of length
* @len bytes, rounded down to the nearest page size.
*
- * The caller must write-lock current->mm->mmap_lock.
+ * The caller must write-lock @mm->mmap_lock. do_mmap() is the common
+ * wrapper that targets current->mm.
*
+ * @mm: The mm_struct to install the mapping into. The caller must hold a
+ * reference and write-lock its mmap_lock.
* @file: An optional struct file pointer describing the file which is to be
* mapped, if a file-backed mapping.
* @addr: If non-zero, hints at (or if @flags has MAP_FIXED set, specifies) the
@@ -333,13 +336,12 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode,
* Returns: Either an error, or the address at which the requested mapping has
* been performed.
*/
-unsigned long do_mmap(struct file *file, unsigned long addr,
- unsigned long len, unsigned long prot,
- unsigned long flags, vm_flags_t vm_flags,
- unsigned long pgoff, unsigned long *populate,
- struct list_head *uf)
+unsigned long __do_mmap(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ vm_flags_t vm_flags, unsigned long pgoff,
+ unsigned long *populate, struct list_head *uf)
{
- struct mm_struct *mm = current->mm;
int pkey = 0;
*populate = 0;
@@ -349,16 +351,6 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
if (!len)
return -EINVAL;
- /*
- * Does the application expect PROT_READ to imply PROT_EXEC?
- *
- * (the exception is when the underlying filesystem is noexec
- * mounted, in which case we don't add PROT_EXEC.)
- */
- if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
- if (!(file && path_noexec(&file->f_path)))
- prot |= PROT_EXEC;
-
/* force arch specific MAP_FIXED handling in get_unmapped_area */
if (flags & MAP_FIXED_NOREPLACE)
flags |= MAP_FIXED;
@@ -557,7 +549,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
vm_flags |= VM_NORESERVE;
}
- addr = mmap_region(file, addr, len, vm_flags, pgoff, uf);
+ addr = mmap_region(mm, file, addr, len, vm_flags, pgoff, uf);
if (!IS_ERR_VALUE(addr) &&
((vm_flags & VM_LOCKED) ||
(flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE))
@@ -565,6 +557,25 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
return addr;
}
+unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ vm_flags_t vm_flags, unsigned long pgoff,
+ unsigned long *populate, struct list_head *uf)
+{
+ /*
+ * Does the application expect PROT_READ to imply PROT_EXEC?
+ *
+ * (the exception is when the underlying filesystem is noexec
+ * mounted, in which case we don't add PROT_EXEC.)
+ */
+ if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
+ if (!(file && path_noexec(&file->f_path)))
+ prot |= PROT_EXEC;
+
+ return __do_mmap(current->mm, file, addr, len, prot, flags, vm_flags,
+ pgoff, populate, uf);
+}
+
unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 9cbf932b028c..02e90577db25 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -939,7 +939,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
break;
}
- if (map_deny_write_exec(&vma->flags, &new_vma_flags)) {
+ if (map_deny_write_exec(vma->vm_mm, &vma->flags, &new_vma_flags)) {
error = -EACCES;
break;
}
diff --git a/mm/nommu.c b/mm/nommu.c
index d6393bd2844e..15390ef5b784 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1009,7 +1009,8 @@ static int do_mmap_private(struct vm_area_struct *vma,
/*
* handle mapping creation for uClinux
*/
-unsigned long do_mmap(struct file *file,
+unsigned long __do_mmap(struct mm_struct *mm,
+ struct file *file,
unsigned long addr,
unsigned long len,
unsigned long prot,
@@ -1024,7 +1025,7 @@ unsigned long do_mmap(struct file *file,
struct rb_node *rb;
unsigned long capabilities, result;
int ret;
- VMA_ITERATOR(vmi, current->mm, 0);
+ VMA_ITERATOR(vmi, mm, 0);
*populate = 0;
@@ -1049,7 +1050,7 @@ unsigned long do_mmap(struct file *file,
if (!region)
goto error_getting_region;
- vma = vm_area_alloc(current->mm);
+ vma = vm_area_alloc(mm);
if (!vma)
goto error_getting_vma;
@@ -1191,7 +1192,7 @@ unsigned long do_mmap(struct file *file,
/* okay... we have a mapping; now we have to register it */
result = vma->vm_start;
- current->mm->total_vm += len >> PAGE_SHIFT;
+ mm->total_vm += len >> PAGE_SHIFT;
share:
BUG_ON(!vma->vm_region);
@@ -1199,8 +1200,8 @@ unsigned long do_mmap(struct file *file,
if (vma_iter_prealloc(&vmi, vma))
goto error_just_free;
- setup_vma_to_mm(vma, current->mm);
- current->mm->map_count++;
+ setup_vma_to_mm(vma, mm);
+ mm->map_count++;
/* add the VMA to the tree */
vma_iter_store_new(&vmi, vma);
@@ -1247,6 +1248,15 @@ unsigned long do_mmap(struct file *file,
return -ENOMEM;
}
+unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ vm_flags_t vm_flags, unsigned long pgoff,
+ unsigned long *populate, struct list_head *uf)
+{
+ return __do_mmap(current->mm, file, addr, len, prot, flags, vm_flags, pgoff,
+ populate, uf);
+}
+
unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
diff --git a/mm/util.c b/mm/util.c
index af2c2103f0d9..2b5254106d15 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -588,6 +588,112 @@ unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr,
return ret;
}
+#ifdef CONFIG_MMU
+/**
+ * vm_mmap_remote - install a file (or anonymous) mapping into @mm, without
+ * target-side cooperation.
+ * @mm: Target mm; caller holds a reference (e.g. get_task_mm()).
+ * @file: Backing file, or NULL for an anonymous mapping.
+ * @addr: Page-aligned address. If non-zero it is used as given (honoring
+ * MAP_FIXED/MAP_FIXED_NOREPLACE in @flags); if zero, the kernel
+ * chooses a free area in @mm and returns it.
+ * @len: Length in bytes.
+ * @prot: PROT_* protection bits.
+ * @flags: MAP_* flags.
+ * @pgoff: Page offset into @file.
+ * @vm_flags: Extra VMA flags to OR in (e.g. VM_SEALED), or 0.
+ *
+ * The general form of the remote install primitive: a supervisor places a
+ * mapping into a remote task's address space. LSM/fsnotify hooks run against
+ * %current (the installer), paralleling pidfd_getfd()'s cross-task install;
+ * cross-task authorization is the caller's responsibility. The foreign mm is
+ * not mm_populate()d.
+ *
+ * Kernel-chosen placement (@addr == 0) resolves through
+ * __get_unmapped_area() against @mm, so the file's own
+ * ->get_unmapped_area() runs and file-specific placement (hugetlb page
+ * size, THP PMD alignment, SHMLBA cache coloring, device dax alignment)
+ * is honored exactly as for a local mmap().
+ *
+ * Returns the mapped address on success, or a negative errno.
+ */
+unsigned long vm_mmap_remote(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long pgoff, vm_flags_t vm_flags)
+{
+ loff_t off = (loff_t)pgoff << PAGE_SHIFT;
+ unsigned long aligned_len = PAGE_ALIGN(len);
+ unsigned long ret;
+ unsigned long populate;
+ LIST_HEAD(uf);
+
+ if (WARN_ON_ONCE(!mm))
+ return -EINVAL;
+
+ ret = security_mmap_file(file, prot, flags);
+ if (!ret)
+ ret = fsnotify_mmap_perm(file, prot, off, len);
+ if (ret)
+ return ret;
+
+ if (mmap_write_lock_killable(mm))
+ return -EINTR;
+
+ /*
+ * Resolve kernel-chosen placement up front and pin it with
+ * MAP_FIXED, so the mm->task_size check below runs before the
+ * mapping is installed. The file's own ->get_unmapped_area()
+ * performs the search against @mm.
+ */
+ if (!addr) {
+ addr = __get_unmapped_area(mm, file, 0, aligned_len, pgoff,
+ flags, vm_flags);
+ if (IS_ERR_VALUE(addr)) {
+ ret = addr;
+ goto unlock;
+ }
+ }
+
+ if (aligned_len > mm->task_size || addr > mm->task_size - aligned_len) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ if (!(flags & MAP_FIXED_NOREPLACE))
+ flags |= MAP_FIXED;
+
+ ret = __do_mmap(mm, file, addr, len, prot, flags, vm_flags,
+ pgoff, &populate, &uf);
+unlock:
+ mmap_write_unlock(mm);
+ userfaultfd_unmap_complete(mm, &uf);
+ return ret;
+}
+
+/**
+ * vm_munmap_remote - unmap a range from @mm, mirroring vm_munmap() but
+ * targeting an explicit mm.
+ * @mm: Target mm; caller holds a reference (e.g. get_task_mm()).
+ * @start: Start address of the range to unmap.
+ * @len: Length in bytes.
+ *
+ * Returns 0 on success, or a negative errno.
+ */
+int vm_munmap_remote(struct mm_struct *mm, unsigned long start, size_t len)
+{
+ VMA_ITERATOR(vmi, mm, start);
+ LIST_HEAD(uf);
+ int ret;
+
+ if (mmap_write_lock_killable(mm))
+ return -EINTR;
+ ret = do_vmi_munmap(&vmi, mm, start, len, &uf, false);
+ mmap_write_unlock(mm);
+ userfaultfd_unmap_complete(mm, &uf);
+ return ret;
+}
+#endif /* CONFIG_MMU */
+
/*
* Perform a userland memory mapping into the current process address space. See
* the comment for do_mmap() for more details on this operation in general.
diff --git a/mm/vma.c b/mm/vma.c
index 9a6cb0338ed8..788c2a0f35ec 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -1333,7 +1333,7 @@ static void vms_complete_munmap_vmas(struct vma_munmap_struct *vms,
struct vm_area_struct *vma;
struct mm_struct *mm;
- mm = current->mm;
+ mm = vms->mm;
mm->map_count -= vms->vma_count;
mm->locked_vm -= vms->locked_vm;
if (vms->unlock)
@@ -1544,10 +1544,11 @@ static int vms_gather_munmap_vmas(struct vma_munmap_struct *vms,
* @unlock: Unlock after the operation. Only unlocked on success
*/
static void init_vma_munmap(struct vma_munmap_struct *vms,
- struct vma_iterator *vmi, struct vm_area_struct *vma,
- unsigned long start, unsigned long end, struct list_head *uf,
- bool unlock)
+ struct mm_struct *mm, struct vma_iterator *vmi,
+ struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, struct list_head *uf, bool unlock)
{
+ vms->mm = mm;
vms->vmi = vmi;
vms->vma = vma;
if (vma) {
@@ -1591,7 +1592,7 @@ int do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma,
struct vma_munmap_struct vms;
int error;
- init_vma_munmap(&vms, vmi, vma, start, end, uf, unlock);
+ init_vma_munmap(&vms, mm, vmi, vma, start, end, uf, unlock);
error = vms_gather_munmap_vmas(&vms, &mas_detach);
if (error)
goto gather_failed;
@@ -1634,7 +1635,8 @@ int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm,
unsigned long end;
struct vm_area_struct *vma;
- if ((offset_in_page(start)) || start > TASK_SIZE || len > TASK_SIZE-start)
+ if ((offset_in_page(start)) || start > mm->task_size ||
+ len > mm->task_size - start)
return -EINVAL;
end = start + PAGE_ALIGN(len);
@@ -2426,7 +2428,7 @@ static int __mmap_setup(struct mmap_state *map, struct vm_area_desc *desc,
/* Find the first overlapping VMA and initialise unmap state. */
vms->vma = vma_find(vmi, map->end);
- init_vma_munmap(vms, vmi, vms->vma, map->addr, map->end, uf,
+ init_vma_munmap(vms, map->mm, vmi, vms->vma, map->addr, map->end, uf,
/* unlock = */ false);
/* OK, we have overlapping VMAs - prepare to unmap them. */
@@ -2731,11 +2733,10 @@ static bool can_set_ksm_flags_early(struct mmap_state *map)
return false;
}
-static unsigned long __mmap_region(struct file *file, unsigned long addr,
- unsigned long len, vma_flags_t vma_flags,
+static unsigned long __mmap_region(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len, vma_flags_t vma_flags,
unsigned long pgoff, struct list_head *uf)
{
- struct mm_struct *mm = current->mm;
struct vm_area_struct *vma = NULL;
bool have_mmap_prepare = file && file->f_op->mmap_prepare;
VMA_ITERATOR(vmi, mm, addr);
@@ -2809,14 +2810,16 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr,
/**
* mmap_region() - Actually perform the userland mapping of a VMA into
- * current->mm with known, aligned and overflow-checked @addr and @len, and
+ * @mm with known, aligned and overflow-checked @addr and @len, and
* correctly determined VMA flags @vm_flags and page offset @pgoff.
*
* This is an internal memory management function, and should not be used
* directly.
*
- * The caller must write-lock current->mm->mmap_lock.
+ * The caller must write-lock @mm->mmap_lock.
*
+ * @mm: The mm_struct to install the mapping into. The caller must hold a
+ * reference and write-lock its mmap_lock.
* @file: If a file-backed mapping, a pointer to the struct file describing the
* file to be mapped, otherwise NULL.
* @addr: The page-aligned address at which to perform the mapping.
@@ -2830,18 +2833,19 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr,
* Returns: Either an error, or the address at which the requested mapping has
* been performed.
*/
-unsigned long mmap_region(struct file *file, unsigned long addr,
- unsigned long len, vm_flags_t vm_flags,
- unsigned long pgoff, struct list_head *uf)
+unsigned long mmap_region(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len,
+ vm_flags_t vm_flags, unsigned long pgoff,
+ struct list_head *uf)
{
unsigned long ret;
bool writable_file_mapping = false;
const vma_flags_t vma_flags = legacy_to_vma_flags(vm_flags);
- mmap_assert_write_locked(current->mm);
+ mmap_assert_write_locked(mm);
/* Check to see if MDWE is applicable. */
- if (map_deny_write_exec(&vma_flags, &vma_flags))
+ if (map_deny_write_exec(mm, &vma_flags, &vma_flags))
return -EACCES;
/* Allow architectures to sanity-check the vm_flags. */
@@ -2857,13 +2861,13 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
writable_file_mapping = true;
}
- ret = __mmap_region(file, addr, len, vma_flags, pgoff, uf);
+ ret = __mmap_region(mm, file, addr, len, vma_flags, pgoff, uf);
/* Clear our write mapping regardless of error. */
if (writable_file_mapping)
mapping_unmap_writable(file->f_mapping);
- validate_mm(current->mm);
+ validate_mm(mm);
return ret;
}
@@ -2983,6 +2987,8 @@ unsigned long unmapped_area(struct mm_struct *mm,
if (low_limit < mmap_min_addr)
low_limit = mmap_min_addr;
high_limit = info->high_limit;
+ if (mm != current->mm)
+ high_limit = min(high_limit, mm->task_size);
retry:
if (vma_iter_area_lowest(&vmi, low_limit, high_limit, length))
return -ENOMEM;
@@ -3043,6 +3049,8 @@ unsigned long unmapped_area_topdown(struct mm_struct *mm,
if (low_limit < mmap_min_addr)
low_limit = mmap_min_addr;
high_limit = info->high_limit;
+ if (mm != current->mm)
+ high_limit = min(high_limit, mm->task_size);
retry:
if (vma_iter_area_highest(&vmi, low_limit, high_limit, length))
return -ENOMEM;
diff --git a/mm/vma.h b/mm/vma.h
index d0a45718deb9..0c0b9d9c5248 100644
--- a/mm/vma.h
+++ b/mm/vma.h
@@ -32,6 +32,7 @@ struct unlink_vma_file_batch {
* vma munmap operation
*/
struct vma_munmap_struct {
+ struct mm_struct *mm; /* The mm being operated on */
struct vma_iterator *vmi;
struct vm_area_struct *vma; /* The first vma to munmap */
struct vm_area_struct *prev; /* vma before the munmap area */
@@ -459,9 +460,9 @@ bool vma_wants_writenotify(struct vm_area_struct *vma, pgprot_t vm_page_prot);
int mm_take_all_locks(struct mm_struct *mm);
void mm_drop_all_locks(struct mm_struct *mm);
-unsigned long mmap_region(struct file *file, unsigned long addr,
- unsigned long len, vm_flags_t vm_flags, unsigned long pgoff,
- struct list_head *uf);
+unsigned long mmap_region(struct mm_struct *mm, struct file *file,
+ unsigned long addr, unsigned long len, vm_flags_t vm_flags,
+ unsigned long pgoff, struct list_head *uf);
int do_brk_flags(struct vma_iterator *vmi, struct vm_area_struct *brkvma,
unsigned long addr, unsigned long request,
@@ -732,11 +733,12 @@ int relocate_vma_down(struct vm_area_struct *vma, unsigned long shift);
*
* Return: false if proposed change is OK, true if not ok and should be denied.
*/
-static inline bool map_deny_write_exec(const vma_flags_t *old,
+static inline bool map_deny_write_exec(struct mm_struct *mm,
+ const vma_flags_t *old,
const vma_flags_t *new)
{
/* If MDWE is disabled, we have nothing to deny. */
- if (!mm_flags_test(MMF_HAS_MDWE, current->mm))
+ if (!mm_flags_test(MMF_HAS_MDWE, mm))
return false;
/* If the new VMA is not executable, we have nothing to deny. */
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v6] selftests/seccomp: cover non-cooperative pinned-memfd install
2026-07-15 21:19 [PATCH v6 0/8] seccomp: non-cooperative pinned-memfd argument redirect Cong Wang
` (6 preceding siblings ...)
2026-07-15 21:20 ` [PATCH v6 7/8] docs/seccomp: document pinned-memfd redirect ioctls Cong Wang
@ 2026-07-15 21:20 ` Cong Wang
7 siblings, 0 replies; 9+ messages in thread
From: Cong Wang @ 2026-07-15 21:20 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Kees Cook, linux-kernel, Will Drewry, Christian Brauner,
Andrew Morton, linux-mm, Cong Wang
From: Cong Wang <cwang@multikernel.io>
Add 11 tests for SECCOMP_IOCTL_NOTIF_PIN_INSTALL and
SECCOMP_IOCTL_NOTIF_SEND_REDIRECT:
- pinned_memfd_remote: basic install + redirect, plus the unsealed
memfd and out-of-pin rejection paths
- pinned_memfd_target_cannot_unmap: the sealed pin survives the
target's munmap/mprotect/mremap/MAP_FIXED attacks (all EPERM)
- pinned_memfd_execve_scm: SCM_RIGHTS supervisor handoff and re-pin
in the fresh post-execve mm
- pinned_memfd_churn: one listener serves many short-lived targets,
no per-target state
- redirect_outer_refilter, redirect_revalidate_chain: a redirect is
re-validated by every outer filter, not just the nearest
- redirect_outer_trace, redirect_outer_notify: outer TRACE and
USER_NOTIF verdicts over a redirected call fail closed with ENOSYS;
the tracer must not get a PTRACE_EVENT_SECCOMP for the substituted
call, and a listener-less outer USER_NOTIF must still block it
- redirect_denied_syscalls: EOPNOTSUPP for rt_sigreturn and the
clone/fork family
- pinned_memfd_abi, redirect_signal_abi: the original arg register is
restored before returning to user mode, and before any signal frame
is built
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Cong Wang <cwang@multikernel.io>
---
tools/testing/selftests/seccomp/seccomp_bpf.c | 1615 +++++++++++++++++
1 file changed, 1615 insertions(+)
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 358b6c65e120..abe8d6c969ab 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -217,6 +217,10 @@ struct seccomp_metadata {
#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
#endif
+#ifndef SECCOMP_FILTER_FLAG_REDIRECT
+#define SECCOMP_FILTER_FLAG_REDIRECT (1UL << 6)
+#endif
+
#ifndef SECCOMP_RET_USER_NOTIF
#define SECCOMP_RET_USER_NOTIF 0x7fc00000U
@@ -295,6 +299,35 @@ struct seccomp_notif_addfd_big {
#define PTRACE_EVENTMSG_SYSCALL_EXIT 2
#endif
+#ifndef SECCOMP_IOCTL_NOTIF_PIN_INSTALL
+struct seccomp_notif_pin_install {
+ __u64 id;
+ __u32 flags;
+ __u32 memfd;
+ __u64 target_addr;
+ __u64 size;
+ __u64 offset;
+};
+#define SECCOMP_IOCTL_NOTIF_PIN_INSTALL SECCOMP_IOWR(5, \
+ struct seccomp_notif_pin_install)
+#endif
+
+#ifndef SECCOMP_IOCTL_NOTIF_SEND_REDIRECT
+#define SECCOMP_REDIRECT_FLAG_CONTINUE (1UL << 0)
+#define SECCOMP_REDIRECT_ARGS 6
+struct seccomp_notif_resp_redirect {
+ __u64 id;
+ __u32 flags;
+ __u32 args_mask;
+ __u32 ptr_mask;
+ __u32 memfd;
+ __u64 args[SECCOMP_REDIRECT_ARGS];
+ __u64 ptr_len[SECCOMP_REDIRECT_ARGS];
+};
+#define SECCOMP_IOCTL_NOTIF_SEND_REDIRECT SECCOMP_IOW(6, \
+ struct seccomp_notif_resp_redirect)
+#endif
+
#ifndef SECCOMP_USER_NOTIF_FLAG_CONTINUE
#define SECCOMP_USER_NOTIF_FLAG_CONTINUE 0x00000001
#endif
@@ -4368,6 +4401,1588 @@ TEST(user_notification_addfd_rlimit)
close(memfd);
}
+/*
+ * Create a write-sealed memfd of @size for PIN_INSTALL and map a supervisor
+ * writable view, primed with @content. F_SEAL_FUTURE_WRITE keeps this
+ * pre-seal mapping writable (so the test can still stage content) while
+ * barring any other writable reference, as PIN_INSTALL requires. Returns
+ * the memfd.
+ */
+static int make_pin_memfd(struct __test_metadata *_metadata, const char *name,
+ size_t size, char **sup_view, const char *content)
+{
+ int memfd = memfd_create(name, MFD_ALLOW_SEALING);
+
+ ASSERT_GE(memfd, 0);
+ ASSERT_EQ(0, ftruncate(memfd, size));
+ ASSERT_EQ(0, fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW));
+
+ *sup_view = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ memfd, 0);
+ ASSERT_NE(MAP_FAILED, *sup_view);
+ ASSERT_EQ(0, fcntl(memfd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE));
+ memcpy(*sup_view, content, strlen(content) + 1);
+ return memfd;
+}
+
+/*
+ * Non-cooperative pinned-memfd: kernel installs a sealed PROT_READ
+ * MAP_SHARED mapping of the supervisor's memfd directly into the
+ * trapped task's mm. The target runs no mmap or mseal code itself —
+ * this exercises the same kernel path that a fork+execve sandbox
+ * supervisor would use to install a pin in the new image's fresh
+ * post-exec mm.
+ *
+ * Target child does nothing but call openat() on a bait path. The
+ * supervisor catches the trap, calls PIN_INSTALL (kernel does the
+ * mmap + seal in target's mm via vm_mmap_remote()), writes a
+ * safe path into its own memfd view, and SEND_REDIRECTs args[1]
+ * into the freshly installed pin. The child's openat resumes,
+ * reads from the sealed pin, and returns an fd to the safe path.
+ */
+TEST(user_notification_pinned_memfd_remote)
+{
+ pid_t pid;
+ long ret;
+ int status, listener, memfd, unsealed;
+ struct seccomp_notif req = {};
+ struct seccomp_notif_pin_install pin = {};
+ struct seccomp_notif_pin_install unsealed_pin = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ char *sup_view;
+ const size_t PIN_SIZE = 4096;
+ const char *safe_path = "/dev/null";
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ memfd = make_pin_memfd(_metadata, "pinned-remote", PIN_SIZE,
+ &sup_view, safe_path);
+
+ listener = user_notif_syscall(__NR_openat,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ int fd;
+
+ /*
+ * Target performs no setup. Just trap on openat. Kernel
+ * (driven by the supervisor) will install the pin in this
+ * process's mm at a kernel-chosen address behind our back,
+ * and our openat will be redirected to read from there.
+ */
+ fd = syscall(__NR_openat, AT_FDCWD,
+ "/this/should/never/be/touched", O_RDONLY, 0);
+ if (fd < 0)
+ _exit(11);
+ _exit(0);
+ }
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_openat);
+
+ pin.id = req.id;
+ pin.memfd = memfd;
+ pin.target_addr = 0;
+ pin.size = PIN_SIZE;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_PIN_INSTALL, &pin)) {
+ if (errno == EINVAL) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ SKIP(goto cleanup,
+ "Kernel does not support pinned-memfd remote install");
+ }
+ TH_LOG("PIN_INSTALL failed: errno=%d", errno);
+ }
+
+ /* The kernel wrote a non-zero, page-aligned address back to us. */
+ EXPECT_NE(0, pin.target_addr);
+ EXPECT_EQ(0, pin.target_addr & (PIN_SIZE - 1));
+
+ /* Reject: the backing memfd must be write-sealed. */
+ unsealed = memfd_create("unsealed", MFD_ALLOW_SEALING);
+ ASSERT_GE(unsealed, 0);
+ ASSERT_EQ(0, ftruncate(unsealed, PIN_SIZE));
+ unsealed_pin.id = req.id;
+ unsealed_pin.memfd = unsealed;
+ unsealed_pin.size = PIN_SIZE;
+ EXPECT_EQ(-1, ioctl(listener, SECCOMP_IOCTL_NOTIF_PIN_INSTALL,
+ &unsealed_pin));
+ EXPECT_EQ(EINVAL, errno);
+ close(unsealed);
+
+ /* Reject: redirect outside any installed pin. */
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 1;
+ redir.ptr_mask = 1U << 1;
+ redir.memfd = memfd;
+ redir.ptr_len[1] = strlen(safe_path) + 1;
+ redir.args[1] = pin.target_addr + PIN_SIZE; /* one byte past */
+ EXPECT_EQ(-1, ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT,
+ &redir));
+ EXPECT_EQ(EFAULT, errno);
+
+ /* Reject: base is inside the pin but the extent runs past its end. */
+ redir.args[1] = pin.target_addr;
+ redir.ptr_len[1] = PIN_SIZE + 1;
+ EXPECT_EQ(-1, ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT,
+ &redir));
+ EXPECT_EQ(EFAULT, errno);
+
+ /* Happy path: redirect into the kernel-installed pin. */
+ redir.args[1] = pin.target_addr;
+ redir.ptr_len[1] = strlen(safe_path) + 1;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT,
+ &redir)) {
+ /* Unblock the trapped child so waitpid() cannot hang. */
+ kill(pid, SIGKILL);
+ }
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ TH_LOG("child exit %d (11=openat fail)", WEXITSTATUS(status));
+ }
+
+cleanup:
+ munmap(sup_view, PIN_SIZE);
+ close(memfd);
+ close(listener);
+}
+
+/*
+ * The installed pin is VM_SEALED. A hostile target that learns the pin
+ * address must still be unable to unmap, move, reprotect or MAP_FIXED-stomp
+ * it, though it may read it. This is the property that keeps a redirected
+ * pointer aimed at supervisor-controlled bytes for the whole syscall.
+ */
+TEST(user_notification_pinned_memfd_target_cannot_unmap)
+{
+ pid_t pid;
+ long ret;
+ int status, listener, memfd;
+ struct seccomp_notif req = {};
+ struct seccomp_notif_pin_install pin = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ char *sup_view;
+ int addrpipe[2];
+ const size_t PIN_SIZE = 4096;
+ const char *safe_path = "/dev/null";
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ ASSERT_EQ(0, pipe(addrpipe));
+
+ memfd = make_pin_memfd(_metadata, "pin-nounmap", PIN_SIZE,
+ &sup_view, safe_path);
+
+ listener = user_notif_syscall(__NR_openat,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ unsigned long pin_addr;
+ char *p;
+ int fd;
+
+ close(addrpipe[1]);
+
+ /* Trap; the supervisor installs+seals the pin and redirects. */
+ fd = syscall(__NR_openat, AT_FDCWD,
+ "/this/should/never/be/touched", O_RDONLY, 0);
+ if (fd < 0)
+ _exit(11);
+
+ /* Learn where the kernel installed the pin. */
+ if (read(addrpipe[0], &pin_addr, sizeof(pin_addr)) !=
+ sizeof(pin_addr))
+ _exit(12);
+ p = (char *)pin_addr;
+
+ /* Mapped and readable: it holds the redirected path. */
+ if (*p != '/')
+ _exit(13);
+
+ /* Sealed: unmap must fail and tear nothing down. */
+ if (munmap((void *)pin_addr, PIN_SIZE) == 0)
+ _exit(20);
+ if (errno != EPERM)
+ _exit(21);
+
+ /* Sealed: cannot reprotect it (even to PROT_NONE). */
+ if (mprotect((void *)pin_addr, PIN_SIZE, PROT_NONE) == 0)
+ _exit(22);
+ if (errno != EPERM)
+ _exit(23);
+
+ /* Sealed: cannot move or resize it. */
+ if (mremap((void *)pin_addr, PIN_SIZE, PIN_SIZE * 2,
+ MREMAP_MAYMOVE) != MAP_FAILED)
+ _exit(24);
+ if (errno != EPERM)
+ _exit(25);
+
+ /* Sealed: cannot be replaced by a MAP_FIXED mapping. */
+ if (mmap((void *)pin_addr, PIN_SIZE, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) !=
+ MAP_FAILED)
+ _exit(26);
+ if (errno != EPERM)
+ _exit(27);
+
+ /* Survived every attack, still mapped and readable. */
+ if (*p != '/')
+ _exit(28);
+ _exit(0);
+ }
+
+ close(addrpipe[0]);
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_openat);
+
+ pin.id = req.id;
+ pin.memfd = memfd;
+ pin.target_addr = 0;
+ pin.size = PIN_SIZE;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_PIN_INSTALL, &pin)) {
+ if (errno == EINVAL) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ SKIP(goto cleanup,
+ "Kernel does not support pinned-memfd remote install");
+ }
+ TH_LOG("PIN_INSTALL failed: errno=%d", errno);
+ }
+
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 1;
+ redir.ptr_mask = 1U << 1;
+ redir.memfd = memfd;
+ redir.ptr_len[1] = strlen(safe_path) + 1;
+ redir.args[1] = pin.target_addr;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT, &redir)) {
+ kill(pid, SIGKILL);
+ }
+
+ /* Hand the target the pin address so it can try to attack it. */
+ EXPECT_EQ(sizeof(pin.target_addr),
+ write(addrpipe[1], &pin.target_addr, sizeof(pin.target_addr)));
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ TH_LOG("child exit %d (>=20 = seal breach)",
+ WEXITSTATUS(status));
+ }
+
+cleanup:
+ close(addrpipe[1]);
+ munmap(sup_view, PIN_SIZE);
+ close(memfd);
+ close(listener);
+}
+
+/*
+ * Helper for the execve test: read up to @max bytes of a NUL-terminated
+ * string from @pid's mm at @addr into @out. Returns the length read
+ * (excluding the NUL), or -1 on failure or no NUL.
+ */
+static ssize_t read_remote_string(pid_t pid, unsigned long addr,
+ char *out, size_t max)
+{
+ struct iovec local = { .iov_base = out, .iov_len = max };
+ struct iovec remote = { .iov_base = (void *)addr, .iov_len = max };
+ ssize_t n;
+ size_t i;
+
+ n = process_vm_readv(pid, &local, 1, &remote, 1, 0);
+ if (n <= 0)
+ return -1;
+ for (i = 0; i < (size_t)n; i++)
+ if (out[i] == '\0')
+ return (ssize_t)i;
+ return -1;
+}
+
+/*
+ * Send a file descriptor over a connected UNIX socket via SCM_RIGHTS.
+ * Used by the execve_scm test so the target child can hand its
+ * SECCOMP_FILTER_FLAG_NEW_LISTENER fd to the supervising parent
+ * without the parent having to inherit the seccomp filter itself.
+ */
+static int send_fd(int sock, int fd)
+{
+ char cbuf[CMSG_SPACE(sizeof(int))] = {};
+ char data = 'x';
+ struct iovec iov = { .iov_base = &data, .iov_len = 1 };
+ struct msghdr msg = {
+ .msg_iov = &iov, .msg_iovlen = 1,
+ .msg_control = cbuf, .msg_controllen = sizeof(cbuf),
+ };
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+ return sendmsg(sock, &msg, 0) < 0 ? -1 : 0;
+}
+
+static int recv_fd(int sock)
+{
+ char cbuf[CMSG_SPACE(sizeof(int))] = {};
+ char data;
+ struct iovec iov = { .iov_base = &data, .iov_len = 1 };
+ struct msghdr msg = {
+ .msg_iov = &iov, .msg_iovlen = 1,
+ .msg_control = cbuf, .msg_controllen = sizeof(cbuf),
+ };
+ struct cmsghdr *cmsg;
+ int fd;
+
+ if (recvmsg(sock, &msg, 0) < 0)
+ return -1;
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg || cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS ||
+ cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
+ return -1;
+ memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
+ return fd;
+}
+
+struct addr_range {
+ unsigned long start, end;
+};
+
+/*
+ * Parse /proc/<pid>/maps looking for the dynamic linker's executable
+ * mapping (glibc ld-linux-*.so, musl ld-musl-*.so, etc.). The trapped
+ * task's instruction_pointer falling in this range identifies a
+ * loader-bootstrap syscall (race-free, kernel-truth) so the supervisor
+ * can auto-allow it without inspecting argument content via the racy
+ * process_vm_readv path.
+ *
+ * Requires the supervisor not to be subject to the seccomp filter
+ * itself -- fopen() internally calls openat(). The execve_scm test
+ * structure (child installs filter, sends listener fd to parent via
+ * SCM_RIGHTS) satisfies that.
+ *
+ * Returns 0 on success with @out populated, -1 if not found.
+ */
+static int find_loader_text_range(pid_t pid, struct addr_range *out)
+{
+ char maps_path[64];
+ char line[512];
+ FILE *f;
+ int found = 0;
+
+ snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps", pid);
+ f = fopen(maps_path, "r");
+ if (!f)
+ return -1;
+
+ while (fgets(line, sizeof(line), f)) {
+ unsigned long start, end;
+ char perms[8];
+ char *path;
+
+ if (sscanf(line, "%lx-%lx %7s", &start, &end, perms) != 3)
+ continue;
+ if (!strchr(perms, 'x'))
+ continue;
+ path = strchr(line, '/');
+ if (!path)
+ continue;
+ /*
+ * Match common dynamic-linker basenames: ld-linux-*.so
+ * (glibc), ld-musl-*.so (musl), ld-*.so (older glibc).
+ */
+ if (strstr(path, "/ld-") || strstr(path, "/ld.so")) {
+ out->start = start;
+ out->end = end;
+ found = 1;
+ break;
+ }
+ }
+ fclose(f);
+ return found ? 0 : -1;
+}
+
+/*
+ * Non-cooperative pinned-memfd across a real execve, using the proper
+ * supervisor-isolation pattern: the child (target) installs the seccomp
+ * filter on itself and sends its listener fd to the parent (supervisor)
+ * via SCM_RIGHTS over a socketpair. The parent therefore does not carry
+ * the seccomp filter and can freely call openat() -- which is what makes
+ * the race-free, kernel-truth loader detection (req.data.instruction_pointer
+ * + /proc/<pid>/maps) actually usable.
+ *
+ * Phase 1: child does a pre-execve openat; the supervisor PIN_INSTALLs and
+ * SEND_REDIRECTs. Phase 2: child execve's, so the pre-execve pin VMA dies
+ * with the old mm. Phase 3: in the fresh post-execve mm the supervisor
+ * PIN_INSTALLs again (idempotent replace of the stale bookkeeping) and
+ * SEND_REDIRECTs, proving the full redirect mechanism survives an mm
+ * replacement, not just the install side.
+ */
+TEST(user_notification_pinned_memfd_execve_scm)
+{
+ pid_t pid;
+ int status, listener, memfd, sv[2];
+ struct seccomp_notif req = {};
+ struct seccomp_notif_pin_install pin = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ struct seccomp_notif_resp cont_resp = {};
+ char *sup_view;
+ const size_t PIN_SIZE = 4096;
+ const char *safe_path = "/dev/null";
+ const char *bait = "/seccomp_pinned_memfd_test_bait_scm";
+ bool post_exec_install_ok = false;
+ bool post_exec_redirect_done = false;
+ bool loader_known = false;
+ bool loader_check_attempted = false;
+ struct addr_range loader_range = {};
+ int phase = 0;
+ int trap_count = 0;
+ const int trap_limit = 200;
+
+ if (access("/bin/cat", X_OK) != 0)
+ SKIP(return, "/bin/cat not present");
+
+ memfd = make_pin_memfd(_metadata, "pin-execve-scm", PIN_SIZE,
+ &sup_view, safe_path);
+
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sv));
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat,
+ 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)ARRAY_SIZE(filter),
+ .filter = filter,
+ };
+ int my_listener;
+ int fd;
+
+ close(sv[0]);
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
+ _exit(20);
+ my_listener = seccomp(SECCOMP_SET_MODE_FILTER,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT,
+ &prog);
+ if (my_listener < 0)
+ _exit(21);
+ if (send_fd(sv[1], my_listener) < 0)
+ _exit(22);
+ close(my_listener);
+ close(sv[1]);
+
+ /* Pre-execve trap. */
+ fd = syscall(__NR_openat, AT_FDCWD,
+ "/this/should/never/be/touched", O_RDONLY, 0);
+ if (fd < 0)
+ _exit(11);
+
+ execl("/bin/cat", "cat", bait, (char *)NULL);
+ _exit(12);
+ }
+
+ close(sv[1]);
+ listener = recv_fd(sv[0]);
+ close(sv[0]);
+ ASSERT_GE(listener, 0);
+
+ /*
+ * Parent has the listener fd and does NOT have the seccomp
+ * filter. fopen(/proc/<pid>/maps) below works without
+ * deadlocking on the parent's own openat.
+ */
+ for (;;) {
+ struct pollfd pfd = { .fd = listener, .events = POLLIN };
+ int pret = poll(&pfd, 1, 500);
+ pid_t reaped;
+ bool ip_in_loader;
+
+ if (pret < 0)
+ break;
+ if (pret == 0 || !(pfd.revents & POLLIN)) {
+ reaped = waitpid(pid, &status, WNOHANG);
+ if (reaped == pid)
+ break;
+ if (pfd.revents & (POLLHUP | POLLERR))
+ break;
+ continue;
+ }
+
+ memset(&req, 0, sizeof(req));
+ if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req) < 0) {
+ TH_LOG("NOTIF_RECV failed: errno=%d", errno);
+ break;
+ }
+ if (++trap_count > trap_limit) {
+ TH_LOG("trap_limit (%d) exceeded", trap_limit);
+ break;
+ }
+
+ if (phase == 0) {
+ pin.id = req.id;
+ pin.memfd = memfd;
+ pin.target_addr = 0;
+ pin.size = PIN_SIZE;
+ if (ioctl(listener,
+ SECCOMP_IOCTL_NOTIF_PIN_INSTALL,
+ &pin) != 0) {
+ TH_LOG("pre-exec PIN_INSTALL failed: errno=%d",
+ errno);
+ if (errno == EINVAL)
+ SKIP(goto cleanup_scm,
+ "Kernel lacks pinned-memfd remote");
+ goto cleanup_scm;
+ }
+
+ memset(&redir, 0, sizeof(redir));
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 1;
+ redir.ptr_mask = 1U << 1;
+ redir.memfd = memfd;
+ redir.ptr_len[1] = strlen(safe_path) + 1;
+ redir.args[1] = pin.target_addr;
+ if (ioctl(listener,
+ SECCOMP_IOCTL_NOTIF_SEND_REDIRECT,
+ &redir) != 0) {
+ TH_LOG("pre-exec SEND_REDIRECT failed: errno=%d",
+ errno);
+ goto cleanup_scm;
+ }
+ phase = 1;
+ continue;
+ }
+
+ /*
+ * Post-execve. Lazily resolve the loader range. The
+ * supervisor's own openat (fopen on /proc/<pid>/maps)
+ * doesn't trap because the filter lives on the child,
+ * not on us.
+ */
+ if (!loader_known && !loader_check_attempted) {
+ if (find_loader_text_range(req.pid,
+ &loader_range) == 0)
+ loader_known = true;
+ loader_check_attempted = true;
+ }
+
+ ip_in_loader = loader_known &&
+ req.data.instruction_pointer >= loader_range.start &&
+ req.data.instruction_pointer < loader_range.end;
+
+ if (ip_in_loader) {
+ memset(&cont_resp, 0, sizeof(cont_resp));
+ cont_resp.id = req.id;
+ cont_resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+ ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &cont_resp);
+ continue;
+ }
+
+ /* Program code: inspect the path to identify the bait. */
+ {
+ char path[PATH_MAX];
+ ssize_t n;
+
+ n = read_remote_string(req.pid, req.data.args[1],
+ path, sizeof(path));
+ if (n < 0 || strcmp(path, bait) != 0) {
+ memset(&cont_resp, 0, sizeof(cont_resp));
+ cont_resp.id = req.id;
+ cont_resp.flags =
+ SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+ ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND,
+ &cont_resp);
+ continue;
+ }
+
+ pin.id = req.id;
+ pin.memfd = memfd;
+ pin.target_addr = 0;
+ pin.size = PIN_SIZE;
+ if (ioctl(listener,
+ SECCOMP_IOCTL_NOTIF_PIN_INSTALL,
+ &pin) == 0) {
+ post_exec_install_ok = true;
+ } else {
+ TH_LOG("post-exec PIN_INSTALL failed: errno=%d",
+ errno);
+ memset(&cont_resp, 0, sizeof(cont_resp));
+ cont_resp.id = req.id;
+ cont_resp.flags =
+ SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+ ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND,
+ &cont_resp);
+ continue;
+ }
+
+ memset(&redir, 0, sizeof(redir));
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 1;
+ redir.ptr_mask = 1U << 1;
+ redir.memfd = memfd;
+ redir.ptr_len[1] = strlen(safe_path) + 1;
+ redir.args[1] = pin.target_addr;
+ if (ioctl(listener,
+ SECCOMP_IOCTL_NOTIF_SEND_REDIRECT,
+ &redir) == 0) {
+ post_exec_redirect_done = true;
+ } else {
+ TH_LOG("post-exec SEND_REDIRECT failed: errno=%d",
+ errno);
+ memset(&cont_resp, 0, sizeof(cont_resp));
+ cont_resp.id = req.id;
+ cont_resp.flags =
+ SECCOMP_USER_NOTIF_FLAG_CONTINUE;
+ ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND,
+ &cont_resp);
+ }
+ }
+ }
+
+ if (waitpid(pid, &status, WNOHANG) == 0) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ }
+ EXPECT_EQ(true, loader_known) {
+ TH_LOG("find_loader_text_range never resolved");
+ }
+ EXPECT_EQ(true, post_exec_install_ok);
+ EXPECT_EQ(true, post_exec_redirect_done);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+
+cleanup_scm:
+ if (waitpid(pid, &status, WNOHANG) == 0) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ }
+ munmap(sup_view, PIN_SIZE);
+ close(memfd);
+ close(listener);
+}
+
+/*
+ * Stateless redirect validation must hold up across many short-lived
+ * targets over one listener, and must not accumulate per-target state.
+ *
+ * PIN_INSTALL records nothing: the installed VM_SEALED VMA is the only
+ * record, and SEND_REDIRECT re-validates the pointer against the live
+ * mapping (sealed, read-only, backed by the supervisor's memfd inode).
+ * So a supervisor servicing a long churn of targets keeps working with
+ * no bookkeeping to leak. Each iteration lets the kernel choose the pin
+ * address in the fresh target mm; every install/redirect must succeed, and
+ * kmemleak/KASAN over the loop confirms nothing accumulates.
+ */
+TEST(user_notification_pinned_memfd_churn)
+{
+ const size_t PIN_SIZE = 4096;
+ const char *safe_path = "/dev/null";
+ const int iters = 16;
+ int listener, memfd, i;
+ char *sup_view;
+ long ret;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ memfd = make_pin_memfd(_metadata, "pinned-reap", PIN_SIZE,
+ &sup_view, safe_path);
+
+ listener = user_notif_syscall(__NR_openat,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ for (i = 0; i < iters; i++) {
+ struct seccomp_notif req = {};
+ struct seccomp_notif_pin_install pin = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ int status;
+ pid_t pid;
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+ if (pid == 0) {
+ int fd = syscall(__NR_openat, AT_FDCWD,
+ "/never/touched", O_RDONLY, 0);
+ _exit(fd < 0 ? 11 : 0);
+ }
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_openat);
+
+ pin.id = req.id;
+ pin.memfd = memfd;
+ pin.target_addr = 0;
+ pin.size = PIN_SIZE;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_PIN_INSTALL,
+ &pin)) {
+ if (errno == EINVAL) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ SKIP(goto cleanup,
+ "Kernel lacks pinned-memfd remote install");
+ }
+ TH_LOG("iter %d PIN_INSTALL failed: errno=%d", i, errno);
+ }
+
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 1;
+ redir.ptr_mask = 1U << 1;
+ redir.memfd = memfd;
+ redir.ptr_len[1] = strlen(safe_path) + 1;
+ redir.args[1] = pin.target_addr;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT,
+ &redir)) {
+ kill(pid, SIGKILL);
+ }
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ TH_LOG("iter %d child exit %d (11=openat fail)",
+ i, WEXITSTATUS(status));
+ }
+ /*
+ * Target is dead now; its pin (this iter's mm, at the
+ * kernel-chosen address) is stale. The next iteration's
+ * PIN_INSTALL walk must reap it rather than leak the range +
+ * mm + memfd reference.
+ */
+ }
+
+cleanup:
+ munmap(sup_view, PIN_SIZE);
+ close(memfd);
+ close(listener);
+}
+
+#ifdef __NR_socket
+/*
+ * A redirect must not let an inner (more recently installed) filter's
+ * notifier smuggle a syscall past an outer filter. Two filters are
+ * stacked on the target:
+ *
+ * outer (installed first): socket(AF_INET, ...) -> RET_ERRNO(EACCES),
+ * everything else ALLOW.
+ * inner (installed second): socket -> RET_USER_NOTIF.
+ *
+ * The child calls socket(AF_UNIX, ...), which the outer filter allows, so
+ * the inner notifier wins and fires. The supervisor SEND_REDIRECTs arg0
+ * to AF_INET. The kernel must then re-run the outer filter against the
+ * rewritten registers and block it with EACCES; without the outer-suffix
+ * re-validation the inner filter would have bypassed the outer policy.
+ */
+TEST(user_notification_redirect_outer_refilter)
+{
+ struct sock_filter outer_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_socket, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(0)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 1),
+ BPF_STMT(BPF_RET | BPF_K,
+ SECCOMP_RET_ERRNO | (EACCES & SECCOMP_RET_DATA)),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog outer_prog = {
+ .len = (unsigned short)ARRAY_SIZE(outer_filter),
+ .filter = outer_filter,
+ };
+ struct seccomp_notif req = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ int status, listener;
+ pid_t pid;
+ long ret;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ /* Outer filter first => it becomes the outer/root of the stack. */
+ ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &outer_prog));
+
+ /* Inner USER_NOTIF filter second (innermost); returns the listener. */
+ listener = user_notif_syscall(__NR_socket,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ int fd = syscall(__NR_socket, AF_UNIX, SOCK_STREAM, 0);
+
+ if (fd >= 0)
+ _exit(12);
+ if (errno != EACCES)
+ _exit(13);
+ _exit(0);
+ }
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_socket);
+ EXPECT_EQ(req.data.args[0], AF_UNIX);
+
+ /* Scalar redirect of arg0 (no pin needed): AF_UNIX -> AF_INET. */
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 0;
+ redir.args[0] = AF_INET;
+ ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT, &redir);
+ if (ret < 0 && errno == EINVAL) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ close(listener);
+ SKIP(return, "Kernel lacks SECCOMP_IOCTL_NOTIF_SEND_REDIRECT");
+ }
+ EXPECT_EQ(0, ret);
+ if (ret)
+ kill(pid, SIGKILL);
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ switch (WEXITSTATUS(status)) {
+ case 12:
+ TH_LOG("child exit 12: redirect bypassed the outer filter");
+ break;
+ case 13:
+ TH_LOG("child exit 13: socket failed with unexpected errno");
+ break;
+ default:
+ TH_LOG("child exit %d (unexpected)", WEXITSTATUS(status));
+ }
+ }
+
+ close(listener);
+}
+#endif /* __NR_socket */
+
+/*
+ * SEND_REDIRECT refuses syscalls whose register substitution would be unsafe
+ * and returns -EOPNOTSUPP: sigreturn (its frame restore fights the redirect
+ * restore) and the clone/fork family (a new child inherits the substituted
+ * registers with no restore). The trapped call is then answered with a plain
+ * error, so it never actually runs.
+ *
+ * The target installs the filter and hands the listener to the supervisor over
+ * a socketpair: a filter that traps fork/clone cannot be installed before the
+ * supervisor itself forks the target.
+ */
+TEST(user_notification_redirect_denied_syscalls)
+{
+ static const int denied[] = {
+ __NR_rt_sigreturn,
+ __NR_clone,
+ __NR_clone3,
+#ifdef __NR_fork
+ __NR_fork,
+#endif
+#ifdef __NR_vfork
+ __NR_vfork,
+#endif
+ };
+ unsigned int i;
+ long ret;
+
+ for (i = 0; i < ARRAY_SIZE(denied); i++) {
+ struct seccomp_notif req = {};
+ struct seccomp_notif_resp resp = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ int sk[2], listener, status;
+ pid_t pid;
+
+ if (denied[i] < 0)
+ continue;
+
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sk));
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+ if (pid == 0) {
+ close(sk[0]);
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
+ _exit(1);
+ listener = user_notif_syscall(
+ denied[i],
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ if (listener < 0)
+ _exit(2);
+ if (send_fd(sk[1], listener))
+ _exit(3);
+ syscall(denied[i], 0, 0, 0, 0, 0, 0);
+ _exit(0);
+ }
+
+ close(sk[1]);
+ listener = recv_fd(sk[0]);
+ if (listener < 0) {
+ waitpid(pid, &status, 0);
+ close(sk[0]);
+ SKIP(return, "SECCOMP_FILTER_FLAG_REDIRECT unsupported");
+ }
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, denied[i]);
+
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 0;
+ redir.args[0] = 0;
+ errno = 0;
+ ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT, &redir);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOPNOTSUPP, errno) {
+ TH_LOG("nr %d: SEND_REDIRECT errno %d, want EOPNOTSUPP",
+ denied[i], errno);
+ }
+
+ resp.id = req.id;
+ resp.error = -EPERM;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp));
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ TH_LOG("nr %d: child exit %d", denied[i],
+ WEXITSTATUS(status));
+ }
+ close(listener);
+ close(sk[0]);
+ }
+}
+
+#ifdef __NR_socket
+/*
+ * Re-validation walks *every* filter outer to the notifier, not just the
+ * nearest. Stack (outer -> inner):
+ *
+ * outer (1st): socket(AF_INET) -> RET_ERRNO(EACCES), else ALLOW
+ * middle (2nd): ALLOW everything
+ * inner (3rd): socket -> RET_USER_NOTIF (the redirector)
+ *
+ * The target calls socket(AF_UNIX); the inner notifier fires and the
+ * supervisor SEND_REDIRECTs arg0 to AF_INET. The outward walk must pass
+ * through the permissive middle filter and still reach the outer filter,
+ * which blocks the substituted call with EACCES. If the walk stopped at the
+ * first outer filter (middle, ALLOW), the redirect would slip past the outer
+ * policy. This exercises the iterative multi-filter walk that the single-outer
+ * outer_refilter test does not.
+ */
+TEST(user_notification_redirect_revalidate_chain)
+{
+ struct sock_filter outer_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_socket, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(0)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 1),
+ BPF_STMT(BPF_RET | BPF_K,
+ SECCOMP_RET_ERRNO | (EACCES & SECCOMP_RET_DATA)),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog outer_prog = {
+ .len = (unsigned short)ARRAY_SIZE(outer_filter),
+ .filter = outer_filter,
+ };
+ struct sock_filter allow_filter[] = {
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog allow_prog = {
+ .len = (unsigned short)ARRAY_SIZE(allow_filter),
+ .filter = allow_filter,
+ };
+ struct seccomp_notif req = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ int status, listener;
+ pid_t pid;
+ long ret;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ /* outer (root) -> middle (permissive) -> inner (notifier). */
+ ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &outer_prog));
+ ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &allow_prog));
+ listener = user_notif_syscall(__NR_socket,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ int fd = syscall(__NR_socket, AF_UNIX, SOCK_STREAM, 0);
+
+ if (fd >= 0)
+ _exit(12);
+ if (errno != EACCES)
+ _exit(13);
+ _exit(0);
+ }
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_socket);
+ EXPECT_EQ(req.data.args[0], AF_UNIX);
+
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 0;
+ redir.args[0] = AF_INET;
+ ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT, &redir);
+ if (ret < 0 && errno == EINVAL) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ close(listener);
+ SKIP(return, "Kernel lacks SECCOMP_IOCTL_NOTIF_SEND_REDIRECT");
+ }
+ EXPECT_EQ(0, ret);
+ if (ret)
+ kill(pid, SIGKILL);
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ switch (WEXITSTATUS(status)) {
+ case 12:
+ TH_LOG("exit 12: walk stopped at middle; outer bypassed");
+ break;
+ case 13:
+ TH_LOG("exit 13: socket failed with unexpected errno");
+ break;
+ default:
+ TH_LOG("child exit %d (unexpected)", WEXITSTATUS(status));
+ }
+ }
+
+ close(listener);
+}
+
+/*
+ * An outer TRACE verdict over a redirected syscall fails closed. TRACE hands
+ * the syscall to a tracer that may rewrite it, and a rewrite cannot be
+ * soundly re-composed mid-walk, so the kernel must not consult the tracer at
+ * all: the substituted call is skipped with -ENOSYS, exactly like TRACE with
+ * no tracer attached, and the tracer must not receive a PTRACE_EVENT_SECCOMP
+ * for it. Stack (outer -> inner):
+ *
+ * outer (1st): socket(AF_INET) -> RET_TRACE, else ALLOW
+ * inner (2nd): socket -> RET_USER_NOTIF (the redirector)
+ *
+ * The traced child calls socket(AF_UNIX); the supervisor (also the tracer)
+ * SEND_REDIRECTs arg0 to AF_INET, which the outer filter claims. The child
+ * must observe ENOSYS and the tracer a plain exit, not a seccomp event stop.
+ */
+TEST(user_notification_redirect_outer_trace)
+{
+ struct sock_filter outer_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_socket, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(0)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE | 0x23),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog outer_prog = {
+ .len = (unsigned short)ARRAY_SIZE(outer_filter),
+ .filter = outer_filter,
+ };
+ struct seccomp_notif req = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ int status, listener;
+ pid_t pid;
+ long ret;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ /* Outer filter first => it becomes the outer/root of the stack. */
+ ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &outer_prog));
+
+ /* Inner USER_NOTIF filter second (innermost); returns the listener. */
+ listener = user_notif_syscall(__NR_socket,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ int fd;
+
+ if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
+ _exit(14);
+ if (raise(SIGSTOP))
+ _exit(15);
+
+ fd = syscall(__NR_socket, AF_UNIX, SOCK_STREAM, 0);
+ if (fd >= 0)
+ _exit(12);
+ if (errno != ENOSYS)
+ _exit(13);
+ _exit(0);
+ }
+
+ /* Tracer handshake: enable PTRACE_EVENT_SECCOMP reporting. */
+ ASSERT_EQ(pid, waitpid(pid, &status, 0));
+ ASSERT_EQ(true, WIFSTOPPED(status));
+ ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
+ ASSERT_EQ(0, ptrace(PTRACE_SETOPTIONS, pid, NULL,
+ PTRACE_O_TRACESECCOMP));
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, pid, NULL, 0));
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_socket);
+ EXPECT_EQ(req.data.args[0], AF_UNIX);
+
+ /* Scalar redirect of arg0 (no pin needed): AF_UNIX -> AF_INET. */
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 0;
+ redir.args[0] = AF_INET;
+ ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT, &redir);
+ if (ret < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ close(listener);
+ SKIP(return, "Kernel lacks SECCOMP_IOCTL_NOTIF_SEND_REDIRECT");
+ }
+ EXPECT_EQ(0, ret);
+
+ /*
+ * A plain exit, not a ptrace stop: the tracer must never see a
+ * PTRACE_EVENT_SECCOMP for the substituted call.
+ */
+ EXPECT_EQ(pid, waitpid(pid, &status, 0));
+ EXPECT_EQ(true, WIFEXITED(status)) {
+ if (WIFSTOPPED(status) &&
+ status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)))
+ TH_LOG("tracer got PTRACE_EVENT_SECCOMP for the redirected call");
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ }
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ switch (WEXITSTATUS(status)) {
+ case 12:
+ TH_LOG("child exit 12: redirected socket ran past the outer TRACE filter");
+ break;
+ case 13:
+ TH_LOG("child exit 13: socket failed with unexpected errno (want ENOSYS)");
+ break;
+ default:
+ TH_LOG("child exit %d (unexpected)", WEXITSTATUS(status));
+ }
+ }
+
+ close(listener);
+}
+
+/*
+ * An outer USER_NOTIF verdict over a redirected syscall: the walk hands the
+ * substituted call to the outer filter's own notifier. A chain can hold only
+ * one listener (installing a second one fails with EBUSY), so an outer
+ * USER_NOTIF filter can never have a live supervisor today; its verdict
+ * resolves like any listener-less USER_NOTIF, skipping the syscall with
+ * -ENOSYS. Stack (outer -> inner):
+ *
+ * outer (1st): socket(AF_INET) -> RET_USER_NOTIF (no listener), else ALLOW
+ * inner (2nd): socket -> RET_USER_NOTIF (the redirector, with the listener)
+ *
+ * The child calls socket(AF_UNIX); the supervisor SEND_REDIRECTs arg0 to
+ * AF_INET, which the outer filter claims. The child must observe ENOSYS: the
+ * redirect must not slip past the outer verdict just because no listener
+ * stands behind it.
+ */
+TEST(user_notification_redirect_outer_notify)
+{
+ struct sock_filter outer_filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_socket, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(0)),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 1),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog outer_prog = {
+ .len = (unsigned short)ARRAY_SIZE(outer_filter),
+ .filter = outer_filter,
+ };
+ struct seccomp_notif req = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ int status, listener;
+ pid_t pid;
+ long ret;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ /* Outer filter first => it becomes the outer/root of the stack. */
+ ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &outer_prog));
+
+ /* Inner USER_NOTIF filter second (innermost); returns the listener. */
+ listener = user_notif_syscall(__NR_socket,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ int fd = syscall(__NR_socket, AF_UNIX, SOCK_STREAM, 0);
+
+ if (fd >= 0)
+ _exit(12);
+ if (errno != ENOSYS)
+ _exit(13);
+ _exit(0);
+ }
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_socket);
+ EXPECT_EQ(req.data.args[0], AF_UNIX);
+
+ /* Scalar redirect of arg0 (no pin needed): AF_UNIX -> AF_INET. */
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 0;
+ redir.args[0] = AF_INET;
+ ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT, &redir);
+ if (ret < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ close(listener);
+ SKIP(return, "Kernel lacks SECCOMP_IOCTL_NOTIF_SEND_REDIRECT");
+ }
+ EXPECT_EQ(0, ret);
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ switch (WEXITSTATUS(status)) {
+ case 12:
+ TH_LOG("child exit 12: redirect ran past the outer USER_NOTIF filter");
+ break;
+ case 13:
+ TH_LOG("child exit 13: socket failed with unexpected errno (want ENOSYS)");
+ break;
+ default:
+ TH_LOG("child exit %d (unexpected)", WEXITSTATUS(status));
+ }
+ }
+
+ close(listener);
+}
+#endif /* __NR_socket */
+
+#ifdef __x86_64__
+/*
+ * Load-bearing ABI check: after SEND_REDIRECT, the trapped task's
+ * redirected arg register must be restored to its original value
+ * before user-mode code resumes. The kernel's restore mechanism
+ * (task_work_add(TWA_RESUME) -> seccomp_redirect_restore_cb) is
+ * what guarantees this; without a test the property is just an
+ * assertion. Bypass libc's syscall() wrapper (which caller-saves
+ * arg values and would mask a restore bug) and capture the actual
+ * arg register immediately after the SYSCALL instruction.
+ *
+ * The child issues openat with RSI = sentinel_path. The supervisor
+ * SEND_REDIRECTs args[1] (RSI) to point into the pin. The kernel:
+ * - saves the original RSI into the knotif
+ * - writes the pin address into RSI via syscall_set_arguments()
+ * - runs the syscall (kernel reads path from the pin)
+ * - on syscall_exit_to_user_mode, fires task_work which calls
+ * syscall_set_arguments() again with the saved original
+ * - returns to user mode
+ *
+ * If task_work fires correctly, the child observes RSI == sentinel.
+ * If broken, RSI holds the pin address (the redirected value the
+ * kernel left in pt_regs).
+ */
+TEST(user_notification_pinned_memfd_abi)
+{
+ pid_t pid;
+ long ret;
+ int status, listener, memfd;
+ struct seccomp_notif req = {};
+ struct seccomp_notif_pin_install pin = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ char *sup_view;
+ const size_t PIN_SIZE = 4096;
+ const char *safe_path = "/dev/null";
+ /*
+ * The "sentinel" is a real string the child can also pass as
+ * the openat path. Its address is captured pre-syscall as RSI;
+ * post-syscall RSI must equal the same address.
+ */
+ static const char sentinel_path[] = "/seccomp_abi_sentinel";
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ memfd = make_pin_memfd(_metadata, "pin-abi", PIN_SIZE,
+ &sup_view, safe_path);
+
+ listener = user_notif_syscall(__NR_openat,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ register long r10_val asm("r10") = 0;
+ unsigned long rsi_after;
+ long fd;
+
+ asm volatile(
+ "syscall\n\t"
+ "mov %%rsi, %[after]"
+ : "=a"(fd), [after] "=&r"(rsi_after)
+ : "0"((long)__NR_openat),
+ "D"((long)AT_FDCWD),
+ "S"((unsigned long)sentinel_path),
+ "d"((long)O_RDONLY),
+ "r"(r10_val)
+ : "rcx", "r11", "memory"
+ );
+
+ if (fd < 0)
+ _exit(11);
+ /*
+ * Load-bearing check: RSI immediately post-SYSCALL must
+ * still be the sentinel pointer the child passed in. The
+ * kernel's REDIRECT-then-restore mechanism is the only
+ * thing that guarantees this; a broken restore would leave
+ * the pin address in RSI.
+ */
+ if (rsi_after != (unsigned long)sentinel_path)
+ _exit(12);
+ _exit(0);
+ }
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_openat);
+ EXPECT_EQ(req.data.args[1], (unsigned long)sentinel_path);
+
+ pin.id = req.id;
+ pin.memfd = memfd;
+ pin.target_addr = 0;
+ pin.size = PIN_SIZE;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_PIN_INSTALL, &pin)) {
+ if (errno == EINVAL) {
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ SKIP(goto cleanup,
+ "Kernel lacks pinned-memfd remote install");
+ }
+ }
+
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 1;
+ redir.ptr_mask = 1U << 1;
+ redir.memfd = memfd;
+ redir.ptr_len[1] = strlen(safe_path) + 1;
+ redir.args[1] = pin.target_addr;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT,
+ &redir)) {
+ kill(pid, SIGKILL);
+ }
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ switch (WEXITSTATUS(status)) {
+ case 11:
+ TH_LOG("child exit 11: openat returned -errno");
+ break;
+ case 12:
+ TH_LOG("child exit 12: ABI violation -- RSI not restored after redirect");
+ break;
+ default:
+ TH_LOG("child exit %d (unexpected)", WEXITSTATUS(status));
+ }
+ }
+
+cleanup:
+ munmap(sup_view, PIN_SIZE);
+ close(memfd);
+ close(listener);
+}
+
+static void redir_sigusr1_handler(int signo)
+{
+ /* _exit() is async-signal-safe; bail with a distinct code if the
+ * signal frame was clobbered so the handler sees the wrong signo.
+ */
+ if (signo != SIGUSR1)
+ _exit(12);
+}
+
+/*
+ * Regression test: a redirect's deferred arg-register restore must run
+ * before a signal frame is built, not after.
+ *
+ * The restore is queued as a TWA_RESUME task_work. get_signal() runs
+ * task_work_run() before it dequeues a signal, so the restore executes
+ * before handle_signal() builds the handler frame (regs->di = signo,
+ * regs->si = &siginfo, regs->dx = &ucontext): the trapped syscall's
+ * original argument values are back in pt_regs first and never clobber
+ * the frame. A broken restore that instead ran after the frame was
+ * built would overwrite those registers and enter the handler with a
+ * corrupted signal number.
+ *
+ * The child traps on pause(), the supervisor redirects arg0 (RDI), and
+ * then interrupts it with SIGUSR1. The handler must observe
+ * signo == SIGUSR1, not the leaked original RDI sentinel.
+ */
+TEST(user_notification_redirect_signal_abi)
+{
+ pid_t pid;
+ long ret;
+ int status, listener;
+ struct seccomp_notif req = {};
+ struct seccomp_notif_resp_redirect redir = {};
+ /* A recognizable original RDI the broken restore would leak in. */
+ const unsigned long RDI_SENTINEL = 0x5a5a5a5aUL;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ listener = user_notif_syscall(__NR_pause,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER |
+ SECCOMP_FILTER_FLAG_REDIRECT);
+ ASSERT_GE(listener, 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ struct sigaction sa = {
+ .sa_handler = redir_sigusr1_handler,
+ };
+ long rc;
+
+ if (sigaction(SIGUSR1, &sa, NULL))
+ _exit(10);
+
+ /* Raw pause() carrying a controlled RDI sentinel. */
+ asm volatile(
+ "syscall"
+ : "=a"(rc)
+ : "0"((long)__NR_pause),
+ "D"(RDI_SENTINEL)
+ : "rcx", "r11", "memory");
+
+ if (rc != -EINTR)
+ _exit(11);
+ _exit(0);
+ }
+
+ ASSERT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req));
+ EXPECT_EQ(req.data.nr, __NR_pause);
+ EXPECT_EQ(req.data.args[0], RDI_SENTINEL);
+
+ /* Redirect arg0 (non-pointer); this arms the original-RDI restore. */
+ redir.id = req.id;
+ redir.flags = SECCOMP_REDIRECT_FLAG_CONTINUE;
+ redir.args_mask = 1U << 0;
+ redir.args[0] = 0;
+ EXPECT_EQ(0, ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND_REDIRECT,
+ &redir)) {
+ int einval = (errno == EINVAL);
+
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+ if (einval)
+ SKIP(goto cleanup,
+ "Kernel lacks SECCOMP_IOCTL_NOTIF_SEND_REDIRECT");
+ goto cleanup;
+ }
+
+ usleep(100000);
+ EXPECT_EQ(0, kill(pid, SIGUSR1));
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status)) {
+ switch (WEXITSTATUS(status)) {
+ case 10:
+ TH_LOG("child exit 10: sigaction failed");
+ break;
+ case 11:
+ TH_LOG("child exit 11: pause() did not return -EINTR");
+ break;
+ case 12:
+ TH_LOG("child exit 12: handler saw wrong signo (frame clobbered)");
+ break;
+ default:
+ TH_LOG("child exit %d (unexpected)", WEXITSTATUS(status));
+ }
+ }
+
+cleanup:
+ close(listener);
+}
+#endif /* __x86_64__ */
+
#ifndef SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP
#define SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP (1UL << 0)
#define SECCOMP_IOCTL_NOTIF_SET_FLAGS SECCOMP_IOW(4, __u64)
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread