Commit b4c0800e authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull misc vfs fixes from Al Viro:
 "Assorted fixes all over the place; some of that is -stable fodder,
  some regressions from the last window"

* 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  ecryptfs_lookup_interpose(): lower_dentry->d_parent is not stable either
  ecryptfs_lookup_interpose(): lower_dentry->d_inode is not stable
  ecryptfs: fix unlink and rmdir in face of underlying fs modifications
  audit_get_nd(): don't unlock parent too early
  exportfs_decode_fh(): negative pinned may become positive without the parent locked
  cgroup: don't put ERR_PTR() into fc->root
  autofs: fix a leak in autofs_expire_indirect()
  aio: Fix io_pgetevents() struct __compat_aio_sigset layout
  fs/namespace.c: fix use-after-free of mount in mnt_warn_timestamp_expiry()
parents 96b95eff 762c6968
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -2179,7 +2179,7 @@ SYSCALL_DEFINE5(io_getevents_time32, __u32, ctx_id,
#ifdef CONFIG_COMPAT

struct __compat_aio_sigset {
	compat_sigset_t __user	*sigmask;
	compat_uptr_t		sigmask;
	compat_size_t		sigsetsize;
};

@@ -2193,7 +2193,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
		struct old_timespec32 __user *, timeout,
		const struct __compat_aio_sigset __user *, usig)
{
	struct __compat_aio_sigset ksig = { NULL, };
	struct __compat_aio_sigset ksig = { 0, };
	struct timespec64 t;
	bool interrupted;
	int ret;
@@ -2204,7 +2204,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
		return -EFAULT;

	ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize);
	ret = set_compat_user_sigmask(compat_ptr(ksig.sigmask), ksig.sigsetsize);
	if (ret)
		return ret;

@@ -2228,7 +2228,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
		struct __kernel_timespec __user *, timeout,
		const struct __compat_aio_sigset __user *, usig)
{
	struct __compat_aio_sigset ksig = { NULL, };
	struct __compat_aio_sigset ksig = { 0, };
	struct timespec64 t;
	bool interrupted;
	int ret;
@@ -2239,7 +2239,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
		return -EFAULT;

	ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize);
	ret = set_compat_user_sigmask(compat_ptr(ksig.sigmask), ksig.sigsetsize);
	if (ret)
		return ret;

+3 −2
Original line number Diff line number Diff line
@@ -459,9 +459,10 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
		 */
		how &= ~AUTOFS_EXP_LEAVES;
		found = should_expire(expired, mnt, timeout, how);
		if (!found || found != expired)
			/* Something has changed, continue */
		if (found != expired) { // something has changed, continue
			dput(found);
			goto next;
		}

		if (expired != dentry)
			dput(dentry);
+53 −31
Original line number Diff line number Diff line
@@ -128,12 +128,19 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
			      struct inode *inode)
{
	struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
	struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir);
	struct dentry *lower_dir_dentry;
	struct inode *lower_dir_inode;
	int rc;

	dget(lower_dentry);
	lower_dir_dentry = lock_parent(lower_dentry);
	lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
	lower_dir_inode = d_inode(lower_dir_dentry);
	inode_lock_nested(lower_dir_inode, I_MUTEX_PARENT);
	dget(lower_dentry);	// don't even try to make the lower negative
	if (lower_dentry->d_parent != lower_dir_dentry)
		rc = -EINVAL;
	else if (d_unhashed(lower_dentry))
		rc = -EINVAL;
	else
		rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
	if (rc) {
		printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
@@ -142,10 +149,11 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
	fsstack_copy_attr_times(dir, lower_dir_inode);
	set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
	inode->i_ctime = dir->i_ctime;
	d_drop(dentry);
out_unlock:
	unlock_dir(lower_dir_dentry);
	dput(lower_dentry);
	inode_unlock(lower_dir_inode);
	if (!rc)
		d_drop(dentry);
	return rc;
}

@@ -311,9 +319,9 @@ static int ecryptfs_i_size_read(struct dentry *dentry, struct inode *inode)
static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry,
				     struct dentry *lower_dentry)
{
	struct inode *inode, *lower_inode = d_inode(lower_dentry);
	struct path *path = ecryptfs_dentry_to_lower_path(dentry->d_parent);
	struct inode *inode, *lower_inode;
	struct ecryptfs_dentry_info *dentry_info;
	struct vfsmount *lower_mnt;
	int rc = 0;

	dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL);
@@ -322,16 +330,23 @@ static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry,
		return ERR_PTR(-ENOMEM);
	}

	lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
	fsstack_copy_attr_atime(d_inode(dentry->d_parent),
				d_inode(lower_dentry->d_parent));
				d_inode(path->dentry));
	BUG_ON(!d_count(lower_dentry));

	ecryptfs_set_dentry_private(dentry, dentry_info);
	dentry_info->lower_path.mnt = lower_mnt;
	dentry_info->lower_path.mnt = mntget(path->mnt);
	dentry_info->lower_path.dentry = lower_dentry;

	if (d_really_is_negative(lower_dentry)) {
	/*
	 * negative dentry can go positive under us here - its parent is not
	 * locked.  That's OK and that could happen just as we return from
	 * ecryptfs_lookup() anyway.  Just need to be careful and fetch
	 * ->d_inode only once - it's not stable here.
	 */
	lower_inode = READ_ONCE(lower_dentry->d_inode);

	if (!lower_inode) {
		/* We want to add because we couldn't find in lower */
		d_add(dentry, NULL);
		return NULL;
@@ -512,22 +527,30 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
{
	struct dentry *lower_dentry;
	struct dentry *lower_dir_dentry;
	struct inode *lower_dir_inode;
	int rc;

	lower_dentry = ecryptfs_dentry_to_lower(dentry);
	dget(dentry);
	lower_dir_dentry = lock_parent(lower_dentry);
	dget(lower_dentry);
	rc = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
	dput(lower_dentry);
	if (!rc && d_really_is_positive(dentry))
	lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
	lower_dir_inode = d_inode(lower_dir_dentry);

	inode_lock_nested(lower_dir_inode, I_MUTEX_PARENT);
	dget(lower_dentry);	// don't even try to make the lower negative
	if (lower_dentry->d_parent != lower_dir_dentry)
		rc = -EINVAL;
	else if (d_unhashed(lower_dentry))
		rc = -EINVAL;
	else
		rc = vfs_rmdir(lower_dir_inode, lower_dentry);
	if (!rc) {
		clear_nlink(d_inode(dentry));
	fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry));
	set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink);
	unlock_dir(lower_dir_dentry);
		fsstack_copy_attr_times(dir, lower_dir_inode);
		set_nlink(dir, lower_dir_inode->i_nlink);
	}
	dput(lower_dentry);
	inode_unlock(lower_dir_inode);
	if (!rc)
		d_drop(dentry);
	dput(dentry);
	return rc;
}

@@ -565,20 +588,22 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
	struct dentry *lower_new_dentry;
	struct dentry *lower_old_dir_dentry;
	struct dentry *lower_new_dir_dentry;
	struct dentry *trap = NULL;
	struct dentry *trap;
	struct inode *target_inode;

	if (flags)
		return -EINVAL;

	lower_old_dir_dentry = ecryptfs_dentry_to_lower(old_dentry->d_parent);
	lower_new_dir_dentry = ecryptfs_dentry_to_lower(new_dentry->d_parent);

	lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
	lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
	dget(lower_old_dentry);
	dget(lower_new_dentry);
	lower_old_dir_dentry = dget_parent(lower_old_dentry);
	lower_new_dir_dentry = dget_parent(lower_new_dentry);

	target_inode = d_inode(new_dentry);

	trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
	dget(lower_new_dentry);
	rc = -EINVAL;
	if (lower_old_dentry->d_parent != lower_old_dir_dentry)
		goto out_lock;
@@ -606,11 +631,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
	if (new_dir != old_dir)
		fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry));
out_lock:
	unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
	dput(lower_new_dir_dentry);
	dput(lower_old_dir_dentry);
	dput(lower_new_dentry);
	dput(lower_old_dentry);
	unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
	return rc;
}

+19 −12
Original line number Diff line number Diff line
@@ -519,26 +519,33 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
		 * inode is actually connected to the parent.
		 */
		err = exportfs_get_name(mnt, target_dir, nbuf, result);
		if (!err) {
		if (err) {
			dput(target_dir);
			goto err_result;
		}

		inode_lock(target_dir->d_inode);
			nresult = lookup_one_len(nbuf, target_dir,
						 strlen(nbuf));
			inode_unlock(target_dir->d_inode);
		nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf));
		if (!IS_ERR(nresult)) {
				if (nresult->d_inode) {
					dput(result);
					result = nresult;
				} else
			if (unlikely(nresult->d_inode != result->d_inode)) {
				dput(nresult);
				nresult = ERR_PTR(-ESTALE);
			}
		}

		inode_unlock(target_dir->d_inode);
		/*
		 * At this point we are done with the parent, but it's pinned
		 * by the child dentry anyway.
		 */
		dput(target_dir);

		if (IS_ERR(nresult)) {
			err = PTR_ERR(nresult);
			goto err_result;
		}
		dput(result);
		result = nresult;

		/*
		 * And finally make sure the dentry is actually acceptable
		 * to NFSD.
+7 −8
Original line number Diff line number Diff line
@@ -2478,8 +2478,10 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount *

		time64_to_tm(sb->s_time_max, 0, &tm);

		pr_warn("Mounted %s file system at %s supports timestamps until %04ld (0x%llx)\n",
			sb->s_type->name, mntpath,
		pr_warn("%s filesystem being %s at %s supports timestamps until %04ld (0x%llx)\n",
			sb->s_type->name,
			is_mounted(mnt) ? "remounted" : "mounted",
			mntpath,
			tm.tm_year+1900, (unsigned long long)sb->s_time_max);

		free_page((unsigned long)buf);
@@ -2764,14 +2766,11 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
	if (IS_ERR(mnt))
		return PTR_ERR(mnt);

	error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags);
	if (error < 0) {
		mntput(mnt);
		return error;
	}

	mnt_warn_timestamp_expiry(mountpoint, mnt);

	error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags);
	if (error < 0)
		mntput(mnt);
	return error;
}

Loading