[RFC PATCH v1 02/13] exec: add an internal helper for opened executables

Li Chen <[email protected]>
Newsgroups org.kernel.vger.linux-api,org.kernel.vger.linux-arch,org.kernel.vger.linux-doc,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kvack.linux-mm
Message-ID <[email protected]>
Split alloc_bprm_file() from alloc_bprm() so internal callers can build
a linux_binprm from an executable file that they already opened.
Add kernel_execveat_file() for in-kernel users that need to execute an
opened file while still using the normal execve credential, LSM, and
binary-format path.

Signed-off-by: Li Chen <[email protected]>
---
 fs/exec.c               | 78 +++++++++++++++++++++++++++++++++++------
 include/linux/binfmts.h |  4 +++
 2 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index 53f7b18d2b1ea..5b91a9b208a77 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1392,16 +1392,13 @@ static void free_bprm(struct linux_binprm *bprm)
 	kfree(bprm);
 }
 
-static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int flags)
+static struct linux_binprm *alloc_bprm_file(struct file *file,
+					    struct filename *filename,
+					    int fd, int flags)
 {
 	struct linux_binprm *bprm;
-	struct file *file;
 	int retval = -ENOMEM;
 
-	file = do_open_execat(fd, filename, flags);
-	if (IS_ERR(file))
-		return ERR_CAST(file);
-
 	bprm = kzalloc_obj(*bprm);
 	if (!bprm) {
 		do_close_execat(file);
@@ -1463,6 +1460,17 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
 	return ERR_PTR(retval);
 }
 
+static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int flags)
+{
+	struct file *file;
+
+	file = do_open_execat(fd, filename, flags);
+	if (IS_ERR(file))
+		return ERR_CAST(file);
+
+	return alloc_bprm_file(file, filename, fd, flags);
+}
+
 DEFINE_CLASS(bprm, struct linux_binprm *, if (!IS_ERR(_T)) free_bprm(_T),
 	alloc_bprm(fd, name, flags), int fd, struct filename *name, int flags)
 
@@ -1901,6 +1909,59 @@ int kernel_execve(const char *kernel_filename,
 	return bprm_execve(bprm);
 }
 
+static inline struct user_arg_ptr native_arg(const char __user *const __user *p)
+{
+	return (struct user_arg_ptr){.ptr.native = p};
+}
+
+static int do_execveat_file_common(struct file *file, struct filename *filename,
+				   struct user_arg_ptr argv,
+				   struct user_arg_ptr envp, int flags)
+{
+	struct linux_binprm *bprm;
+	struct file *exec_file;
+	int retval;
+
+	if (flags & ~AT_EMPTY_PATH)
+		return -EINVAL;
+
+	if ((current->flags & PF_NPROC_EXCEEDED) &&
+	    is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC)))
+		return -EAGAIN;
+
+	current->flags &= ~PF_NPROC_EXCEEDED;
+
+	retval = exe_file_deny_write_access(file);
+	if (retval)
+		return retval;
+	exec_file = get_file(file);
+
+	bprm = alloc_bprm_file(exec_file, filename, AT_FDCWD, flags);
+	if (IS_ERR(bprm))
+		return PTR_ERR(bprm);
+
+	retval = do_execveat_common_bprm(bprm, argv, envp);
+	free_bprm(bprm);
+	return retval;
+}
+
+int kernel_execveat_file(struct file *file, const char *filename,
+			 const void __user *argv,
+			 const void __user *envp,
+			 int flags)
+{
+	const char __user *const __user *user_argv;
+	const char __user *const __user *user_envp;
+
+	CLASS(filename_kernel, name)(filename);
+
+	user_argv = (const char __user *const __user *)argv;
+	user_envp = (const char __user *const __user *)envp;
+
+	return do_execveat_file_common(file, name, native_arg(user_argv),
+				       native_arg(user_envp), flags);
+}
+
 void set_binfmt(struct linux_binfmt *new)
 {
 	struct mm_struct *mm = current->mm;
@@ -1925,11 +1986,6 @@ void set_dumpable(struct mm_struct *mm, int value)
 	__mm_flags_set_mask_dumpable(mm, value);
 }
 
-static inline struct user_arg_ptr native_arg(const char __user *const __user *p)
-{
-	return (struct user_arg_ptr){.ptr.native = p};
-}
-
 SYSCALL_DEFINE3(execve,
 		const char __user *, filename,
 		const char __user *const __user *, argv,
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 65abd5ab8836c..c0715678c9a06 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -141,6 +141,10 @@ extern int transfer_args_to_stack(struct linux_binprm *bprm,
 				  unsigned long *sp_location);
 extern int bprm_change_interp(const char *interp, struct linux_binprm *bprm);
 int copy_string_kernel(const char *arg, struct linux_binprm *bprm);
+int kernel_execveat_file(struct file *file, const char *filename,
+			 const void __user *argv,
+			 const void __user *envp,
+			 int flags);
 extern void set_binfmt(struct linux_binfmt *new);
 extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);
 
-- 
2.52.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.