diff mbox series

[X] fixes for LP: #1415636/CVE-2015-1350

Message ID 1533135539-15485-1-git-send-email-jose.pekkarinen@canonical.com
State New
Headers show
Series [X] fixes for LP: #1415636/CVE-2015-1350 | expand

Commit Message

Jose Pekkarinen Aug. 1, 2018, 2:58 p.m. UTC
fs: Avoid premature clearing of capabilities(030b533c4fd)
Reviewed-by: Christoph Hellwig <hch@lst.de>
by: Jan Kara <jack@suse.cz>

It's applied on:

UBUNTU: Ubuntu-lts-3.19.0-82.90~14.04.1(ceea1114793f68).
Signed-off-by: Brad Figg <brad.figg@canonical.com>

Currently, notify_change() clears capabilities or IMA attributes by
calling security_inode_killpriv() before calling into ->setattr. Thus it
happens before any other permission checks in inode_change_ok() and user
is thus allowed to trigger clearing of capabilities or IMA attributes
for any file he can look up e.g. by calling chown for that file. This is
unexpected and can lead to user DoSing a system.

Fix the problem by calling security_inode_killpriv() at the end of
inode_change_ok() instead of from notify_change(). At that moment we are
sure user has permissions to do the requested change.

References: CVE-2015-1350
Signed-off-by: José Pekkarinen <jose.pekkarinen@canonical.com>
---
 fs/9p/vfs_inode.c       |  2 +-
 fs/9p/vfs_inode_dotl.c  |  2 +-
 fs/adfs/inode.c         |  2 +-
 fs/affs/inode.c         |  2 +-
 fs/attr.c               | 23 ++++++++++++++++-------
 fs/btrfs/inode.c        |  2 +-
 fs/ceph/inode.c         |  2 +-
 fs/cifs/inode.c         |  4 ++--
 fs/ecryptfs/inode.c     |  2 +-
 fs/exofs/inode.c        |  2 +-
 fs/ext2/inode.c         |  2 +-
 fs/ext3/inode.c         |  2 +-
 fs/ext4/inode.c         |  2 +-
 fs/f2fs/file.c          |  2 +-
 fs/fat/file.c           |  2 +-
 fs/fuse/dir.c           | 13 +++++++------
 fs/fuse/file.c          |  3 ++-
 fs/fuse/fuse_i.h        |  2 +-
 fs/gfs2/inode.c         |  2 +-
 fs/hfs/inode.c          |  2 +-
 fs/hfsplus/inode.c      |  2 +-
 fs/hostfs/hostfs_kern.c |  2 +-
 fs/hpfs/inode.c         |  2 +-
 fs/hugetlbfs/inode.c    |  2 +-
 fs/jffs2/fs.c           |  2 +-
 fs/jfs/file.c           |  2 +-
 fs/kernfs/inode.c       |  2 +-
 fs/libfs.c              |  2 +-
 fs/logfs/file.c         |  2 +-
 fs/minix/file.c         |  2 +-
 fs/ncpfs/inode.c        |  2 +-
 fs/nfsd/vfs.c           |  7 ++++---
 fs/nilfs2/inode.c       |  2 +-
 fs/ntfs/inode.c         |  2 +-
 fs/ocfs2/dlmfs/dlmfs.c  |  2 +-
 fs/ocfs2/file.c         |  2 +-
 fs/omfs/file.c          |  2 +-
 fs/proc/base.c          |  2 +-
 fs/proc/generic.c       |  2 +-
 fs/proc/proc_sysctl.c   |  2 +-
 fs/ramfs/file-nommu.c   |  2 +-
 fs/reiserfs/inode.c     |  2 +-
 fs/sysv/file.c          |  2 +-
 fs/ubifs/file.c         |  2 +-
 fs/udf/file.c           |  2 +-
 fs/ufs/truncate.c       |  2 +-
 fs/xfs/xfs_iops.c       |  6 ++++--
 include/linux/fs.h      |  2 +-
 mm/shmem.c              |  2 +-
 49 files changed, 78 insertions(+), 64 deletions(-)

Comments

