* [PATCH v4 1/2] mm/memfd: Refactor and cleanup the logic in memfd_create()
2025-01-10 16:58 [PATCH v4 0/2] Cleanup for memfd_create() Isaac J. Manjarres
@ 2025-01-10 16:58 ` Isaac J. Manjarres
2025-01-10 16:59 ` [PATCH v4 2/2] mm/memfd: Use strncpy_from_user() to read memfd name Isaac J. Manjarres
1 sibling, 0 replies; 3+ messages in thread
From: Isaac J. Manjarres @ 2025-01-10 16:58 UTC (permalink / raw)
To: lorenzo.stoakes, Andrew Morton
Cc: kaleshsingh, jstultz, aliceryhl, surenb, Isaac J. Manjarres,
kernel-team, linux-mm, linux-kernel
memfd_create() is a pretty busy function that could be easier to read
if some of the logic was split out into helper functions.
Therefore, split the flags sanitization, name allocation, and file
structure allocation into their own helper functions.
No functional change.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com>
---
mm/memfd.c | 81 ++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 58 insertions(+), 23 deletions(-)
diff --git a/mm/memfd.c b/mm/memfd.c
index 5f5a23c9051d..04d9e2a23df8 100644
--- a/mm/memfd.c
+++ b/mm/memfd.c
@@ -369,15 +369,9 @@ int memfd_check_seals_mmap(struct file *file, unsigned long *vm_flags_ptr)
return err;
}
-SYSCALL_DEFINE2(memfd_create,
- const char __user *, uname,
- unsigned int, flags)
+static int sanitize_flags(unsigned int *flags_ptr)
{
- unsigned int *file_seals;
- struct file *file;
- int fd, error;
- char *name;
- long len;
+ unsigned int flags = *flags_ptr;
if (!(flags & MFD_HUGETLB)) {
if (flags & ~(unsigned int)MFD_ALL_FLAGS)
@@ -393,20 +387,25 @@ SYSCALL_DEFINE2(memfd_create,
if ((flags & MFD_EXEC) && (flags & MFD_NOEXEC_SEAL))
return -EINVAL;
- error = check_sysctl_memfd_noexec(&flags);
- if (error < 0)
- return error;
+ return check_sysctl_memfd_noexec(flags_ptr);
+}
+
+static char *alloc_name(const char __user *uname)
+{
+ int error;
+ char *name;
+ long len;
/* length includes terminating zero */
len = strnlen_user(uname, MFD_NAME_MAX_LEN + 1);
if (len <= 0)
- return -EFAULT;
+ return ERR_PTR(-EFAULT);
if (len > MFD_NAME_MAX_LEN + 1)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
name = kmalloc(len + MFD_NAME_PREFIX_LEN, GFP_KERNEL);
if (!name)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
strcpy(name, MFD_NAME_PREFIX);
if (copy_from_user(&name[MFD_NAME_PREFIX_LEN], uname, len)) {
@@ -420,23 +419,28 @@ SYSCALL_DEFINE2(memfd_create,
goto err_name;
}
- fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
- if (fd < 0) {
- error = fd;
- goto err_name;
- }
+ return name;
+
+err_name:
+ kfree(name);
+ return ERR_PTR(error);
+}
+
+static struct file *alloc_file(const char *name, unsigned int flags)
+{
+ unsigned int *file_seals;
+ struct file *file;
if (flags & MFD_HUGETLB) {
file = hugetlb_file_setup(name, 0, VM_NORESERVE,
HUGETLB_ANONHUGE_INODE,
(flags >> MFD_HUGE_SHIFT) &
MFD_HUGE_MASK);
- } else
+ } else {
file = shmem_file_setup(name, 0, VM_NORESERVE);
- if (IS_ERR(file)) {
- error = PTR_ERR(file);
- goto err_fd;
}
+ if (IS_ERR(file))
+ return file;
file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
file->f_flags |= O_LARGEFILE;
@@ -456,6 +460,37 @@ SYSCALL_DEFINE2(memfd_create,
*file_seals &= ~F_SEAL_SEAL;
}
+ return file;
+}
+
+SYSCALL_DEFINE2(memfd_create,
+ const char __user *, uname,
+ unsigned int, flags)
+{
+ struct file *file;
+ int fd, error;
+ char *name;
+
+ error = sanitize_flags(&flags);
+ if (error < 0)
+ return error;
+
+ name = alloc_name(uname);
+ if (IS_ERR(name))
+ return PTR_ERR(name);
+
+ fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0);
+ if (fd < 0) {
+ error = fd;
+ goto err_name;
+ }
+
+ file = alloc_file(name, flags);
+ if (IS_ERR(file)) {
+ error = PTR_ERR(file);
+ goto err_fd;
+ }
+
fd_install(fd, file);
kfree(name);
return fd;
--
2.47.1.613.gc27f4b7a9f-goog
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH v4 2/2] mm/memfd: Use strncpy_from_user() to read memfd name
2025-01-10 16:58 [PATCH v4 0/2] Cleanup for memfd_create() Isaac J. Manjarres
2025-01-10 16:58 ` [PATCH v4 1/2] mm/memfd: Refactor and cleanup the logic in memfd_create() Isaac J. Manjarres
@ 2025-01-10 16:59 ` Isaac J. Manjarres
1 sibling, 0 replies; 3+ messages in thread
From: Isaac J. Manjarres @ 2025-01-10 16:59 UTC (permalink / raw)
To: lorenzo.stoakes, Andrew Morton
Cc: kaleshsingh, jstultz, aliceryhl, surenb, Isaac J. Manjarres,
kernel-team, linux-mm, linux-kernel
The existing logic uses strnlen_user() to calculate the length of the
memfd name from userspace and then copies the string into a buffer using
copy_from_user(). This is error-prone, as the string length
could have changed between the time when it was calculated and when the
string was copied. The existing logic handles this by ensuring that the
last byte in the buffer is the terminating zero.
This handling is contrived and can better be handled by using
strncpy_from_user(), which gets the length of the string and copies
it in one shot. Therefore, simplify the logic for copying the memfd
name by using strncpy_from_user().
No functional change.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com>
---
mm/memfd.c | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/mm/memfd.c b/mm/memfd.c
index 04d9e2a23df8..37f7be57c2f5 100644
--- a/mm/memfd.c
+++ b/mm/memfd.c
@@ -396,26 +396,18 @@ static char *alloc_name(const char __user *uname)
char *name;
long len;
- /* length includes terminating zero */
- len = strnlen_user(uname, MFD_NAME_MAX_LEN + 1);
- if (len <= 0)
- return ERR_PTR(-EFAULT);
- if (len > MFD_NAME_MAX_LEN + 1)
- return ERR_PTR(-EINVAL);
-
- name = kmalloc(len + MFD_NAME_PREFIX_LEN, GFP_KERNEL);
+ name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
if (!name)
return ERR_PTR(-ENOMEM);
strcpy(name, MFD_NAME_PREFIX);
- if (copy_from_user(&name[MFD_NAME_PREFIX_LEN], uname, len)) {
+ /* returned length does not include terminating zero */
+ len = strncpy_from_user(&name[MFD_NAME_PREFIX_LEN], uname, MFD_NAME_MAX_LEN + 1);
+ if (len < 0) {
error = -EFAULT;
goto err_name;
- }
-
- /* terminating-zero may have changed after strnlen_user() returned */
- if (name[len + MFD_NAME_PREFIX_LEN - 1]) {
- error = -EFAULT;
+ } else if (len > MFD_NAME_MAX_LEN) {
+ error = -EINVAL;
goto err_name;
}
--
2.47.1.613.gc27f4b7a9f-goog
^ permalink raw reply [flat|nested] 3+ messages in thread