@@ -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
@@ -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
@@ -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);
@@ -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
@@ -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
@@ -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 */
@@ -362,6 +362,7 @@ COND_SYSCALL(memfd_secret);
/* memfd_restricted */
COND_SYSCALL(memfd_restricted);
+COND_SYSCALL(memfd_restricted_bind);
/*
* Architecture specific weak syscall entries.
@@ -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);
+}
@@ -46,6 +46,7 @@ cat << EOF
#ifndef __ARCH_WANT_MEMFD_RESTRICTED
#define __IGNORE_memfd_restricted
+#define __IGNORE_memfd_restricted_bind
#endif
/* Missing flags argument */
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@google.com> --- 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(-)