Commit 26c9342b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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)
  ...
parents 8a5203c6 0787a93b
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -1340,3 +1340,14 @@ The ->setlease() file_operation must now be explicitly set in order to provide
support for leases. When set to NULL, the kernel will now return -EINVAL to
attempts to set a lease. Filesystems that wish to use the kernel-internal lease
implementation should set it to generic_setlease().

---

**mandatory**

fs/namei.c primitives that consume filesystem references (do_renameat2(),
do_linkat(), do_symlinkat(), do_mkdirat(), do_mknodat(), do_unlinkat()
and do_rmdir()) are gone; they are replaced with non-consuming analogues
(filename_renameat2(), etc.)
Callers are adjusted - responsibility for dropping the filenames belongs
to them now.
+11 −23
Original line number Diff line number Diff line
@@ -454,42 +454,30 @@ static int
osf_ufs_mount(const char __user *dirname,
	      struct ufs_args __user *args, int flags)
{
	int retval;
	struct cdfs_args tmp;
	struct filename *devname;
	struct ufs_args tmp;
	char *devname __free(kfree) = NULL;

	retval = -EFAULT;
	if (copy_from_user(&tmp, args, sizeof(tmp)))
		goto out;
	devname = getname(tmp.devname);
	retval = PTR_ERR(devname);
		return -EFAULT;
	devname = strndup_user(tmp.devname, PATH_MAX);
	if (IS_ERR(devname))
		goto out;
	retval = do_mount(devname->name, dirname, "ext2", flags, NULL);
	putname(devname);
 out:
	return retval;
		return PTR_ERR(devname);
	return do_mount(devname, dirname, "ext2", flags, NULL);
}

static int
osf_cdfs_mount(const char __user *dirname,
	       struct cdfs_args __user *args, int flags)
{
	int retval;
	struct cdfs_args tmp;
	struct filename *devname;
	char *devname __free(kfree) = NULL;

	retval = -EFAULT;
	if (copy_from_user(&tmp, args, sizeof(tmp)))
		goto out;
	devname = getname(tmp.devname);
	retval = PTR_ERR(devname);
		return -EFAULT;
	devname = strndup_user(tmp.devname, PATH_MAX);
	if (IS_ERR(devname))
		goto out;
	retval = do_mount(devname->name, dirname, "iso9660", flags, NULL);
	putname(devname);
 out:
	return retval;
		return PTR_ERR(devname);
	return do_mount(devname, dirname, "iso9660", flags, NULL);
}

static int
+2 −1
Original line number Diff line number Diff line
@@ -895,11 +895,12 @@ static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,
	 * privs and don't want to unlink another user's coredump.
	 */
	if (!coredump_force_suid_safe(cprm)) {
		CLASS(filename_kernel, name)(cn->corename);
		/*
		 * If it doesn't exist, that's fine. If there's some
		 * other problem, we'll catch it at the filp_open().
		 */
		do_unlinkat(AT_FDCWD, getname_kernel(cn->corename));
		filename_unlinkat(AT_FDCWD, name);
	}

	/*
+1 −7
Original line number Diff line number Diff line
@@ -3297,10 +3297,6 @@ static void __init dcache_init(void)
	runtime_const_init(ptr, dentry_hashtable);
}

/* SLAB cache for __getname() consumers */
struct kmem_cache *names_cachep __ro_after_init;
EXPORT_SYMBOL(names_cachep);

void __init vfs_caches_init_early(void)
{
	int i;
@@ -3314,9 +3310,7 @@ void __init vfs_caches_init_early(void)

void __init vfs_caches_init(void)
{
	names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0,
			SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL);

	filename_init();
	dcache_init();
	inode_init();
	files_init();
+54 −124
Original line number Diff line number Diff line
@@ -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 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
	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,134 +1836,62 @@ 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);
	return bprm_execve(bprm);
}

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);
}

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);
}
#endif

void set_binfmt(struct linux_binfmt *new)
{
	struct mm_struct *mm = current->mm;
@@ -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

Loading