Khalid Elmously Aug. 2, 2018, 5:44 a.m. UTC | #1
On 2018-08-01 14:58:59 , José Pekkarinen wrote:
> fs: Avoid premature clearing of capabilities(030b533c4fd)
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> by: Jan Kara <jack@suse.cz>
> 
> It's applied on:
> 
> UBUNTU: Ubuntu-lts-3.19.0-82.90~14.04.1(ceea1114793f68).
> Signed-off-by: Brad Figg <brad.figg@canonical.com>
> 
> Currently, notify_change() clears capabilities or IMA attributes by
> calling security_inode_killpriv() before calling into ->setattr. Thus it
> happens before any other permission checks in inode_change_ok() and user
> is thus allowed to trigger clearing of capabilities or IMA attributes
> for any file he can look up e.g. by calling chown for that file. This is
> unexpected and can lead to user DoSing a system.
> 
> Fix the problem by calling security_inode_killpriv() at the end of
> inode_change_ok() instead of from notify_change(). At that moment we are
> sure user has permissions to do the requested change.
> 
> References: CVE-2015-1350
> Signed-off-by: José Pekkarinen <jose.pekkarinen@canonical.com>
> ---
>  fs/9p/vfs_inode.c       |  2 +-
>  fs/9p/vfs_inode_dotl.c  |  2 +-
>  fs/adfs/inode.c         |  2 +-
>  fs/affs/inode.c         |  2 +-
>  fs/attr.c               | 23 ++++++++++++++++-------
>  fs/btrfs/inode.c        |  2 +-
>  fs/ceph/inode.c         |  2 +-
>  fs/cifs/inode.c         |  4 ++--
>  fs/ecryptfs/inode.c     |  2 +-
>  fs/exofs/inode.c        |  2 +-
>  fs/ext2/inode.c         |  2 +-
>  fs/ext3/inode.c         |  2 +-
>  fs/ext4/inode.c         |  2 +-
>  fs/f2fs/file.c          |  2 +-
>  fs/fat/file.c           |  2 +-
>  fs/fuse/dir.c           | 13 +++++++------
>  fs/fuse/file.c          |  3 ++-
>  fs/fuse/fuse_i.h        |  2 +-
>  fs/gfs2/inode.c         |  2 +-
>  fs/hfs/inode.c          |  2 +-
>  fs/hfsplus/inode.c      |  2 +-
>  fs/hostfs/hostfs_kern.c |  2 +-
>  fs/hpfs/inode.c         |  2 +-
>  fs/hugetlbfs/inode.c    |  2 +-
>  fs/jffs2/fs.c           |  2 +-
>  fs/jfs/file.c           |  2 +-
>  fs/kernfs/inode.c       |  2 +-
>  fs/libfs.c              |  2 +-
>  fs/logfs/file.c         |  2 +-
>  fs/minix/file.c         |  2 +-
>  fs/ncpfs/inode.c        |  2 +-
>  fs/nfsd/vfs.c           |  7 ++++---
>  fs/nilfs2/inode.c       |  2 +-
>  fs/ntfs/inode.c         |  2 +-
>  fs/ocfs2/dlmfs/dlmfs.c  |  2 +-
>  fs/ocfs2/file.c         |  2 +-
>  fs/omfs/file.c          |  2 +-
>  fs/proc/base.c          |  2 +-
>  fs/proc/generic.c       |  2 +-
>  fs/proc/proc_sysctl.c   |  2 +-
>  fs/ramfs/file-nommu.c   |  2 +-
>  fs/reiserfs/inode.c     |  2 +-
>  fs/sysv/file.c          |  2 +-
>  fs/ubifs/file.c         |  2 +-
>  fs/udf/file.c           |  2 +-
>  fs/ufs/truncate.c       |  2 +-
>  fs/xfs/xfs_iops.c       |  6 ++++--
>  include/linux/fs.h      |  2 +-
>  mm/shmem.c              |  2 +-
>  49 files changed, 78 insertions(+), 64 deletions(-)
> 
> diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
> index 711d744..65fcb86 100644
> --- a/fs/9p/vfs_inode.c
> +++ b/fs/9p/vfs_inode.c
> @@ -1094,7 +1094,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct p9_wstat wstat;
>  
>  	p9_debug(P9_DEBUG_VFS, "\n");
> -	retval = inode_change_ok(dentry->d_inode, iattr);
> +	retval = inode_change_ok(dentry, iattr);
>  	if (retval)
>  		return retval;
>  
> diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
> index 1254c7b..961a5b7 100644
> --- a/fs/9p/vfs_inode_dotl.c
> +++ b/fs/9p/vfs_inode_dotl.c
> @@ -560,7 +560,7 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
>  
>  	p9_debug(P9_DEBUG_VFS, "\n");
>  
> -	retval = inode_change_ok(inode, iattr);
> +	retval = inode_change_ok(dentry, iattr);
>  	if (retval)
>  		return retval;
>  
> diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
> index b9acada..fceb187 100644
> --- a/fs/adfs/inode.c
> +++ b/fs/adfs/inode.c
> @@ -303,7 +303,7 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)
>  	unsigned int ia_valid = attr->ia_valid;
>  	int error;
>  	
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  
>  	/*
>  	 * we can't change the UID or GID of any file -
> diff --git a/fs/affs/inode.c b/fs/affs/inode.c
> index d0609a2..e929583 100644
> --- a/fs/affs/inode.c
> +++ b/fs/affs/inode.c
> @@ -219,7 +219,7 @@ affs_notify_change(struct dentry *dentry, struct iattr *attr)
>  
>  	pr_debug("notify_change(%lu,0x%x)\n", inode->i_ino, attr->ia_valid);
>  
> -	error = inode_change_ok(inode,attr);
> +	error = inode_change_ok(dentry,attr);
>  	if (error)
>  		goto out;
>  
> diff --git a/fs/attr.c b/fs/attr.c
> index 6530ced..72ec99f 100644
> --- a/fs/attr.c
> +++ b/fs/attr.c
> @@ -28,8 +28,9 @@
>   * Should be called as the first thing in ->setattr implementations,
>   * possibly after taking additional locks.
>   */
> -int inode_change_ok(const struct inode *inode, struct iattr *attr)
> +int inode_change_ok(struct dentry *dentry, struct iattr *attr)
>  {
> +	struct inode *inode = d_inode(dentry);
>  	unsigned int ia_valid = attr->ia_valid;
>  
>  	/*
> @@ -44,7 +45,7 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr)
>  
>  	/* If force is set do it anyway. */
>  	if (ia_valid & ATTR_FORCE)
> -		return 0;
> +		goto kill_priv;
>  
>  	/* Make sure a caller can chown. */
>  	if ((ia_valid & ATTR_UID) &&
> @@ -77,6 +78,16 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr)
>  			return -EPERM;
>  	}
>  
> +kill_priv:
> +	/* User has permission for the change */
> +	if (ia_valid & ATTR_KILL_PRIV) {
> +		int error;
> +
> +		error = security_inode_killpriv(dentry);
> +		if (error)
> +			return error;
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL(inode_change_ok);
> @@ -217,13 +228,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
>  	if (!(ia_valid & ATTR_MTIME_SET))
>  		attr->ia_mtime = now;
>  	if (ia_valid & ATTR_KILL_PRIV) {
> -		attr->ia_valid &= ~ATTR_KILL_PRIV;
> -		ia_valid &= ~ATTR_KILL_PRIV;
>  		error = security_inode_need_killpriv(dentry);
> -		if (error > 0)
> -			error = security_inode_killpriv(dentry);
> -		if (error)
> +		if (error < 0)
>  			return error;
> +		if (error == 0)
> +			ia_valid = attr->ia_valid &= ~ATTR_KILL_PRIV;
>  	}
>  
>  	/*
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index 508a4ca..47d3999 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -4805,7 +4805,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (btrfs_root_readonly(root))
>  		return -EROFS;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index f61a741..4003158 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -1727,7 +1727,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (ceph_snap(inode) != CEPH_NOSNAP)
>  		return -EROFS;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err != 0)
>  		return err;
>  
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index 5772aa0..0e0766c 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -2092,7 +2092,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
>  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
>  		attrs->ia_valid |= ATTR_FORCE;
>  
> -	rc = inode_change_ok(inode, attrs);
> +	rc = inode_change_ok(direntry, attrs);
>  	if (rc < 0)
>  		goto out;
>  
> @@ -2233,7 +2233,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
>  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
>  		attrs->ia_valid |= ATTR_FORCE;
>  
> -	rc = inode_change_ok(inode, attrs);
> +	rc = inode_change_ok(direntry, attrs);
>  	if (rc < 0) {
>  		free_xid(xid);
>  		return rc;
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 1686dc2..3cf2cdd 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -943,7 +943,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
>  	}
>  	mutex_unlock(&crypt_stat->cs_mutex);
>  
> -	rc = inode_change_ok(inode, ia);
> +	rc = inode_change_ok(dentry, ia);
>  	if (rc)
>  		goto out;
>  	if (ia->ia_valid & ATTR_SIZE) {
> diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
> index f1d3d4e..4bb7077 100644
> --- a/fs/exofs/inode.c
> +++ b/fs/exofs/inode.c
> @@ -1039,7 +1039,7 @@ int exofs_setattr(struct dentry *dentry, struct iattr *iattr)
>  	if (unlikely(error))
>  		return error;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (unlikely(error))
>  		return error;
>  
> diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
> index 36d35c3..c75bbd4 100644
> --- a/fs/ext2/inode.c
> +++ b/fs/ext2/inode.c
> @@ -1547,7 +1547,7 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
> index 2c6ccc4..421b537 100644
> --- a/fs/ext3/inode.c
> +++ b/fs/ext3/inode.c
> @@ -3244,7 +3244,7 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
>  	int error, rc = 0;
>  	const unsigned int ia_valid = attr->ia_valid;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 77c8ce1..51ea725 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -4482,7 +4482,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
>  	int orphan = 0;
>  	const unsigned int ia_valid = attr->ia_valid;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 3c27e0e..1ca06de 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -600,7 +600,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct f2fs_inode_info *fi = F2FS_I(inode);
>  	int err;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/fat/file.c b/fs/fat/file.c
> index 8429c68..626f9d4 100644
> --- a/fs/fat/file.c
> +++ b/fs/fat/file.c
> @@ -394,7 +394,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
>  			attr->ia_valid &= ~TIMES_SET_FLAGS;
>  	}
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	attr->ia_valid = ia_valid;
>  	if (error) {
>  		if (sbi->options.quiet)
> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
> index 08e7b1a..7b2a04c 100644
> --- a/fs/fuse/dir.c
> +++ b/fs/fuse/dir.c
> @@ -1602,9 +1602,10 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
>   * vmtruncate() doesn't allow for this case, so do the rlimit checking
>   * and the actual truncation by hand.
>   */
> -int fuse_do_setattr(struct inode *inode, struct iattr *attr,
> +int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
>  		    struct file *file)
>  {
> +	struct inode *inode = d_inode(dentry);
>  	struct fuse_conn *fc = get_fuse_conn(inode);
>  	struct fuse_inode *fi = get_fuse_inode(inode);
>  	FUSE_ARGS(args);
> @@ -1619,7 +1620,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
>  	if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
>  		attr->ia_valid |= ATTR_FORCE;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> @@ -1710,17 +1711,17 @@ error:
>  	return err;
>  }
>  
> -static int fuse_setattr(struct dentry *entry, struct iattr *attr)
> +static int fuse_setattr(struct dentry *dentry, struct iattr *attr)
>  {
> -	struct inode *inode = entry->d_inode;
> +	struct inode *inode = dentry->d_inode;
>  
>  	if (!fuse_allow_current_process(get_fuse_conn(inode)))
>  		return -EACCES;
>  
>  	if (attr->ia_valid & ATTR_FILE)
> -		return fuse_do_setattr(inode, attr, attr->ia_file);
> +		return fuse_do_setattr(dentry, attr, attr->ia_file);
>  	else
> -		return fuse_do_setattr(inode, attr, NULL);
> +		return fuse_do_setattr(dentry, attr, NULL);
>  }
>  
>  static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index 60788e9..dbcc12a 100644
> --- a/fs/fuse/file.c
> +++ b/fs/fuse/file.c
> @@ -2782,6 +2782,7 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
>  static void fuse_do_truncate(struct file *file)
>  {
>  	struct inode *inode = file->f_mapping->host;
> +	struct dentry *dentry = inode->i_sb->s_root;
>  	struct iattr attr;
>  
>  	attr.ia_valid = ATTR_SIZE;
> @@ -2790,7 +2791,7 @@ static void fuse_do_truncate(struct file *file)
>  	attr.ia_file = file;
>  	attr.ia_valid |= ATTR_FILE;
>  
> -	fuse_do_setattr(inode, &attr, file);
> +	fuse_do_setattr(dentry, &attr, file);
>  }
>  
>  static inline loff_t fuse_round_up(loff_t off)
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index 1cdfb07..195c942 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -903,7 +903,7 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos);
>  int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
>  int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
>  
> -int fuse_do_setattr(struct inode *inode, struct iattr *attr,
> +int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
>  		    struct file *file);
>  
>  void fuse_set_initialized(struct fuse_conn *fc);
> diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
> index 9054002..c7fb53e 100644
> --- a/fs/gfs2/inode.c
> +++ b/fs/gfs2/inode.c
> @@ -1760,7 +1760,7 @@ static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
>  		goto out;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		goto out;
>  
> diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
> index d0929bc..9b42ee0 100644
> --- a/fs/hfs/inode.c
> +++ b/fs/hfs/inode.c
> @@ -604,7 +604,7 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr)
>  	struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
>  	int error;
>  
> -	error = inode_change_ok(inode, attr); /* basic permission checks */
> +	error = inode_change_ok(dentry, attr); /* basic permission checks */
>  	if (error)
>  		return error;
>  
> diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
> index 0cf786f..a74ff43 100644
> --- a/fs/hfsplus/inode.c
> +++ b/fs/hfsplus/inode.c
> @@ -247,7 +247,7 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
> index 75967b7e..bbf633f 100644
> --- a/fs/hostfs/hostfs_kern.c
> +++ b/fs/hostfs/hostfs_kern.c
> @@ -802,7 +802,7 @@ static int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
>  
>  	int fd = HOSTFS_I(inode)->fd;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
> index 7ce4b74..8e6942e 100644
> --- a/fs/hpfs/inode.c
> +++ b/fs/hpfs/inode.c
> @@ -272,7 +272,7 @@ int hpfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size)
>  		goto out_unlock;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		goto out_unlock;
>  
> diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
> index 5eba47f..a4ddc94 100644
> --- a/fs/hugetlbfs/inode.c
> +++ b/fs/hugetlbfs/inode.c
> @@ -429,7 +429,7 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
>  
>  	BUG_ON(!inode);
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
> index 601afd1..870ed4f 100644
> --- a/fs/jffs2/fs.c
> +++ b/fs/jffs2/fs.c
> @@ -193,7 +193,7 @@ int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct inode *inode = dentry->d_inode;
>  	int rc;
>  
> -	rc = inode_change_ok(inode, iattr);
> +	rc = inode_change_ok(dentry, iattr);
>  	if (rc)
>  		return rc;
>  
> diff --git a/fs/jfs/file.c b/fs/jfs/file.c
> index 33aa0cc..2ede547 100644
> --- a/fs/jfs/file.c
> +++ b/fs/jfs/file.c
> @@ -103,7 +103,7 @@ int jfs_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct inode *inode = dentry->d_inode;
>  	int rc;
>  
> -	rc = inode_change_ok(inode, iattr);
> +	rc = inode_change_ok(dentry, iattr);
>  	if (rc)
>  		return rc;
>  
> diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
> index 5b8ab29..325234f 100644
> --- a/fs/kernfs/inode.c
> +++ b/fs/kernfs/inode.c
> @@ -131,7 +131,7 @@ int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr)
>  		return -EINVAL;
>  
>  	mutex_lock(&kernfs_mutex);
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		goto out;
>  
> diff --git a/fs/libfs.c b/fs/libfs.c
> index 3a5deb3..5509af4 100644
> --- a/fs/libfs.c
> +++ b/fs/libfs.c
> @@ -371,7 +371,7 @@ int simple_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/logfs/file.c b/fs/logfs/file.c
> index 8538752..bf8b09d 100644
> --- a/fs/logfs/file.c
> +++ b/fs/logfs/file.c
> @@ -244,7 +244,7 @@ static int logfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int err = 0;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/minix/file.c b/fs/minix/file.c
> index a967de0..57bd2a4 100644
> --- a/fs/minix/file.c
> +++ b/fs/minix/file.c
> @@ -28,7 +28,7 @@ static int minix_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
> index e31e589..35a6d37 100644
> --- a/fs/ncpfs/inode.c
> +++ b/fs/ncpfs/inode.c
> @@ -885,7 +885,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
>  	/* ageing the dentry to force validation */
>  	ncp_age_dentry(server, dentry);
>  
> -	result = inode_change_ok(inode, attr);
> +	result = inode_change_ok(dentry, attr);
>  	if (result < 0)
>  		goto out;
>  
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index 5685c67..38e5b6a 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -300,7 +300,7 @@ commit_metadata(struct svc_fh *fhp)
>   * NFS semantics and what Linux expects.
>   */
>  static void
> -nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
> +nfsd_sanitize_attrs(struct dentry *dentry, struct iattr *iap)
>  {
>  	/*
>  	 * NFSv2 does not differentiate between "set-[ac]time-to-now"
> @@ -313,6 +313,7 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
>  	 * We only call inode_change_ok as the last test as technically
>  	 * it is not an interface that we should be using.
>  	 */
> +	struct inode *inode = d_inode(dentry);
>  #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
>  #define	MAX_TOUCH_TIME_ERROR (30*60)
>  	if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
> @@ -328,7 +329,7 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
>  		if (delta < 0)
>  			delta = -delta;
>  		if (delta < MAX_TOUCH_TIME_ERROR &&
> -		    inode_change_ok(inode, iap) != 0) {
> +		    inode_change_ok(dentry, iap) != 0) {
>  			/*
>  			 * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
>  			 * This will cause notify_change to set these times
> @@ -435,7 +436,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
>  	if (!iap->ia_valid)
>  		goto out;
>  
> -	nfsd_sanitize_attrs(inode, iap);
> +	nfsd_sanitize_attrs(dentry, iap);
>  
>  	/*
>  	 * The size case is special, it changes the file in addition to the
> diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
> index 8b59695..0791dd4 100644
> --- a/fs/nilfs2/inode.c
> +++ b/fs/nilfs2/inode.c
> @@ -840,7 +840,7 @@ int nilfs_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct super_block *sb = inode->i_sb;
>  	int err;
>  
> -	err = inode_change_ok(inode, iattr);
> +	err = inode_change_ok(dentry, iattr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
> index 898b994..6d51746 100644
> --- a/fs/ntfs/inode.c
> +++ b/fs/ntfs/inode.c
> @@ -2894,7 +2894,7 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	int err;
>  	unsigned int ia_valid = attr->ia_valid;
>  
> -	err = inode_change_ok(vi, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		goto out;
>  	/* We do not support NTFS ACLs yet. */
> diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
> index 57c40e3..4473e95 100644
> --- a/fs/ocfs2/dlmfs/dlmfs.c
> +++ b/fs/ocfs2/dlmfs/dlmfs.c
> @@ -211,7 +211,7 @@ static int dlmfs_file_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  
>  	attr->ia_valid &= ~ATTR_SIZE;
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> index 7e5aa39..9067886 100644
> --- a/fs/ocfs2/file.c
> +++ b/fs/ocfs2/file.c
> @@ -1143,7 +1143,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (!(attr->ia_valid & OCFS2_VALID_ATTRS))
>  		return 0;
>  
> -	status = inode_change_ok(inode, attr);
> +	status = inode_change_ok(dentry, attr);
>  	if (status)
>  		return status;
>  
> diff --git a/fs/omfs/file.c b/fs/omfs/file.c
> index 902e885..db6314c 100644
> --- a/fs/omfs/file.c
> +++ b/fs/omfs/file.c
> @@ -351,7 +351,7 @@ static int omfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index d022159..7abfc57 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -509,7 +509,7 @@ int proc_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (attr->ia_valid & ATTR_MODE)
>  		return -EPERM;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/proc/generic.c b/fs/proc/generic.c
> index aaabab4..5f24651 100644
> --- a/fs/proc/generic.c
> +++ b/fs/proc/generic.c
> @@ -105,7 +105,7 @@ static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
>  	struct proc_dir_entry *de = PDE(inode);
>  	int error;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
> index 3f7dc3e..be5b366 100644
> --- a/fs/proc/proc_sysctl.c
> +++ b/fs/proc/proc_sysctl.c
> @@ -753,7 +753,7 @@ static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
>  		return -EPERM;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
> index bbafbde..daba062 100644
> --- a/fs/ramfs/file-nommu.c
> +++ b/fs/ramfs/file-nommu.c
> @@ -163,7 +163,7 @@ static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia)
>  	int ret = 0;
>  
>  	/* POSIX UID/GID verification for setting inode attributes */
> -	ret = inode_change_ok(inode, ia);
> +	ret = inode_change_ok(dentry, ia);
>  	if (ret)
>  		return ret;
>  
> diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
> index a7eec98..80f5192 100644
> --- a/fs/reiserfs/inode.c
> +++ b/fs/reiserfs/inode.c
> @@ -3312,7 +3312,7 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	unsigned int ia_valid;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/sysv/file.c b/fs/sysv/file.c
> index b00811c..09099b5 100644
> --- a/fs/sysv/file.c
> +++ b/fs/sysv/file.c
> @@ -35,7 +35,7 @@ static int sysv_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
> index 538519e..b663d11 100644
> --- a/fs/ubifs/file.c
> +++ b/fs/ubifs/file.c
> @@ -1262,7 +1262,7 @@ int ubifs_setattr(struct dentry *dentry, struct iattr *attr)
>  
>  	dbg_gen("ino %lu, mode %#x, ia_valid %#x",
>  		inode->i_ino, inode->i_mode, attr->ia_valid);
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/udf/file.c b/fs/udf/file.c
> index 08f3555..e2a382e 100644
> --- a/fs/udf/file.c
> +++ b/fs/udf/file.c
> @@ -258,7 +258,7 @@ static int udf_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c
> index f04f89f..926ba19 100644
> --- a/fs/ufs/truncate.c
> +++ b/fs/ufs/truncate.c
> @@ -496,7 +496,7 @@ int ufs_setattr(struct dentry *dentry, struct iattr *attr)
>  	unsigned int ia_valid = attr->ia_valid;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
> index 17d057c..ea6af4f 100644
> --- a/fs/xfs/xfs_iops.c
> +++ b/fs/xfs/xfs_iops.c
> @@ -530,6 +530,7 @@ xfs_setattr_nonsize(
>  {
>  	xfs_mount_t		*mp = ip->i_mount;
>  	struct inode		*inode = VFS_I(ip);
> +	struct dentry 		*dentry = inode->i_sb->s_root;

I'm not sure I understand the above line. Can you please explain why it's true?



>  	int			mask = iattr->ia_valid;
>  	xfs_trans_t		*tp;
>  	int			error;
> @@ -548,7 +549,7 @@ xfs_setattr_nonsize(
>  		if (XFS_FORCED_SHUTDOWN(mp))
>  			return -EIO;
>  
> -		error = inode_change_ok(inode, iattr);
> +		error = inode_change_ok(dentry, iattr);
>  		if (error)
>  			return error;
>  	}
> @@ -736,6 +737,7 @@ xfs_setattr_size(
>  {
>  	struct xfs_mount	*mp = ip->i_mount;
>  	struct inode		*inode = VFS_I(ip);
> +	struct dentry		*dentry = inode->i_sb->s_root;

Same for the preceding line.


>  	xfs_off_t		oldsize, newsize;
>  	struct xfs_trans	*tp;
>  	int			error;
> @@ -751,7 +753,7 @@ xfs_setattr_size(
>  	if (XFS_FORCED_SHUTDOWN(mp))
>  		return -EIO;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		return error;
>  
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 92c88b8..d12c959 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2690,7 +2690,7 @@ extern int buffer_migrate_page(struct address_space *,
>  #define buffer_migrate_page NULL
>  #endif
>  
> -extern int inode_change_ok(const struct inode *, struct iattr *);
> +extern int inode_change_ok(struct dentry *, struct iattr *);
>  extern int inode_newsize_ok(const struct inode *, loff_t offset);
>  extern void setattr_copy(struct inode *inode, const struct iattr *attr);
>  
> diff --git a/mm/shmem.c b/mm/shmem.c
> index 993e6ba..fc6cab1 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -553,7 +553,7 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct shmem_inode_info *info = SHMEM_I(inode);
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> -- 
> 2.7.4
> 
> 
> -- 
> kernel-team mailing list
> kernel-team@lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
Kleber Sacilotto de Souza Aug. 2, 2018, 8:01 a.m. UTC | #2
On 08/01/18 16:58, José Pekkarinen wrote:
> fs: Avoid premature clearing of capabilities(030b533c4fd)
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> by: Jan Kara <jack@suse.cz>
> 
> It's applied on:
> 
> UBUNTU: Ubuntu-lts-3.19.0-82.90~14.04.1(ceea1114793f68).
> Signed-off-by: Brad Figg <brad.figg@canonical.com>

Hi José,

We don't have any 3.19 based kernel that's currently supported, was the
patch really backported for the version stated above?

> 
> Currently, notify_change() clears capabilities or IMA attributes by
> calling security_inode_killpriv() before calling into ->setattr. Thus it
> happens before any other permission checks in inode_change_ok() and user
> is thus allowed to trigger clearing of capabilities or IMA attributes
> for any file he can look up e.g. by calling chown for that file. This is
> unexpected and can lead to user DoSing a system.
> 
> Fix the problem by calling security_inode_killpriv() at the end of
> inode_change_ok() instead of from notify_change(). At that moment we are
> sure user has permissions to do the requested change.
> 
> References: CVE-2015-1350
> Signed-off-by: José Pekkarinen <jose.pekkarinen@canonical.com>


The body of the email doesn't follow our patch format for kernel SRU
requests. Please re-submit the patch following the format as described at:

https://wiki.ubuntu.com/Kernel/Dev/StablePatchFormat



Thanks,
Kleber

> ---
>  fs/9p/vfs_inode.c       |  2 +-
>  fs/9p/vfs_inode_dotl.c  |  2 +-
>  fs/adfs/inode.c         |  2 +-
>  fs/affs/inode.c         |  2 +-
>  fs/attr.c               | 23 ++++++++++++++++-------
>  fs/btrfs/inode.c        |  2 +-
>  fs/ceph/inode.c         |  2 +-
>  fs/cifs/inode.c         |  4 ++--
>  fs/ecryptfs/inode.c     |  2 +-
>  fs/exofs/inode.c        |  2 +-
>  fs/ext2/inode.c         |  2 +-
>  fs/ext3/inode.c         |  2 +-
>  fs/ext4/inode.c         |  2 +-
>  fs/f2fs/file.c          |  2 +-
>  fs/fat/file.c           |  2 +-
>  fs/fuse/dir.c           | 13 +++++++------
>  fs/fuse/file.c          |  3 ++-
>  fs/fuse/fuse_i.h        |  2 +-
>  fs/gfs2/inode.c         |  2 +-
>  fs/hfs/inode.c          |  2 +-
>  fs/hfsplus/inode.c      |  2 +-
>  fs/hostfs/hostfs_kern.c |  2 +-
>  fs/hpfs/inode.c         |  2 +-
>  fs/hugetlbfs/inode.c    |  2 +-
>  fs/jffs2/fs.c           |  2 +-
>  fs/jfs/file.c           |  2 +-
>  fs/kernfs/inode.c       |  2 +-
>  fs/libfs.c              |  2 +-
>  fs/logfs/file.c         |  2 +-
>  fs/minix/file.c         |  2 +-
>  fs/ncpfs/inode.c        |  2 +-
>  fs/nfsd/vfs.c           |  7 ++++---
>  fs/nilfs2/inode.c       |  2 +-
>  fs/ntfs/inode.c         |  2 +-
>  fs/ocfs2/dlmfs/dlmfs.c  |  2 +-
>  fs/ocfs2/file.c         |  2 +-
>  fs/omfs/file.c          |  2 +-
>  fs/proc/base.c          |  2 +-
>  fs/proc/generic.c       |  2 +-
>  fs/proc/proc_sysctl.c   |  2 +-
>  fs/ramfs/file-nommu.c   |  2 +-
>  fs/reiserfs/inode.c     |  2 +-
>  fs/sysv/file.c          |  2 +-
>  fs/ubifs/file.c         |  2 +-
>  fs/udf/file.c           |  2 +-
>  fs/ufs/truncate.c       |  2 +-
>  fs/xfs/xfs_iops.c       |  6 ++++--
>  include/linux/fs.h      |  2 +-
>  mm/shmem.c              |  2 +-
>  49 files changed, 78 insertions(+), 64 deletions(-)
> 
> diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
> index 711d744..65fcb86 100644
> --- a/fs/9p/vfs_inode.c
> +++ b/fs/9p/vfs_inode.c
> @@ -1094,7 +1094,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct p9_wstat wstat;
>  
>  	p9_debug(P9_DEBUG_VFS, "\n");
> -	retval = inode_change_ok(dentry->d_inode, iattr);
> +	retval = inode_change_ok(dentry, iattr);
>  	if (retval)
>  		return retval;
>  
> diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
> index 1254c7b..961a5b7 100644
> --- a/fs/9p/vfs_inode_dotl.c
> +++ b/fs/9p/vfs_inode_dotl.c
> @@ -560,7 +560,7 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
>  
>  	p9_debug(P9_DEBUG_VFS, "\n");
>  
> -	retval = inode_change_ok(inode, iattr);
> +	retval = inode_change_ok(dentry, iattr);
>  	if (retval)
>  		return retval;
>  
> diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
> index b9acada..fceb187 100644
> --- a/fs/adfs/inode.c
> +++ b/fs/adfs/inode.c
> @@ -303,7 +303,7 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)
>  	unsigned int ia_valid = attr->ia_valid;
>  	int error;
>  	
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  
>  	/*
>  	 * we can't change the UID or GID of any file -
> diff --git a/fs/affs/inode.c b/fs/affs/inode.c
> index d0609a2..e929583 100644
> --- a/fs/affs/inode.c
> +++ b/fs/affs/inode.c
> @@ -219,7 +219,7 @@ affs_notify_change(struct dentry *dentry, struct iattr *attr)
>  
>  	pr_debug("notify_change(%lu,0x%x)\n", inode->i_ino, attr->ia_valid);
>  
> -	error = inode_change_ok(inode,attr);
> +	error = inode_change_ok(dentry,attr);
>  	if (error)
>  		goto out;
>  
> diff --git a/fs/attr.c b/fs/attr.c
> index 6530ced..72ec99f 100644
> --- a/fs/attr.c
> +++ b/fs/attr.c
> @@ -28,8 +28,9 @@
>   * Should be called as the first thing in ->setattr implementations,
>   * possibly after taking additional locks.
>   */
> -int inode_change_ok(const struct inode *inode, struct iattr *attr)
> +int inode_change_ok(struct dentry *dentry, struct iattr *attr)
>  {
> +	struct inode *inode = d_inode(dentry);
>  	unsigned int ia_valid = attr->ia_valid;
>  
>  	/*
> @@ -44,7 +45,7 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr)
>  
>  	/* If force is set do it anyway. */
>  	if (ia_valid & ATTR_FORCE)
> -		return 0;
> +		goto kill_priv;
>  
>  	/* Make sure a caller can chown. */
>  	if ((ia_valid & ATTR_UID) &&
> @@ -77,6 +78,16 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr)
>  			return -EPERM;
>  	}
>  
> +kill_priv:
> +	/* User has permission for the change */
> +	if (ia_valid & ATTR_KILL_PRIV) {
> +		int error;
> +
> +		error = security_inode_killpriv(dentry);
> +		if (error)
> +			return error;
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL(inode_change_ok);
> @@ -217,13 +228,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
>  	if (!(ia_valid & ATTR_MTIME_SET))
>  		attr->ia_mtime = now;
>  	if (ia_valid & ATTR_KILL_PRIV) {
> -		attr->ia_valid &= ~ATTR_KILL_PRIV;
> -		ia_valid &= ~ATTR_KILL_PRIV;
>  		error = security_inode_need_killpriv(dentry);
> -		if (error > 0)
> -			error = security_inode_killpriv(dentry);
> -		if (error)
> +		if (error < 0)
>  			return error;
> +		if (error == 0)
> +			ia_valid = attr->ia_valid &= ~ATTR_KILL_PRIV;
>  	}
>  
>  	/*
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index 508a4ca..47d3999 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -4805,7 +4805,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (btrfs_root_readonly(root))
>  		return -EROFS;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index f61a741..4003158 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -1727,7 +1727,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (ceph_snap(inode) != CEPH_NOSNAP)
>  		return -EROFS;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err != 0)
>  		return err;
>  
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index 5772aa0..0e0766c 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -2092,7 +2092,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
>  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
>  		attrs->ia_valid |= ATTR_FORCE;
>  
> -	rc = inode_change_ok(inode, attrs);
> +	rc = inode_change_ok(direntry, attrs);
>  	if (rc < 0)
>  		goto out;
>  
> @@ -2233,7 +2233,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
>  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
>  		attrs->ia_valid |= ATTR_FORCE;
>  
> -	rc = inode_change_ok(inode, attrs);
> +	rc = inode_change_ok(direntry, attrs);
>  	if (rc < 0) {
>  		free_xid(xid);
>  		return rc;
> diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
> index 1686dc2..3cf2cdd 100644
> --- a/fs/ecryptfs/inode.c
> +++ b/fs/ecryptfs/inode.c
> @@ -943,7 +943,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
>  	}
>  	mutex_unlock(&crypt_stat->cs_mutex);
>  
> -	rc = inode_change_ok(inode, ia);
> +	rc = inode_change_ok(dentry, ia);
>  	if (rc)
>  		goto out;
>  	if (ia->ia_valid & ATTR_SIZE) {
> diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
> index f1d3d4e..4bb7077 100644
> --- a/fs/exofs/inode.c
> +++ b/fs/exofs/inode.c
> @@ -1039,7 +1039,7 @@ int exofs_setattr(struct dentry *dentry, struct iattr *iattr)
>  	if (unlikely(error))
>  		return error;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (unlikely(error))
>  		return error;
>  
> diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
> index 36d35c3..c75bbd4 100644
> --- a/fs/ext2/inode.c
> +++ b/fs/ext2/inode.c
> @@ -1547,7 +1547,7 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
> index 2c6ccc4..421b537 100644
> --- a/fs/ext3/inode.c
> +++ b/fs/ext3/inode.c
> @@ -3244,7 +3244,7 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
>  	int error, rc = 0;
>  	const unsigned int ia_valid = attr->ia_valid;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 77c8ce1..51ea725 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -4482,7 +4482,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
>  	int orphan = 0;
>  	const unsigned int ia_valid = attr->ia_valid;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 3c27e0e..1ca06de 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -600,7 +600,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct f2fs_inode_info *fi = F2FS_I(inode);
>  	int err;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/fat/file.c b/fs/fat/file.c
> index 8429c68..626f9d4 100644
> --- a/fs/fat/file.c
> +++ b/fs/fat/file.c
> @@ -394,7 +394,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
>  			attr->ia_valid &= ~TIMES_SET_FLAGS;
>  	}
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	attr->ia_valid = ia_valid;
>  	if (error) {
>  		if (sbi->options.quiet)
> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
> index 08e7b1a..7b2a04c 100644
> --- a/fs/fuse/dir.c
> +++ b/fs/fuse/dir.c
> @@ -1602,9 +1602,10 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
>   * vmtruncate() doesn't allow for this case, so do the rlimit checking
>   * and the actual truncation by hand.
>   */
> -int fuse_do_setattr(struct inode *inode, struct iattr *attr,
> +int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
>  		    struct file *file)
>  {
> +	struct inode *inode = d_inode(dentry);
>  	struct fuse_conn *fc = get_fuse_conn(inode);
>  	struct fuse_inode *fi = get_fuse_inode(inode);
>  	FUSE_ARGS(args);
> @@ -1619,7 +1620,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
>  	if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
>  		attr->ia_valid |= ATTR_FORCE;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> @@ -1710,17 +1711,17 @@ error:
>  	return err;
>  }
>  
> -static int fuse_setattr(struct dentry *entry, struct iattr *attr)
> +static int fuse_setattr(struct dentry *dentry, struct iattr *attr)
>  {
> -	struct inode *inode = entry->d_inode;
> +	struct inode *inode = dentry->d_inode;
>  
>  	if (!fuse_allow_current_process(get_fuse_conn(inode)))
>  		return -EACCES;
>  
>  	if (attr->ia_valid & ATTR_FILE)
> -		return fuse_do_setattr(inode, attr, attr->ia_file);
> +		return fuse_do_setattr(dentry, attr, attr->ia_file);
>  	else
> -		return fuse_do_setattr(inode, attr, NULL);
> +		return fuse_do_setattr(dentry, attr, NULL);
>  }
>  
>  static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index 60788e9..dbcc12a 100644
> --- a/fs/fuse/file.c
> +++ b/fs/fuse/file.c
> @@ -2782,6 +2782,7 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
>  static void fuse_do_truncate(struct file *file)
>  {
>  	struct inode *inode = file->f_mapping->host;
> +	struct dentry *dentry = inode->i_sb->s_root;
>  	struct iattr attr;
>  
>  	attr.ia_valid = ATTR_SIZE;
> @@ -2790,7 +2791,7 @@ static void fuse_do_truncate(struct file *file)
>  	attr.ia_file = file;
>  	attr.ia_valid |= ATTR_FILE;
>  
> -	fuse_do_setattr(inode, &attr, file);
> +	fuse_do_setattr(dentry, &attr, file);
>  }
>  
>  static inline loff_t fuse_round_up(loff_t off)
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index 1cdfb07..195c942 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -903,7 +903,7 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos);
>  int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
>  int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
>  
> -int fuse_do_setattr(struct inode *inode, struct iattr *attr,
> +int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
>  		    struct file *file);
>  
>  void fuse_set_initialized(struct fuse_conn *fc);
> diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
> index 9054002..c7fb53e 100644
> --- a/fs/gfs2/inode.c
> +++ b/fs/gfs2/inode.c
> @@ -1760,7 +1760,7 @@ static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
>  		goto out;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		goto out;
>  
> diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
> index d0929bc..9b42ee0 100644
> --- a/fs/hfs/inode.c
> +++ b/fs/hfs/inode.c
> @@ -604,7 +604,7 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr)
>  	struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
>  	int error;
>  
> -	error = inode_change_ok(inode, attr); /* basic permission checks */
> +	error = inode_change_ok(dentry, attr); /* basic permission checks */
>  	if (error)
>  		return error;
>  
> diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
> index 0cf786f..a74ff43 100644
> --- a/fs/hfsplus/inode.c
> +++ b/fs/hfsplus/inode.c
> @@ -247,7 +247,7 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
> index 75967b7e..bbf633f 100644
> --- a/fs/hostfs/hostfs_kern.c
> +++ b/fs/hostfs/hostfs_kern.c
> @@ -802,7 +802,7 @@ static int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
>  
>  	int fd = HOSTFS_I(inode)->fd;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
> index 7ce4b74..8e6942e 100644
> --- a/fs/hpfs/inode.c
> +++ b/fs/hpfs/inode.c
> @@ -272,7 +272,7 @@ int hpfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size)
>  		goto out_unlock;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		goto out_unlock;
>  
> diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
> index 5eba47f..a4ddc94 100644
> --- a/fs/hugetlbfs/inode.c
> +++ b/fs/hugetlbfs/inode.c
> @@ -429,7 +429,7 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
>  
>  	BUG_ON(!inode);
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
> index 601afd1..870ed4f 100644
> --- a/fs/jffs2/fs.c
> +++ b/fs/jffs2/fs.c
> @@ -193,7 +193,7 @@ int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct inode *inode = dentry->d_inode;
>  	int rc;
>  
> -	rc = inode_change_ok(inode, iattr);
> +	rc = inode_change_ok(dentry, iattr);
>  	if (rc)
>  		return rc;
>  
> diff --git a/fs/jfs/file.c b/fs/jfs/file.c
> index 33aa0cc..2ede547 100644
> --- a/fs/jfs/file.c
> +++ b/fs/jfs/file.c
> @@ -103,7 +103,7 @@ int jfs_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct inode *inode = dentry->d_inode;
>  	int rc;
>  
> -	rc = inode_change_ok(inode, iattr);
> +	rc = inode_change_ok(dentry, iattr);
>  	if (rc)
>  		return rc;
>  
> diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
> index 5b8ab29..325234f 100644
> --- a/fs/kernfs/inode.c
> +++ b/fs/kernfs/inode.c
> @@ -131,7 +131,7 @@ int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr)
>  		return -EINVAL;
>  
>  	mutex_lock(&kernfs_mutex);
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		goto out;
>  
> diff --git a/fs/libfs.c b/fs/libfs.c
> index 3a5deb3..5509af4 100644
> --- a/fs/libfs.c
> +++ b/fs/libfs.c
> @@ -371,7 +371,7 @@ int simple_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/logfs/file.c b/fs/logfs/file.c
> index 8538752..bf8b09d 100644
> --- a/fs/logfs/file.c
> +++ b/fs/logfs/file.c
> @@ -244,7 +244,7 @@ static int logfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int err = 0;
>  
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/minix/file.c b/fs/minix/file.c
> index a967de0..57bd2a4 100644
> --- a/fs/minix/file.c
> +++ b/fs/minix/file.c
> @@ -28,7 +28,7 @@ static int minix_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
> index e31e589..35a6d37 100644
> --- a/fs/ncpfs/inode.c
> +++ b/fs/ncpfs/inode.c
> @@ -885,7 +885,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
>  	/* ageing the dentry to force validation */
>  	ncp_age_dentry(server, dentry);
>  
> -	result = inode_change_ok(inode, attr);
> +	result = inode_change_ok(dentry, attr);
>  	if (result < 0)
>  		goto out;
>  
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index 5685c67..38e5b6a 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -300,7 +300,7 @@ commit_metadata(struct svc_fh *fhp)
>   * NFS semantics and what Linux expects.
>   */
>  static void
> -nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
> +nfsd_sanitize_attrs(struct dentry *dentry, struct iattr *iap)
>  {
>  	/*
>  	 * NFSv2 does not differentiate between "set-[ac]time-to-now"
> @@ -313,6 +313,7 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
>  	 * We only call inode_change_ok as the last test as technically
>  	 * it is not an interface that we should be using.
>  	 */
> +	struct inode *inode = d_inode(dentry);
>  #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
>  #define	MAX_TOUCH_TIME_ERROR (30*60)
>  	if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
> @@ -328,7 +329,7 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
>  		if (delta < 0)
>  			delta = -delta;
>  		if (delta < MAX_TOUCH_TIME_ERROR &&
> -		    inode_change_ok(inode, iap) != 0) {
> +		    inode_change_ok(dentry, iap) != 0) {
>  			/*
>  			 * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
>  			 * This will cause notify_change to set these times
> @@ -435,7 +436,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
>  	if (!iap->ia_valid)
>  		goto out;
>  
> -	nfsd_sanitize_attrs(inode, iap);
> +	nfsd_sanitize_attrs(dentry, iap);
>  
>  	/*
>  	 * The size case is special, it changes the file in addition to the
> diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
> index 8b59695..0791dd4 100644
> --- a/fs/nilfs2/inode.c
> +++ b/fs/nilfs2/inode.c
> @@ -840,7 +840,7 @@ int nilfs_setattr(struct dentry *dentry, struct iattr *iattr)
>  	struct super_block *sb = inode->i_sb;
>  	int err;
>  
> -	err = inode_change_ok(inode, iattr);
> +	err = inode_change_ok(dentry, iattr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
> index 898b994..6d51746 100644
> --- a/fs/ntfs/inode.c
> +++ b/fs/ntfs/inode.c
> @@ -2894,7 +2894,7 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	int err;
>  	unsigned int ia_valid = attr->ia_valid;
>  
> -	err = inode_change_ok(vi, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		goto out;
>  	/* We do not support NTFS ACLs yet. */
> diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
> index 57c40e3..4473e95 100644
> --- a/fs/ocfs2/dlmfs/dlmfs.c
> +++ b/fs/ocfs2/dlmfs/dlmfs.c
> @@ -211,7 +211,7 @@ static int dlmfs_file_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  
>  	attr->ia_valid &= ~ATTR_SIZE;
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> index 7e5aa39..9067886 100644
> --- a/fs/ocfs2/file.c
> +++ b/fs/ocfs2/file.c
> @@ -1143,7 +1143,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (!(attr->ia_valid & OCFS2_VALID_ATTRS))
>  		return 0;
>  
> -	status = inode_change_ok(inode, attr);
> +	status = inode_change_ok(dentry, attr);
>  	if (status)
>  		return status;
>  
> diff --git a/fs/omfs/file.c b/fs/omfs/file.c
> index 902e885..db6314c 100644
> --- a/fs/omfs/file.c
> +++ b/fs/omfs/file.c
> @@ -351,7 +351,7 @@ static int omfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index d022159..7abfc57 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -509,7 +509,7 @@ int proc_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (attr->ia_valid & ATTR_MODE)
>  		return -EPERM;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/proc/generic.c b/fs/proc/generic.c
> index aaabab4..5f24651 100644
> --- a/fs/proc/generic.c
> +++ b/fs/proc/generic.c
> @@ -105,7 +105,7 @@ static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
>  	struct proc_dir_entry *de = PDE(inode);
>  	int error;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
> index 3f7dc3e..be5b366 100644
> --- a/fs/proc/proc_sysctl.c
> +++ b/fs/proc/proc_sysctl.c
> @@ -753,7 +753,7 @@ static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
>  	if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
>  		return -EPERM;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
> index bbafbde..daba062 100644
> --- a/fs/ramfs/file-nommu.c
> +++ b/fs/ramfs/file-nommu.c
> @@ -163,7 +163,7 @@ static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia)
>  	int ret = 0;
>  
>  	/* POSIX UID/GID verification for setting inode attributes */
> -	ret = inode_change_ok(inode, ia);
> +	ret = inode_change_ok(dentry, ia);
>  	if (ret)
>  		return ret;
>  
> diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
> index a7eec98..80f5192 100644
> --- a/fs/reiserfs/inode.c
> +++ b/fs/reiserfs/inode.c
> @@ -3312,7 +3312,7 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr)
>  	unsigned int ia_valid;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/sysv/file.c b/fs/sysv/file.c
> index b00811c..09099b5 100644
> --- a/fs/sysv/file.c
> +++ b/fs/sysv/file.c
> @@ -35,7 +35,7 @@ static int sysv_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
> index 538519e..b663d11 100644
> --- a/fs/ubifs/file.c
> +++ b/fs/ubifs/file.c
> @@ -1262,7 +1262,7 @@ int ubifs_setattr(struct dentry *dentry, struct iattr *attr)
>  
>  	dbg_gen("ino %lu, mode %#x, ia_valid %#x",
>  		inode->i_ino, inode->i_mode, attr->ia_valid);
> -	err = inode_change_ok(inode, attr);
> +	err = inode_change_ok(dentry, attr);
>  	if (err)
>  		return err;
>  
> diff --git a/fs/udf/file.c b/fs/udf/file.c
> index 08f3555..e2a382e 100644
> --- a/fs/udf/file.c
> +++ b/fs/udf/file.c
> @@ -258,7 +258,7 @@ static int udf_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct inode *inode = dentry->d_inode;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c
> index f04f89f..926ba19 100644
> --- a/fs/ufs/truncate.c
> +++ b/fs/ufs/truncate.c
> @@ -496,7 +496,7 @@ int ufs_setattr(struct dentry *dentry, struct iattr *attr)
>  	unsigned int ia_valid = attr->ia_valid;
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
> index 17d057c..ea6af4f 100644
> --- a/fs/xfs/xfs_iops.c
> +++ b/fs/xfs/xfs_iops.c
> @@ -530,6 +530,7 @@ xfs_setattr_nonsize(
>  {
>  	xfs_mount_t		*mp = ip->i_mount;
>  	struct inode		*inode = VFS_I(ip);
> +	struct dentry 		*dentry = inode->i_sb->s_root;
>  	int			mask = iattr->ia_valid;
>  	xfs_trans_t		*tp;
>  	int			error;
> @@ -548,7 +549,7 @@ xfs_setattr_nonsize(
>  		if (XFS_FORCED_SHUTDOWN(mp))
>  			return -EIO;
>  
> -		error = inode_change_ok(inode, iattr);
> +		error = inode_change_ok(dentry, iattr);
>  		if (error)
>  			return error;
>  	}
> @@ -736,6 +737,7 @@ xfs_setattr_size(
>  {
>  	struct xfs_mount	*mp = ip->i_mount;
>  	struct inode		*inode = VFS_I(ip);
> +	struct dentry		*dentry = inode->i_sb->s_root;
>  	xfs_off_t		oldsize, newsize;
>  	struct xfs_trans	*tp;
>  	int			error;
> @@ -751,7 +753,7 @@ xfs_setattr_size(
>  	if (XFS_FORCED_SHUTDOWN(mp))
>  		return -EIO;
>  
> -	error = inode_change_ok(inode, iattr);
> +	error = inode_change_ok(dentry, iattr);
>  	if (error)
>  		return error;
>  
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 92c88b8..d12c959 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2690,7 +2690,7 @@ extern int buffer_migrate_page(struct address_space *,
>  #define buffer_migrate_page NULL
>  #endif
>  
> -extern int inode_change_ok(const struct inode *, struct iattr *);
> +extern int inode_change_ok(struct dentry *, struct iattr *);
>  extern int inode_newsize_ok(const struct inode *, loff_t offset);
>  extern void setattr_copy(struct inode *inode, const struct iattr *attr);
>  
> diff --git a/mm/shmem.c b/mm/shmem.c
> index 993e6ba..fc6cab1 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -553,7 +553,7 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
>  	struct shmem_inode_info *info = SHMEM_I(inode);
>  	int error;
>  
> -	error = inode_change_ok(inode, attr);
> +	error = inode_change_ok(dentry, attr);
>  	if (error)
>  		return error;
>  
>
Jose Pekkarinen Aug. 2, 2018, 9 a.m. UTC | #3
On Thursday, 2 August 2018 08:44:30 EEST Khaled Elmously wrote:
[snip]
> > +	struct dentry 		*dentry = inode->i_sb->s_root;
> 
> I'm not sure I understand the above line. Can you please explain why it's
> true?

	Seems to be the only way to find the dentry struct associated to an inode, 
in newer kernels most of the functions modified in this patch already receives 
a dentry instead of a raw inode, but for the remaining cases something needs 
to be tailored. I failed to find any macro to help with this endeavour.

	Thanks!

	José.
Jose Pekkarinen Aug. 2, 2018, 9:03 a.m. UTC | #4
On Thursday, 2 August 2018 11:01:34 EEST Kleber Souza wrote:
> On 08/01/18 16:58, José Pekkarinen wrote:
> > fs: Avoid premature clearing of capabilities(030b533c4fd)
> > Reviewed-by: Christoph Hellwig <hch@lst.de>
> > by: Jan Kara <jack@suse.cz>
> > 
> > It's applied on:
> > 
> > UBUNTU: Ubuntu-lts-3.19.0-82.90~14.04.1(ceea1114793f68).
> > Signed-off-by: Brad Figg <brad.figg@canonical.com>
> 
> Hi José,
> 
> We don't have any 3.19 based kernel that's currently supported, was the
> patch really backported for the version stated above?

	Yes it's, I didn't find any other 3.x kernel for xenial, 4.x is supposed 
not to be affected according to the information on the CVE. Is there any 
source I'm missing on the run?
 
> > Currently, notify_change() clears capabilities or IMA attributes by
> > calling security_inode_killpriv() before calling into ->setattr. Thus it
> > happens before any other permission checks in inode_change_ok() and user
> > is thus allowed to trigger clearing of capabilities or IMA attributes
> > for any file he can look up e.g. by calling chown for that file. This is
> > unexpected and can lead to user DoSing a system.
> > 
> > Fix the problem by calling security_inode_killpriv() at the end of
> > inode_change_ok() instead of from notify_change(). At that moment we are
> > sure user has permissions to do the requested change.
> > 
> > References: CVE-2015-1350
> > Signed-off-by: José Pekkarinen <jose.pekkarinen@canonical.com>
> 
> The body of the email doesn't follow our patch format for kernel SRU
> requests. Please re-submit the patch following the format as described at:
> 
> https://wiki.ubuntu.com/Kernel/Dev/StablePatchFormat

	I'll fix that for the following. Thanks!

	José.
Andy Whitcroft Aug. 2, 2018, 9:29 a.m. UTC | #5
On Thu, Aug 02, 2018 at 12:00:13PM +0300, José Pekkarinen wrote:
> On Thursday, 2 August 2018 08:44:30 EEST Khaled Elmously wrote:
> [snip]
> > > +	struct dentry 		*dentry = inode->i_sb->s_root;
> > 
> > I'm not sure I understand the above line. Can you please explain why it's
> > true?
> 
> 	Seems to be the only way to find the dentry struct associated to an inode, 
> in newer kernels most of the functions modified in this patch already receives 
> a dentry instead of a raw inode, but for the remaining cases something needs 
> to be tailored. I failed to find any macro to help with this endeavour.

That isn't 'the' dentry associated with an inode, that is the dentry
associated with root of the filesystem.  Surely you would have to
iterate over the inode->i_dentry list looking for a valid one.

-apw
Kleber Sacilotto de Souza Aug. 2, 2018, 12:25 p.m. UTC | #6
On 08/02/18 11:03, José Pekkarinen wrote:
> On Thursday, 2 August 2018 11:01:34 EEST Kleber Souza wrote:
>> On 08/01/18 16:58, José Pekkarinen wrote:
>>> fs: Avoid premature clearing of capabilities(030b533c4fd)
>>> Reviewed-by: Christoph Hellwig <hch@lst.de>
>>> by: Jan Kara <jack@suse.cz>
>>>
>>> It's applied on:
>>>
>>> UBUNTU: Ubuntu-lts-3.19.0-82.90~14.04.1(ceea1114793f68).
>>> Signed-off-by: Brad Figg <brad.figg@canonical.com>
>>
>> Hi José,
>>
>> We don't have any 3.19 based kernel that's currently supported, was the
>> patch really backported for the version stated above?
> 
> 	Yes it's, I didn't find any other 3.x kernel for xenial, 4.x is supposed 
> not to be affected according to the information on the CVE. Is there any 
> source I'm missing on the run?

That version you mentioned above seems to be a Vivid backport for
Trusty, we never had a 3.x kernel version for Xenial. We only do
backports and not forward ports of kernels.

According to the CVE page [1], the linux package is still affected on
Xenial (4.4) and on Trusty (3.13). So the linux trees that need to be
used for the backport are xenial/master-next [2] and trusty/master-next
[3] branches.


Thanks,
Kleber


[1]
https://people.canonical.com/~ubuntu-security/cve/2015/CVE-2015-1350.html

[2]
https://code.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/xenial/+ref/master-next

[3]
https://code.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/trusty/+ref/master-next

>  
>>> Currently, notify_change() clears capabilities or IMA attributes by
>>> calling security_inode_killpriv() before calling into ->setattr. Thus it
>>> happens before any other permission checks in inode_change_ok() and user
>>> is thus allowed to trigger clearing of capabilities or IMA attributes
>>> for any file he can look up e.g. by calling chown for that file. This is
>>> unexpected and can lead to user DoSing a system.
>>>
>>> Fix the problem by calling security_inode_killpriv() at the end of
>>> inode_change_ok() instead of from notify_change(). At that moment we are
>>> sure user has permissions to do the requested change.
>>>
>>> References: CVE-2015-1350
>>> Signed-off-by: José Pekkarinen <jose.pekkarinen@canonical.com>
>>
>> The body of the email doesn't follow our patch format for kernel SRU
>> requests. Please re-submit the patch following the format as described at:
>>
>> https://wiki.ubuntu.com/Kernel/Dev/StablePatchFormat
> 
> 	I'll fix that for the following. Thanks!
> 
> 	José.
> 
>
Khalid Elmously Aug. 6, 2018, 4:11 a.m. UTC | #7
On 2018-08-02 10:29:59 , Andy Whitcroft wrote:
> On Thu, Aug 02, 2018 at 12:00:13PM +0300, José Pekkarinen wrote:
> > On Thursday, 2 August 2018 08:44:30 EEST Khaled Elmously wrote:
> > [snip]
> > > > +	struct dentry 		*dentry = inode->i_sb->s_root;
> > > 
> > > I'm not sure I understand the above line. Can you please explain why it's
> > > true?
> > 
> > 	Seems to be the only way to find the dentry struct associated to an inode, 
> > in newer kernels most of the functions modified in this patch already receives 
> > a dentry instead of a raw inode, but for the remaining cases something needs 
> > to be tailored. I failed to find any macro to help with this endeavour.
> 
> That isn't 'the' dentry associated with an inode, that is the dentry
> associated with root of the filesystem.  Surely you would have to
> iterate over the inode->i_dentry list looking for a valid one.
>

José, as Andy pointed out, this isn't the right dentry for the inode. With this change you'd be doing a permission check against a completely different inode.

I wasn't really sure if iterating over the dentry list is correct either, but I think it might actually be OK, since you're going to re-dereference the inode again as soon as you're in inode_change_ok() and from my understanding it should always lead you back to the same inode again.


For what it's worth, d_find_alias could be used instead of iterating manually.

Note you have a similar change made for fuse_do_truncate() - that would need to be fixed as well.

Once those are fixed, I think I would be OK with this change for fixing CVE-2015-1350.


> -apw

Thanks
Khaled
diff mbox series

Patch

diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 711d744..65fcb86 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -1094,7 +1094,7 @@  static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
 	struct p9_wstat wstat;
 
 	p9_debug(P9_DEBUG_VFS, "\n");
-	retval = inode_change_ok(dentry->d_inode, iattr);
+	retval = inode_change_ok(dentry, iattr);
 	if (retval)
 		return retval;
 
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index 1254c7b..961a5b7 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -560,7 +560,7 @@  int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
 
 	p9_debug(P9_DEBUG_VFS, "\n");
 
-	retval = inode_change_ok(inode, iattr);
+	retval = inode_change_ok(dentry, iattr);
 	if (retval)
 		return retval;
 
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
index b9acada..fceb187 100644
--- a/fs/adfs/inode.c
+++ b/fs/adfs/inode.c
@@ -303,7 +303,7 @@  adfs_notify_change(struct dentry *dentry, struct iattr *attr)
 	unsigned int ia_valid = attr->ia_valid;
 	int error;
 	
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 
 	/*
 	 * we can't change the UID or GID of any file -
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index d0609a2..e929583 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -219,7 +219,7 @@  affs_notify_change(struct dentry *dentry, struct iattr *attr)
 
 	pr_debug("notify_change(%lu,0x%x)\n", inode->i_ino, attr->ia_valid);
 
-	error = inode_change_ok(inode,attr);
+	error = inode_change_ok(dentry,attr);
 	if (error)
 		goto out;
 
diff --git a/fs/attr.c b/fs/attr.c
index 6530ced..72ec99f 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -28,8 +28,9 @@ 
  * Should be called as the first thing in ->setattr implementations,
  * possibly after taking additional locks.
  */
-int inode_change_ok(const struct inode *inode, struct iattr *attr)
+int inode_change_ok(struct dentry *dentry, struct iattr *attr)
 {
+	struct inode *inode = d_inode(dentry);
 	unsigned int ia_valid = attr->ia_valid;
 
 	/*
@@ -44,7 +45,7 @@  int inode_change_ok(const struct inode *inode, struct iattr *attr)
 
 	/* If force is set do it anyway. */
 	if (ia_valid & ATTR_FORCE)
-		return 0;
+		goto kill_priv;
 
 	/* Make sure a caller can chown. */
 	if ((ia_valid & ATTR_UID) &&
@@ -77,6 +78,16 @@  int inode_change_ok(const struct inode *inode, struct iattr *attr)
 			return -EPERM;
 	}
 
+kill_priv:
+	/* User has permission for the change */
+	if (ia_valid & ATTR_KILL_PRIV) {
+		int error;
+
+		error = security_inode_killpriv(dentry);
+		if (error)
+			return error;
+	}
+
 	return 0;
 }
 EXPORT_SYMBOL(inode_change_ok);
@@ -217,13 +228,11 @@  int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
 	if (!(ia_valid & ATTR_MTIME_SET))
 		attr->ia_mtime = now;
 	if (ia_valid & ATTR_KILL_PRIV) {
-		attr->ia_valid &= ~ATTR_KILL_PRIV;
-		ia_valid &= ~ATTR_KILL_PRIV;
 		error = security_inode_need_killpriv(dentry);
-		if (error > 0)
-			error = security_inode_killpriv(dentry);
-		if (error)
+		if (error < 0)
 			return error;
+		if (error == 0)
+			ia_valid = attr->ia_valid &= ~ATTR_KILL_PRIV;
 	}
 
 	/*
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 508a4ca..47d3999 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4805,7 +4805,7 @@  static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
 	if (btrfs_root_readonly(root))
 		return -EROFS;
 
-	err = inode_change_ok(inode, attr);
+	err = inode_change_ok(dentry, attr);
 	if (err)
 		return err;
 
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index f61a741..4003158 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1727,7 +1727,7 @@  int ceph_setattr(struct dentry *dentry, struct iattr *attr)
 	if (ceph_snap(inode) != CEPH_NOSNAP)
 		return -EROFS;
 
-	err = inode_change_ok(inode, attr);
+	err = inode_change_ok(dentry, attr);
 	if (err != 0)
 		return err;
 
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 5772aa0..0e0766c 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -2092,7 +2092,7 @@  cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
 		attrs->ia_valid |= ATTR_FORCE;
 
-	rc = inode_change_ok(inode, attrs);
+	rc = inode_change_ok(direntry, attrs);
 	if (rc < 0)
 		goto out;
 
@@ -2233,7 +2233,7 @@  cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
 		attrs->ia_valid |= ATTR_FORCE;
 
-	rc = inode_change_ok(inode, attrs);
+	rc = inode_change_ok(direntry, attrs);
 	if (rc < 0) {
 		free_xid(xid);
 		return rc;
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 1686dc2..3cf2cdd 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -943,7 +943,7 @@  static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
 	}
 	mutex_unlock(&crypt_stat->cs_mutex);
 
-	rc = inode_change_ok(inode, ia);
+	rc = inode_change_ok(dentry, ia);
 	if (rc)
 		goto out;
 	if (ia->ia_valid & ATTR_SIZE) {
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index f1d3d4e..4bb7077 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -1039,7 +1039,7 @@  int exofs_setattr(struct dentry *dentry, struct iattr *iattr)
 	if (unlikely(error))
 		return error;
 
-	error = inode_change_ok(inode, iattr);
+	error = inode_change_ok(dentry, iattr);
 	if (unlikely(error))
 		return error;
 
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 36d35c3..c75bbd4 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -1547,7 +1547,7 @@  int ext2_setattr(struct dentry *dentry, struct iattr *iattr)
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = inode_change_ok(inode, iattr);
+	error = inode_change_ok(dentry, iattr);
 	if (error)
 		return error;
 
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 2c6ccc4..421b537 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -3244,7 +3244,7 @@  int ext3_setattr(struct dentry *dentry, struct iattr *attr)
 	int error, rc = 0;
 	const unsigned int ia_valid = attr->ia_valid;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 77c8ce1..51ea725 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4482,7 +4482,7 @@  int ext4_setattr(struct dentry *dentry, struct iattr *attr)
 	int orphan = 0;
 	const unsigned int ia_valid = attr->ia_valid;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 3c27e0e..1ca06de 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -600,7 +600,7 @@  int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
 	struct f2fs_inode_info *fi = F2FS_I(inode);
 	int err;
 
-	err = inode_change_ok(inode, attr);
+	err = inode_change_ok(dentry, attr);
 	if (err)
 		return err;
 
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 8429c68..626f9d4 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -394,7 +394,7 @@  int fat_setattr(struct dentry *dentry, struct iattr *attr)
 			attr->ia_valid &= ~TIMES_SET_FLAGS;
 	}
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	attr->ia_valid = ia_valid;
 	if (error) {
 		if (sbi->options.quiet)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 08e7b1a..7b2a04c 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1602,9 +1602,10 @@  int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
  * vmtruncate() doesn't allow for this case, so do the rlimit checking
  * and the actual truncation by hand.
  */
-int fuse_do_setattr(struct inode *inode, struct iattr *attr,
+int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
 		    struct file *file)
 {
+	struct inode *inode = d_inode(dentry);
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	struct fuse_inode *fi = get_fuse_inode(inode);
 	FUSE_ARGS(args);
@@ -1619,7 +1620,7 @@  int fuse_do_setattr(struct inode *inode, struct iattr *attr,
 	if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
 		attr->ia_valid |= ATTR_FORCE;
 
-	err = inode_change_ok(inode, attr);
+	err = inode_change_ok(dentry, attr);
 	if (err)
 		return err;
 
@@ -1710,17 +1711,17 @@  error:
 	return err;
 }
 
-static int fuse_setattr(struct dentry *entry, struct iattr *attr)
+static int fuse_setattr(struct dentry *dentry, struct iattr *attr)
 {
-	struct inode *inode = entry->d_inode;
+	struct inode *inode = dentry->d_inode;
 
 	if (!fuse_allow_current_process(get_fuse_conn(inode)))
 		return -EACCES;
 
 	if (attr->ia_valid & ATTR_FILE)
-		return fuse_do_setattr(inode, attr, attr->ia_file);
+		return fuse_do_setattr(dentry, attr, attr->ia_file);
 	else
-		return fuse_do_setattr(inode, attr, NULL);
+		return fuse_do_setattr(dentry, attr, NULL);
 }
 
 static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 60788e9..dbcc12a 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2782,6 +2782,7 @@  int fuse_notify_poll_wakeup(struct fuse_conn *fc,
 static void fuse_do_truncate(struct file *file)
 {
 	struct inode *inode = file->f_mapping->host;
+	struct dentry *dentry = inode->i_sb->s_root;
 	struct iattr attr;
 
 	attr.ia_valid = ATTR_SIZE;
@@ -2790,7 +2791,7 @@  static void fuse_do_truncate(struct file *file)
 	attr.ia_file = file;
 	attr.ia_valid |= ATTR_FILE;
 
-	fuse_do_setattr(inode, &attr, file);
+	fuse_do_setattr(dentry, &attr, file);
 }
 
 static inline loff_t fuse_round_up(loff_t off)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 1cdfb07..195c942 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -903,7 +903,7 @@  bool fuse_write_update_size(struct inode *inode, loff_t pos);
 int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
 int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
 
-int fuse_do_setattr(struct inode *inode, struct iattr *attr,
+int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
 		    struct file *file);
 
 void fuse_set_initialized(struct fuse_conn *fc);
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 9054002..c7fb53e 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1760,7 +1760,7 @@  static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		goto out;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		goto out;
 
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index d0929bc..9b42ee0 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -604,7 +604,7 @@  int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr)
 	struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
 	int error;
 
-	error = inode_change_ok(inode, attr); /* basic permission checks */
+	error = inode_change_ok(dentry, attr); /* basic permission checks */
 	if (error)
 		return error;
 
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 0cf786f..a74ff43 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -247,7 +247,7 @@  static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 75967b7e..bbf633f 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -802,7 +802,7 @@  static int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
 
 	int fd = HOSTFS_I(inode)->fd;
 
-	err = inode_change_ok(inode, attr);
+	err = inode_change_ok(dentry, attr);
 	if (err)
 		return err;
 
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
index 7ce4b74..8e6942e 100644
--- a/fs/hpfs/inode.c
+++ b/fs/hpfs/inode.c
@@ -272,7 +272,7 @@  int hpfs_setattr(struct dentry *dentry, struct iattr *attr)
 	if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size)
 		goto out_unlock;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		goto out_unlock;
 
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 5eba47f..a4ddc94 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -429,7 +429,7 @@  static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
 
 	BUG_ON(!inode);
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 601afd1..870ed4f 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -193,7 +193,7 @@  int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
 	struct inode *inode = dentry->d_inode;
 	int rc;
 
-	rc = inode_change_ok(inode, iattr);
+	rc = inode_change_ok(dentry, iattr);
 	if (rc)
 		return rc;
 
diff --git a/fs/jfs/file.c b/fs/jfs/file.c
index 33aa0cc..2ede547 100644
--- a/fs/jfs/file.c
+++ b/fs/jfs/file.c
@@ -103,7 +103,7 @@  int jfs_setattr(struct dentry *dentry, struct iattr *iattr)
 	struct inode *inode = dentry->d_inode;
 	int rc;
 
-	rc = inode_change_ok(inode, iattr);
+	rc = inode_change_ok(dentry, iattr);
 	if (rc)
 		return rc;
 
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index 5b8ab29..325234f 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -131,7 +131,7 @@  int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr)
 		return -EINVAL;
 
 	mutex_lock(&kernfs_mutex);
-	error = inode_change_ok(inode, iattr);
+	error = inode_change_ok(dentry, iattr);
 	if (error)
 		goto out;
 
diff --git a/fs/libfs.c b/fs/libfs.c
index 3a5deb3..5509af4 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -371,7 +371,7 @@  int simple_setattr(struct dentry *dentry, struct iattr *iattr)
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = inode_change_ok(inode, iattr);
+	error = inode_change_ok(dentry, iattr);
 	if (error)
 		return error;
 
diff --git a/fs/logfs/file.c b/fs/logfs/file.c
index 8538752..bf8b09d 100644
--- a/fs/logfs/file.c
+++ b/fs/logfs/file.c
@@ -244,7 +244,7 @@  static int logfs_setattr(struct dentry *dentry, struct iattr *attr)
 	struct inode *inode = dentry->d_inode;
 	int err = 0;
 
-	err = inode_change_ok(inode, attr);
+	err = inode_change_ok(dentry, attr);
 	if (err)
 		return err;
 
diff --git a/fs/minix/file.c b/fs/minix/file.c
index a967de0..57bd2a4 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -28,7 +28,7 @@  static int minix_setattr(struct dentry *dentry, struct iattr *attr)
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index e31e589..35a6d37 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -885,7 +885,7 @@  int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
 	/* ageing the dentry to force validation */
 	ncp_age_dentry(server, dentry);
 
-	result = inode_change_ok(inode, attr);
+	result = inode_change_ok(dentry, attr);
 	if (result < 0)
 		goto out;
 
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 5685c67..38e5b6a 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -300,7 +300,7 @@  commit_metadata(struct svc_fh *fhp)
  * NFS semantics and what Linux expects.
  */
 static void
-nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
+nfsd_sanitize_attrs(struct dentry *dentry, struct iattr *iap)
 {
 	/*
 	 * NFSv2 does not differentiate between "set-[ac]time-to-now"
@@ -313,6 +313,7 @@  nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
 	 * We only call inode_change_ok as the last test as technically
 	 * it is not an interface that we should be using.
 	 */
+	struct inode *inode = d_inode(dentry);
 #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
 #define	MAX_TOUCH_TIME_ERROR (30*60)
 	if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
@@ -328,7 +329,7 @@  nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
 		if (delta < 0)
 			delta = -delta;
 		if (delta < MAX_TOUCH_TIME_ERROR &&
-		    inode_change_ok(inode, iap) != 0) {
+		    inode_change_ok(dentry, iap) != 0) {
 			/*
 			 * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
 			 * This will cause notify_change to set these times
@@ -435,7 +436,7 @@  nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
 	if (!iap->ia_valid)
 		goto out;
 
-	nfsd_sanitize_attrs(inode, iap);
+	nfsd_sanitize_attrs(dentry, iap);
 
 	/*
 	 * The size case is special, it changes the file in addition to the
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 8b59695..0791dd4 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -840,7 +840,7 @@  int nilfs_setattr(struct dentry *dentry, struct iattr *iattr)
 	struct super_block *sb = inode->i_sb;
 	int err;
 
-	err = inode_change_ok(inode, iattr);
+	err = inode_change_ok(dentry, iattr);
 	if (err)
 		return err;
 
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 898b994..6d51746 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -2894,7 +2894,7 @@  int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
 	int err;
 	unsigned int ia_valid = attr->ia_valid;
 
-	err = inode_change_ok(vi, attr);
+	err = inode_change_ok(dentry, attr);
 	if (err)
 		goto out;
 	/* We do not support NTFS ACLs yet. */
diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
index 57c40e3..4473e95 100644
--- a/fs/ocfs2/dlmfs/dlmfs.c
+++ b/fs/ocfs2/dlmfs/dlmfs.c
@@ -211,7 +211,7 @@  static int dlmfs_file_setattr(struct dentry *dentry, struct iattr *attr)
 	struct inode *inode = dentry->d_inode;
 
 	attr->ia_valid &= ~ATTR_SIZE;
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 7e5aa39..9067886 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1143,7 +1143,7 @@  int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
 	if (!(attr->ia_valid & OCFS2_VALID_ATTRS))
 		return 0;
 
-	status = inode_change_ok(inode, attr);
+	status = inode_change_ok(dentry, attr);
 	if (status)
 		return status;
 
diff --git a/fs/omfs/file.c b/fs/omfs/file.c
index 902e885..db6314c 100644
--- a/fs/omfs/file.c
+++ b/fs/omfs/file.c
@@ -351,7 +351,7 @@  static int omfs_setattr(struct dentry *dentry, struct iattr *attr)
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d022159..7abfc57 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -509,7 +509,7 @@  int proc_setattr(struct dentry *dentry, struct iattr *attr)
 	if (attr->ia_valid & ATTR_MODE)
 		return -EPERM;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index aaabab4..5f24651 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -105,7 +105,7 @@  static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
 	struct proc_dir_entry *de = PDE(inode);
 	int error;
 
-	error = inode_change_ok(inode, iattr);
+	error = inode_change_ok(dentry, iattr);
 	if (error)
 		return error;
 
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 3f7dc3e..be5b366 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -753,7 +753,7 @@  static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
 	if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
 		return -EPERM;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
index bbafbde..daba062 100644
--- a/fs/ramfs/file-nommu.c
+++ b/fs/ramfs/file-nommu.c
@@ -163,7 +163,7 @@  static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia)
 	int ret = 0;
 
 	/* POSIX UID/GID verification for setting inode attributes */
-	ret = inode_change_ok(inode, ia);
+	ret = inode_change_ok(dentry, ia);
 	if (ret)
 		return ret;
 
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index a7eec98..80f5192 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -3312,7 +3312,7 @@  int reiserfs_setattr(struct dentry *dentry, struct iattr *attr)
 	unsigned int ia_valid;
 	int error;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/sysv/file.c b/fs/sysv/file.c
index b00811c..09099b5 100644
--- a/fs/sysv/file.c
+++ b/fs/sysv/file.c
@@ -35,7 +35,7 @@  static int sysv_setattr(struct dentry *dentry, struct iattr *attr)
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 538519e..b663d11 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1262,7 +1262,7 @@  int ubifs_setattr(struct dentry *dentry, struct iattr *attr)
 
 	dbg_gen("ino %lu, mode %#x, ia_valid %#x",
 		inode->i_ino, inode->i_mode, attr->ia_valid);
-	err = inode_change_ok(inode, attr);
+	err = inode_change_ok(dentry, attr);
 	if (err)
 		return err;
 
diff --git a/fs/udf/file.c b/fs/udf/file.c
index 08f3555..e2a382e 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -258,7 +258,7 @@  static int udf_setattr(struct dentry *dentry, struct iattr *attr)
 	struct inode *inode = dentry->d_inode;
 	int error;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c
index f04f89f..926ba19 100644
--- a/fs/ufs/truncate.c
+++ b/fs/ufs/truncate.c
@@ -496,7 +496,7 @@  int ufs_setattr(struct dentry *dentry, struct iattr *attr)
 	unsigned int ia_valid = attr->ia_valid;
 	int error;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;
 
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 17d057c..ea6af4f 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -530,6 +530,7 @@  xfs_setattr_nonsize(
 {
 	xfs_mount_t		*mp = ip->i_mount;
 	struct inode		*inode = VFS_I(ip);
+	struct dentry 		*dentry = inode->i_sb->s_root;
 	int			mask = iattr->ia_valid;
 	xfs_trans_t		*tp;
 	int			error;
@@ -548,7 +549,7 @@  xfs_setattr_nonsize(
 		if (XFS_FORCED_SHUTDOWN(mp))
 			return -EIO;
 
-		error = inode_change_ok(inode, iattr);
+		error = inode_change_ok(dentry, iattr);
 		if (error)
 			return error;
 	}
@@ -736,6 +737,7 @@  xfs_setattr_size(
 {
 	struct xfs_mount	*mp = ip->i_mount;
 	struct inode		*inode = VFS_I(ip);
+	struct dentry		*dentry = inode->i_sb->s_root;
 	xfs_off_t		oldsize, newsize;
 	struct xfs_trans	*tp;
 	int			error;
@@ -751,7 +753,7 @@  xfs_setattr_size(
 	if (XFS_FORCED_SHUTDOWN(mp))
 		return -EIO;
 
-	error = inode_change_ok(inode, iattr);
+	error = inode_change_ok(dentry, iattr);
 	if (error)
 		return error;
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 92c88b8..d12c959 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2690,7 +2690,7 @@  extern int buffer_migrate_page(struct address_space *,
 #define buffer_migrate_page NULL
 #endif
 
-extern int inode_change_ok(const struct inode *, struct iattr *);
+extern int inode_change_ok(struct dentry *, struct iattr *);
 extern int inode_newsize_ok(const struct inode *, loff_t offset);
 extern void setattr_copy(struct inode *inode, const struct iattr *attr);
 
diff --git a/mm/shmem.c b/mm/shmem.c
index 993e6ba..fc6cab1 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -553,7 +553,7 @@  static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
 	struct shmem_inode_info *info = SHMEM_I(inode);
 	int error;
 
-	error = inode_change_ok(inode, attr);
+	error = inode_change_ok(dentry, attr);
 	if (error)
 		return error;