summaryrefslogtreecommitdiff
path: root/fs/exec.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 16:58:28 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-09 16:58:28 -0800
commit26c9342bb761e463774a64fb6210b4f95f5bc035 (patch)
tree486d504cf5dcfe041eba4d9d5eb468a5a7b81566 /fs/exec.c
parent8a5203c630c67d578975ff237413f5e0b5000af8 (diff)
parent0787a93baa1aab9fd0cb8500105d11d3d3a58f7a (diff)
downloadlinux-next-26c9342bb761e463774a64fb6210b4f95f5bc035.tar.gz
linux-next-26c9342bb761e463774a64fb6210b4f95f5bc035.zip
Merge tag 'pull-filename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs 'struct filename' updates from Al Viro: "[Mostly] sanitize struct filename handling" * tag 'pull-filename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (68 commits) sysfs(2): fs_index() argument is _not_ a pathname alpha: switch osf_mount() to strndup_user() ksmbd: use CLASS(filename_kernel) mqueue: switch to CLASS(filename) user_statfs(): switch to CLASS(filename) statx: switch to CLASS(filename_maybe_null) quotactl_block(): switch to CLASS(filename) chroot(2): switch to CLASS(filename) move_mount(2): switch to CLASS(filename_maybe_null) namei.c: switch user pathname imports to CLASS(filename{,_flags}) namei.c: convert getname_kernel() callers to CLASS(filename_kernel) do_f{chmod,chown,access}at(): use CLASS(filename_uflags) do_readlinkat(): switch to CLASS(filename_flags) do_sys_truncate(): switch to CLASS(filename) do_utimes_path(): switch to CLASS(filename_uflags) chdir(2): unspaghettify a bit... do_fchownat(): unspaghettify a bit... fspick(2): use CLASS(filename_flags) name_to_handle_at(): use CLASS(filename_uflags) vfs_open_tree(): use CLASS(filename_uflags) ...
Diffstat (limited to 'fs/exec.c')
-rw-r--r--fs/exec.c178
1 files changed, 54 insertions, 124 deletions
diff --git a/fs/exec.c b/fs/exec.c
index d0606e53376f..2e3a6593c6fd 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -777,10 +777,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
return ERR_PTR(-EINVAL);
if (flags & AT_SYMLINK_NOFOLLOW)
open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW;
- if (flags & AT_EMPTY_PATH)
- open_exec_flags.lookup_flags |= LOOKUP_EMPTY;
- file = do_filp_open(fd, name, &open_exec_flags);
+ file = do_file_open(fd, name, &open_exec_flags);
if (IS_ERR(file))
return file;
@@ -815,14 +813,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
*/
struct file *open_exec(const char *name)
{
- struct filename *filename = getname_kernel(name);
- struct file *f = ERR_CAST(filename);
-
- if (!IS_ERR(filename)) {
- f = do_open_execat(AT_FDCWD, filename, 0);
- putname(filename);
- }
- return f;
+ CLASS(filename_kernel, filename)(name);
+ return do_open_execat(AT_FDCWD, filename, 0);
}
EXPORT_SYMBOL(open_exec);
@@ -1471,6 +1463,9 @@ out_free:
return ERR_PTR(retval);
}
+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)
+
int bprm_change_interp(const char *interp, struct linux_binprm *bprm)
{
/* If a binfmt changed the interp, free it first. */
@@ -1785,12 +1780,8 @@ static int do_execveat_common(int fd, struct filename *filename,
struct user_arg_ptr envp,
int flags)
{
- struct linux_binprm *bprm;
int retval;
- if (IS_ERR(filename))
- return PTR_ERR(filename);
-
/*
* We move the actual failure in case of RLIMIT_NPROC excess from
* set*uid() to execve() because too many poorly written programs
@@ -1798,47 +1789,43 @@ static int do_execveat_common(int fd, struct filename *filename,
* whether NPROC limit is still exceeded.
*/
if ((current->flags & PF_NPROC_EXCEEDED) &&
- is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) {
- retval = -EAGAIN;
- goto out_ret;
- }
+ is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC)))
+ return -EAGAIN;
/* We're below the limit (still or again), so we don't want to make
* further execve() calls fail. */
current->flags &= ~PF_NPROC_EXCEEDED;
- bprm = alloc_bprm(fd, filename, flags);
- if (IS_ERR(bprm)) {
- retval = PTR_ERR(bprm);
- goto out_ret;
- }
+ CLASS(bprm, bprm)(fd, filename, flags);
+ if (IS_ERR(bprm))
+ return PTR_ERR(bprm);
retval = count(argv, MAX_ARG_STRINGS);
if (retval < 0)
- goto out_free;
+ return retval;
bprm->argc = retval;
retval = count(envp, MAX_ARG_STRINGS);
if (retval < 0)
- goto out_free;
+ return retval;
bprm->envc = retval;
retval = bprm_stack_limits(bprm);
if (retval < 0)
- goto out_free;
+ return retval;
retval = copy_string_kernel(bprm->filename, bprm);
if (retval < 0)
- goto out_free;
+ return retval;
bprm->exec = bprm->p;
retval = copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
- goto out_free;
+ return retval;
retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
- goto out_free;
+ return retval;
/*
* When argv is empty, add an empty string ("") as argv[0] to
@@ -1849,133 +1836,61 @@ static int do_execveat_common(int fd, struct filename *filename,
if (bprm->argc == 0) {
retval = copy_string_kernel("", bprm);
if (retval < 0)
- goto out_free;
+ return retval;
bprm->argc = 1;
pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
current->comm, bprm->filename);
}
- retval = bprm_execve(bprm);
-out_free:
- free_bprm(bprm);
-
-out_ret:
- putname(filename);
- return retval;
+ return bprm_execve(bprm);
}
int kernel_execve(const char *kernel_filename,
const char *const *argv, const char *const *envp)
{
- struct filename *filename;
- struct linux_binprm *bprm;
- int fd = AT_FDCWD;
int retval;
/* It is non-sense for kernel threads to call execve */
if (WARN_ON_ONCE(current->flags & PF_KTHREAD))
return -EINVAL;
- filename = getname_kernel(kernel_filename);
- if (IS_ERR(filename))
- return PTR_ERR(filename);
-
- bprm = alloc_bprm(fd, filename, 0);
- if (IS_ERR(bprm)) {
- retval = PTR_ERR(bprm);
- goto out_ret;
- }
+ CLASS(filename_kernel, filename)(kernel_filename);
+ CLASS(bprm, bprm)(AT_FDCWD, filename, 0);
+ if (IS_ERR(bprm))
+ return PTR_ERR(bprm);
retval = count_strings_kernel(argv);
if (WARN_ON_ONCE(retval == 0))
- retval = -EINVAL;
+ return -EINVAL;
if (retval < 0)
- goto out_free;
+ return retval;
bprm->argc = retval;
retval = count_strings_kernel(envp);
if (retval < 0)
- goto out_free;
+ return retval;
bprm->envc = retval;
retval = bprm_stack_limits(bprm);
if (retval < 0)
- goto out_free;
+ return retval;
retval = copy_string_kernel(bprm->filename, bprm);
if (retval < 0)
- goto out_free;
+ return retval;
bprm->exec = bprm->p;
retval = copy_strings_kernel(bprm->envc, envp, bprm);
if (retval < 0)
- goto out_free;
+ return retval;
retval = copy_strings_kernel(bprm->argc, argv, bprm);
if (retval < 0)
- goto out_free;
-
- retval = bprm_execve(bprm);
-out_free:
- free_bprm(bprm);
-out_ret:
- putname(filename);
- return retval;
-}
-
-static int do_execve(struct filename *filename,
- const char __user *const __user *__argv,
- const char __user *const __user *__envp)
-{
- struct user_arg_ptr argv = { .ptr.native = __argv };
- struct user_arg_ptr envp = { .ptr.native = __envp };
- return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
-}
-
-static int do_execveat(int fd, struct filename *filename,
- const char __user *const __user *__argv,
- const char __user *const __user *__envp,
- int flags)
-{
- struct user_arg_ptr argv = { .ptr.native = __argv };
- struct user_arg_ptr envp = { .ptr.native = __envp };
-
- return do_execveat_common(fd, filename, argv, envp, flags);
-}
-
-#ifdef CONFIG_COMPAT
-static int compat_do_execve(struct filename *filename,
- const compat_uptr_t __user *__argv,
- const compat_uptr_t __user *__envp)
-{
- struct user_arg_ptr argv = {
- .is_compat = true,
- .ptr.compat = __argv,
- };
- struct user_arg_ptr envp = {
- .is_compat = true,
- .ptr.compat = __envp,
- };
- return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
-}
+ return retval;
-static int compat_do_execveat(int fd, struct filename *filename,
- const compat_uptr_t __user *__argv,
- const compat_uptr_t __user *__envp,
- int flags)
-{
- struct user_arg_ptr argv = {
- .is_compat = true,
- .ptr.compat = __argv,
- };
- struct user_arg_ptr envp = {
- .is_compat = true,
- .ptr.compat = __envp,
- };
- return do_execveat_common(fd, filename, argv, envp, flags);
+ return bprm_execve(bprm);
}
-#endif
void set_binfmt(struct linux_binfmt *new)
{
@@ -2001,12 +1916,19 @@ 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,
const char __user *const __user *, envp)
{
- return do_execve(getname(filename), argv, envp);
+ CLASS(filename, name)(filename);
+ return do_execveat_common(AT_FDCWD, name,
+ native_arg(argv), native_arg(envp), 0);
}
SYSCALL_DEFINE5(execveat,
@@ -2015,17 +1937,25 @@ SYSCALL_DEFINE5(execveat,
const char __user *const __user *, envp,
int, flags)
{
- return do_execveat(fd,
- getname_uflags(filename, flags),
- argv, envp, flags);
+ CLASS(filename_uflags, name)(filename, flags);
+ return do_execveat_common(fd, name,
+ native_arg(argv), native_arg(envp), flags);
}
#ifdef CONFIG_COMPAT
+
+static inline struct user_arg_ptr compat_arg(const compat_uptr_t __user *p)
+{
+ return (struct user_arg_ptr){.is_compat = true, .ptr.compat = p};
+}
+
COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename,
const compat_uptr_t __user *, argv,
const compat_uptr_t __user *, envp)
{
- return compat_do_execve(getname(filename), argv, envp);
+ CLASS(filename, name)(filename);
+ return do_execveat_common(AT_FDCWD, name,
+ compat_arg(argv), compat_arg(envp), 0);
}
COMPAT_SYSCALL_DEFINE5(execveat, int, fd,
@@ -2034,9 +1964,9 @@ COMPAT_SYSCALL_DEFINE5(execveat, int, fd,
const compat_uptr_t __user *, envp,
int, flags)
{
- return compat_do_execveat(fd,
- getname_uflags(filename, flags),
- argv, envp, flags);
+ CLASS(filename_uflags, name)(filename, flags);
+ return do_execveat_common(fd, name,
+ compat_arg(argv), compat_arg(envp), flags);
}
#endif