[RFC PATCH 5/6] mm: restrictedmem: Add memfd_restricted_bind() syscall

From: Ackerley Tng
Date: Thu Apr 13 2023 - 20:12:21 EST


memfd_restricted_bind() sets the NUMA memory policy, which consists of
a policy mode and zero or more nodes, for an offset within a
restrictedmem file with file descriptor fd and continuing for len
bytes.

This is intended to be like mbind() but specially for restrictedmem
files, which cannot be mmap()ed into userspace and hence has no memory
addresses that can be used with mbind().

Unlike mbind(), memfd_restricted_bind() will override any existing
memory policy if a new memory policy is defined for the same ranges.

For now, memfd_restricted_bind() does not perform migrations and no
flags are supported.

This syscall is specialised just for restrictedmem files because this
functionality is not required by other files.

Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
---
arch/x86/entry/syscalls/syscall_32.tbl | 1 +
arch/x86/entry/syscalls/syscall_64.tbl | 1 +
include/linux/mempolicy.h | 2 +-
include/linux/syscalls.h | 5 ++
include/uapi/asm-generic/unistd.h | 5 +-
include/uapi/linux/mempolicy.h | 7 ++-
kernel/sys_ni.c | 1 +
mm/restrictedmem.c | 75 ++++++++++++++++++++++++++
scripts/checksyscalls.sh | 1 +
9 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index dc70ba90247e..c94e9ce46cc3 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -456,3 +456,4 @@
449 i386 futex_waitv sys_futex_waitv
450 i386 set_mempolicy_home_node sys_set_mempolicy_home_node
451 i386 memfd_restricted sys_memfd_restricted
+452 i386 memfd_restricted_bind sys_memfd_restricted_bind
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 06516abc8318..6bd86b45d63a 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -373,6 +373,7 @@
449 common futex_waitv sys_futex_waitv
450 common set_mempolicy_home_node sys_set_mempolicy_home_node
451 common memfd_restricted sys_memfd_restricted
+452 common memfd_restricted_bind sys_memfd_restricted_bind

#
# Due to a historical design error, certain syscalls are numbered differently
diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h
index 15facd9de087..af62233df0c0 100644
--- a/include/linux/mempolicy.h
+++ b/include/linux/mempolicy.h
@@ -126,7 +126,7 @@ struct shared_policy {

int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst);
struct mempolicy *mpol_create(
- unsigned long mode, const unsigned long __user *nmask, unsigned long maxnode)
+ unsigned long mode, const unsigned long __user *nmask, unsigned long maxnode);
void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol);
int __mpol_set_shared_policy(struct shared_policy *info, struct mempolicy *mpol,
unsigned long pgoff_start, unsigned long npages);
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 660be0bf89d5..852b202d3837 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -1059,6 +1059,11 @@ asmlinkage long sys_set_mempolicy_home_node(unsigned long start, unsigned long l
unsigned long home_node,
unsigned long flags);
asmlinkage long sys_memfd_restricted(unsigned int flags);
+asmlinkage long sys_memfd_restricted_bind(int fd, struct file_range __user *range,
+ unsigned long mode,
+ const unsigned long __user *nmask,
+ unsigned long maxnode,
+ unsigned int flags);

/*
* Architecture-specific system calls
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index e2ea7cd964f8..b5a1385bb4a7 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -889,10 +889,13 @@ __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node)
#ifdef __ARCH_WANT_MEMFD_RESTRICTED
#define __NR_memfd_restricted 451
__SYSCALL(__NR_memfd_restricted, sys_memfd_restricted)
+
+#define __NR_memfd_restricted_bind 452
+__SYSCALL(__NR_memfd_restricted_bind, sys_memfd_restricted_bind)
#endif

#undef __NR_syscalls
-#define __NR_syscalls 452
+#define __NR_syscalls 453

/*
* 32 bit systems traditionally used different
diff --git a/include/uapi/linux/mempolicy.h b/include/uapi/linux/mempolicy.h
index 046d0ccba4cd..979499abd253 100644
--- a/include/uapi/linux/mempolicy.h
+++ b/include/uapi/linux/mempolicy.h
@@ -6,9 +6,9 @@
#ifndef _UAPI_LINUX_MEMPOLICY_H
#define _UAPI_LINUX_MEMPOLICY_H

+#include <asm-generic/posix_types.h>
#include <linux/errno.h>

-
/*
* Both the MPOL_* mempolicy mode and the MPOL_F_* optional mode flags are
* passed by the user to either set_mempolicy() or mbind() in an 'int' actual.
@@ -72,4 +72,9 @@ enum {
#define RECLAIM_WRITE (1<<1) /* Writeout pages during reclaim */
#define RECLAIM_UNMAP (1<<2) /* Unmap pages during reclaim */

+struct file_range {
+ __kernel_loff_t offset;
+ __kernel_size_t len;
+};
+
#endif /* _UAPI_LINUX_MEMPOLICY_H */
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 7c4a32cbd2e7..db24d3fe6dc5 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -362,6 +362,7 @@ COND_SYSCALL(memfd_secret);

/* memfd_restricted */
COND_SYSCALL(memfd_restricted);
+COND_SYSCALL(memfd_restricted_bind);

/*
* Architecture specific weak syscall entries.
diff --git a/mm/restrictedmem.c b/mm/restrictedmem.c
index 55e99e6c09a1..9c249722c61b 100644
--- a/mm/restrictedmem.c
+++ b/mm/restrictedmem.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/mempolicy.h>
#include "linux/sbitmap.h"
#include <linux/pagemap.h>
#include <linux/pseudo_fs.h>
@@ -359,3 +360,77 @@ int restrictedmem_get_page(struct file *file, pgoff_t offset,
return 0;
}
EXPORT_SYMBOL_GPL(restrictedmem_get_page);
+
+static int restrictedmem_set_shared_policy(
+ struct file *file, loff_t start, size_t len, struct mempolicy *mpol)
+{
+ struct restrictedmem *rm;
+ unsigned long end;
+
+ if (!PAGE_ALIGNED(start))
+ return -EINVAL;
+
+ len = PAGE_ALIGN(len);
+ end = start + len;
+
+ if (end < start)
+ return -EINVAL;
+ if (end == start)
+ return 0;
+
+ rm = file->f_mapping->private_data;
+ return __mpol_set_shared_policy(shmem_shared_policy(rm->memfd), mpol,
+ start >> PAGE_SHIFT, len >> PAGE_SHIFT);
+}
+
+static long do_memfd_restricted_bind(
+ int fd, loff_t offset, size_t len,
+ unsigned long mode, const unsigned long __user *nmask,
+ unsigned long maxnode, unsigned int flags)
+{
+ long ret;
+ struct fd f;
+ struct mempolicy *mpol;
+
+ /* None of the flags are supported */
+ if (flags)
+ return -EINVAL;
+
+ f = fdget_raw(fd);
+ if (!f.file)
+ return -EBADF;
+
+ if (!file_is_restrictedmem(f.file))
+ return -EINVAL;
+
+ mpol = mpol_create(mode, nmask, maxnode);
+ if (IS_ERR(mpol)) {
+ ret = PTR_ERR(mpol);
+ goto out;
+ }
+
+ ret = restrictedmem_set_shared_policy(f.file, offset, len, mpol);
+
+ mpol_put(mpol);
+
+out:
+ fdput(f);
+
+ return ret;
+}
+
+SYSCALL_DEFINE6(memfd_restricted_bind, int, fd, struct file_range __user *, range,
+ unsigned long, mode, const unsigned long __user *, nmask,
+ unsigned long, maxnode, unsigned int, flags)
+{
+ loff_t offset;
+ size_t len;
+
+ if (unlikely(get_user(offset, &range->offset)))
+ return -EFAULT;
+ if (unlikely(get_user(len, &range->len)))
+ return -EFAULT;
+
+ return do_memfd_restricted_bind(fd, offset, len, mode, nmask,
+ maxnode, flags);
+}
diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh
index 3c4d2508226a..e253529cf1ec 100755
--- a/scripts/checksyscalls.sh
+++ b/scripts/checksyscalls.sh
@@ -46,6 +46,7 @@ cat << EOF

#ifndef __ARCH_WANT_MEMFD_RESTRICTED
#define __IGNORE_memfd_restricted
+#define __IGNORE_memfd_restricted_bind
#endif

/* Missing flags argument */
--
2.40.0.634.g4ca3ef3211-goog