forked from Minki/linux
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()
This commit is contained in:
commit
b4c0800e42
10
fs/aio.c
10
fs/aio.c
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -128,13 +128,20 @@ 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);
|
||||
rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
|
||||
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);
|
||||
goto out_unlock;
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
inode_lock(target_dir->d_inode);
|
||||
nresult = lookup_one_len(nbuf, target_dir,
|
||||
strlen(nbuf));
|
||||
inode_unlock(target_dir->d_inode);
|
||||
if (!IS_ERR(nresult)) {
|
||||
if (nresult->d_inode) {
|
||||
dput(result);
|
||||
result = nresult;
|
||||
} else
|
||||
dput(nresult);
|
||||
}
|
||||
if (err) {
|
||||
dput(target_dir);
|
||||
goto err_result;
|
||||
}
|
||||
|
||||
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
|
||||
* 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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
if (IS_ERR(d))
|
||||
return PTR_ERR(d);
|
||||
inode_unlock(d_backing_inode(parent->dentry));
|
||||
if (d_is_positive(d)) {
|
||||
/* update watch filter fields */
|
||||
watch->dev = d->d_sb->s_dev;
|
||||
watch->ino = d_backing_inode(d)->i_ino;
|
||||
}
|
||||
inode_unlock(d_backing_inode(parent->dentry));
|
||||
dput(d);
|
||||
return 0;
|
||||
}
|
||||
|
@ -2119,11 +2119,12 @@ int cgroup_do_get_tree(struct fs_context *fc)
|
||||
|
||||
nsdentry = kernfs_node_dentry(cgrp->kn, sb);
|
||||
dput(fc->root);
|
||||
fc->root = nsdentry;
|
||||
if (IS_ERR(nsdentry)) {
|
||||
ret = PTR_ERR(nsdentry);
|
||||
deactivate_locked_super(sb);
|
||||
ret = PTR_ERR(nsdentry);
|
||||
nsdentry = NULL;
|
||||
}
|
||||
fc->root = nsdentry;
|
||||
}
|
||||
|
||||
if (!ctx->kfc.new_sb_created)
|
||||
|
Loading…
Reference in New Issue
Block a user