Commit b4c0800e authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

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
...@@ -2179,7 +2179,7 @@ SYSCALL_DEFINE5(io_getevents_time32, __u32, ctx_id, ...@@ -2179,7 +2179,7 @@ SYSCALL_DEFINE5(io_getevents_time32, __u32, ctx_id,
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
struct __compat_aio_sigset { struct __compat_aio_sigset {
compat_sigset_t __user *sigmask; compat_uptr_t sigmask;
compat_size_t sigsetsize; compat_size_t sigsetsize;
}; };
...@@ -2193,7 +2193,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents, ...@@ -2193,7 +2193,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
struct old_timespec32 __user *, timeout, struct old_timespec32 __user *, timeout,
const struct __compat_aio_sigset __user *, usig) const struct __compat_aio_sigset __user *, usig)
{ {
struct __compat_aio_sigset ksig = { NULL, }; struct __compat_aio_sigset ksig = { 0, };
struct timespec64 t; struct timespec64 t;
bool interrupted; bool interrupted;
int ret; int ret;
...@@ -2204,7 +2204,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents, ...@@ -2204,7 +2204,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
if (usig && copy_from_user(&ksig, usig, sizeof(ksig))) if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
return -EFAULT; return -EFAULT;
ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize); ret = set_compat_user_sigmask(compat_ptr(ksig.sigmask), ksig.sigsetsize);
if (ret) if (ret)
return ret; return ret;
...@@ -2228,7 +2228,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64, ...@@ -2228,7 +2228,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
struct __kernel_timespec __user *, timeout, struct __kernel_timespec __user *, timeout,
const struct __compat_aio_sigset __user *, usig) const struct __compat_aio_sigset __user *, usig)
{ {
struct __compat_aio_sigset ksig = { NULL, }; struct __compat_aio_sigset ksig = { 0, };
struct timespec64 t; struct timespec64 t;
bool interrupted; bool interrupted;
int ret; int ret;
...@@ -2239,7 +2239,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64, ...@@ -2239,7 +2239,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
if (usig && copy_from_user(&ksig, usig, sizeof(ksig))) if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
return -EFAULT; return -EFAULT;
ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize); ret = set_compat_user_sigmask(compat_ptr(ksig.sigmask), ksig.sigsetsize);
if (ret) if (ret)
return ret; return ret;
......
...@@ -459,9 +459,10 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb, ...@@ -459,9 +459,10 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
*/ */
how &= ~AUTOFS_EXP_LEAVES; how &= ~AUTOFS_EXP_LEAVES;
found = should_expire(expired, mnt, timeout, how); found = should_expire(expired, mnt, timeout, how);
if (!found || found != expired) if (found != expired) { // something has changed, continue
/* Something has changed, continue */ dput(found);
goto next; goto next;
}
if (expired != dentry) if (expired != dentry)
dput(dentry); dput(dentry);
......
...@@ -128,13 +128,20 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry, ...@@ -128,13 +128,20 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
struct inode *inode) struct inode *inode)
{ {
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); 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 dentry *lower_dir_dentry;
struct inode *lower_dir_inode;
int rc; int rc;
dget(lower_dentry); lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
lower_dir_dentry = lock_parent(lower_dentry); lower_dir_inode = d_inode(lower_dir_dentry);
rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL); 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) { if (rc) {
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc); printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
goto out_unlock; goto out_unlock;
...@@ -142,10 +149,11 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry, ...@@ -142,10 +149,11 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
fsstack_copy_attr_times(dir, lower_dir_inode); fsstack_copy_attr_times(dir, lower_dir_inode);
set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink); set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
inode->i_ctime = dir->i_ctime; inode->i_ctime = dir->i_ctime;
d_drop(dentry);
out_unlock: out_unlock:
unlock_dir(lower_dir_dentry);
dput(lower_dentry); dput(lower_dentry);
inode_unlock(lower_dir_inode);
if (!rc)
d_drop(dentry);
return rc; return rc;
} }
...@@ -311,9 +319,9 @@ static int ecryptfs_i_size_read(struct dentry *dentry, struct inode *inode) ...@@ -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, static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry,
struct dentry *lower_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 ecryptfs_dentry_info *dentry_info;
struct vfsmount *lower_mnt;
int rc = 0; int rc = 0;
dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL); dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL);
...@@ -322,16 +330,23 @@ static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry, ...@@ -322,16 +330,23 @@ static struct dentry *ecryptfs_lookup_interpose(struct dentry *dentry,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
fsstack_copy_attr_atime(d_inode(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)); BUG_ON(!d_count(lower_dentry));
ecryptfs_set_dentry_private(dentry, dentry_info); 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; 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 */ /* We want to add because we couldn't find in lower */
d_add(dentry, NULL); d_add(dentry, NULL);
return NULL; return NULL;
...@@ -512,22 +527,30 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -512,22 +527,30 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
{ {
struct dentry *lower_dentry; struct dentry *lower_dentry;
struct dentry *lower_dir_dentry; struct dentry *lower_dir_dentry;
struct inode *lower_dir_inode;
int rc; int rc;
lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_dentry = ecryptfs_dentry_to_lower(dentry);
dget(dentry); lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
lower_dir_dentry = lock_parent(lower_dentry); lower_dir_inode = d_inode(lower_dir_dentry);
dget(lower_dentry);
rc = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry); inode_lock_nested(lower_dir_inode, I_MUTEX_PARENT);
dput(lower_dentry); dget(lower_dentry); // don't even try to make the lower negative
if (!rc && d_really_is_positive(dentry)) 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)); clear_nlink(d_inode(dentry));
fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry)); fsstack_copy_attr_times(dir, lower_dir_inode);
set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink); set_nlink(dir, lower_dir_inode->i_nlink);
unlock_dir(lower_dir_dentry); }
dput(lower_dentry);
inode_unlock(lower_dir_inode);
if (!rc) if (!rc)
d_drop(dentry); d_drop(dentry);
dput(dentry);
return rc; return rc;
} }
...@@ -565,20 +588,22 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -565,20 +588,22 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct dentry *lower_new_dentry; struct dentry *lower_new_dentry;
struct dentry *lower_old_dir_dentry; struct dentry *lower_old_dir_dentry;
struct dentry *lower_new_dir_dentry; struct dentry *lower_new_dir_dentry;
struct dentry *trap = NULL; struct dentry *trap;
struct inode *target_inode; struct inode *target_inode;
if (flags) if (flags)
return -EINVAL; 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_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
lower_new_dentry = ecryptfs_dentry_to_lower(new_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); target_inode = d_inode(new_dentry);
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dget(lower_new_dentry);
rc = -EINVAL; rc = -EINVAL;
if (lower_old_dentry->d_parent != lower_old_dir_dentry) if (lower_old_dentry->d_parent != lower_old_dir_dentry)
goto out_lock; goto out_lock;
...@@ -606,11 +631,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -606,11 +631,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new_dir != old_dir) if (new_dir != old_dir)
fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry));
out_lock: 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_new_dentry);
dput(lower_old_dentry); unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
return rc; return rc;
} }
......
...@@ -519,26 +519,33 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, ...@@ -519,26 +519,33 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
* inode is actually connected to the parent. * inode is actually connected to the parent.
*/ */
err = exportfs_get_name(mnt, target_dir, nbuf, result); err = exportfs_get_name(mnt, target_dir, nbuf, result);
if (!err) { if (err) {
inode_lock(target_dir->d_inode); dput(target_dir);
nresult = lookup_one_len(nbuf, target_dir, goto err_result;
strlen(nbuf));
inode_unlock(target_dir->d_inode);
if (!IS_ERR(nresult)) {
if (nresult->d_inode) {
dput(result);
result = nresult;
} else
dput(nresult);
}
} }
inode_lock(target_dir->d_inode);
nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf));
if (!IS_ERR(nresult)) {
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 * At this point we are done with the parent, but it's pinned
* by the child dentry anyway. * by the child dentry anyway.
*/ */
dput(target_dir); 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 * And finally make sure the dentry is actually acceptable
* to NFSD. * to NFSD.
......
...@@ -2478,8 +2478,10 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount * ...@@ -2478,8 +2478,10 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount *
time64_to_tm(sb->s_time_max, 0, &tm); time64_to_tm(sb->s_time_max, 0, &tm);
pr_warn("Mounted %s file system at %s supports timestamps until %04ld (0x%llx)\n", pr_warn("%s filesystem being %s at %s supports timestamps until %04ld (0x%llx)\n",
sb->s_type->name, mntpath, sb->s_type->name,
is_mounted(mnt) ? "remounted" : "mounted",
mntpath,
tm.tm_year+1900, (unsigned long long)sb->s_time_max); tm.tm_year+1900, (unsigned long long)sb->s_time_max);
free_page((unsigned long)buf); free_page((unsigned long)buf);
...@@ -2764,14 +2766,11 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint, ...@@ -2764,14 +2766,11 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
if (IS_ERR(mnt)) if (IS_ERR(mnt))
return PTR_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); mnt_warn_timestamp_expiry(mountpoint, mnt);
error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags);
if (error < 0)
mntput(mnt);
return error; return error;
} }
......
...@@ -351,12 +351,12 @@ static int audit_get_nd(struct audit_watch *watch, struct path *parent) ...@@ -351,12 +351,12 @@ static int audit_get_nd(struct audit_watch *watch, struct path *parent)
struct dentry *d = kern_path_locked(watch->path, parent); struct dentry *d = kern_path_locked(watch->path, parent);
if (IS_ERR(d)) if (IS_ERR(d))
return PTR_ERR(d); return PTR_ERR(d);
inode_unlock(d_backing_inode(parent->dentry));
if (d_is_positive(d)) { if (d_is_positive(d)) {
/* update watch filter fields */ /* update watch filter fields */
watch->dev = d->d_sb->s_dev; watch->dev = d->d_sb->s_dev;
watch->ino = d_backing_inode(d)->i_ino; watch->ino = d_backing_inode(d)->i_ino;
} }
inode_unlock(d_backing_inode(parent->dentry));
dput(d); dput(d);
return 0; return 0;
} }
......
...@@ -2119,11 +2119,12 @@ int cgroup_do_get_tree(struct fs_context *fc) ...@@ -2119,11 +2119,12 @@ int cgroup_do_get_tree(struct fs_context *fc)
nsdentry = kernfs_node_dentry(cgrp->kn, sb); nsdentry = kernfs_node_dentry(cgrp->kn, sb);
dput(fc->root); dput(fc->root);
fc->root = nsdentry;
if (IS_ERR(nsdentry)) { if (IS_ERR(nsdentry)) {
ret = PTR_ERR(nsdentry);
deactivate_locked_super(sb); deactivate_locked_super(sb);
ret = PTR_ERR(nsdentry);
nsdentry = NULL;
} }
fc->root = nsdentry;
} }
if (!ctx->kfc.new_sb_created) if (!ctx->kfc.new_sb_created)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment