From 941d935ac7636911a3fd8fa80e758e52b0b11e20 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 11 Jun 2019 18:09:28 +0300 Subject: ovl: fix wrong flags check in FS_IOC_FS[SG]ETXATTR ioctls The ioctl argument was parsed as the wrong type. Fixes: b21d9c435f93 ("ovl: support the FS_IOC_FS[SG]ETXATTR ioctls") Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/file.c | 91 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 340a6ad45914..75d8d00fa087 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -409,37 +409,16 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd, return ret; } -static unsigned int ovl_get_inode_flags(struct inode *inode) -{ - unsigned int flags = READ_ONCE(inode->i_flags); - unsigned int ovl_iflags = 0; - - if (flags & S_SYNC) - ovl_iflags |= FS_SYNC_FL; - if (flags & S_APPEND) - ovl_iflags |= FS_APPEND_FL; - if (flags & S_IMMUTABLE) - ovl_iflags |= FS_IMMUTABLE_FL; - if (flags & S_NOATIME) - ovl_iflags |= FS_NOATIME_FL; - - return ovl_iflags; -} - static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd, - unsigned long arg) + unsigned long arg, unsigned int iflags) { long ret; struct inode *inode = file_inode(file); - unsigned int flags; - unsigned int old_flags; + unsigned int old_iflags; if (!inode_owner_or_capable(inode)) return -EACCES; - if (get_user(flags, (int __user *) arg)) - return -EFAULT; - ret = mnt_want_write_file(file); if (ret) return ret; @@ -448,8 +427,8 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd, /* Check the capability before cred override */ ret = -EPERM; - old_flags = ovl_get_inode_flags(inode); - if (((flags ^ old_flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) && + old_iflags = READ_ONCE(inode->i_flags); + if (((iflags ^ old_iflags) & (S_APPEND | S_IMMUTABLE)) && !capable(CAP_LINUX_IMMUTABLE)) goto unlock; @@ -469,6 +448,63 @@ unlock: } +static unsigned int ovl_fsflags_to_iflags(unsigned int flags) +{ + unsigned int iflags = 0; + + if (flags & FS_SYNC_FL) + iflags |= S_SYNC; + if (flags & FS_APPEND_FL) + iflags |= S_APPEND; + if (flags & FS_IMMUTABLE_FL) + iflags |= S_IMMUTABLE; + if (flags & FS_NOATIME_FL) + iflags |= S_NOATIME; + + return iflags; +} + +static long ovl_ioctl_set_fsflags(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned int flags; + + if (get_user(flags, (int __user *) arg)) + return -EFAULT; + + return ovl_ioctl_set_flags(file, cmd, arg, + ovl_fsflags_to_iflags(flags)); +} + +static unsigned int ovl_fsxflags_to_iflags(unsigned int xflags) +{ + unsigned int iflags = 0; + + if (xflags & FS_XFLAG_SYNC) + iflags |= S_SYNC; + if (xflags & FS_XFLAG_APPEND) + iflags |= S_APPEND; + if (xflags & FS_XFLAG_IMMUTABLE) + iflags |= S_IMMUTABLE; + if (xflags & FS_XFLAG_NOATIME) + iflags |= S_NOATIME; + + return iflags; +} + +static long ovl_ioctl_set_fsxflags(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct fsxattr fa; + + memset(&fa, 0, sizeof(fa)); + if (copy_from_user(&fa, (void __user *) arg, sizeof(fa))) + return -EFAULT; + + return ovl_ioctl_set_flags(file, cmd, arg, + ovl_fsxflags_to_iflags(fa.fsx_xflags)); +} + static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; @@ -480,8 +516,11 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case FS_IOC_SETFLAGS: + ret = ovl_ioctl_set_fsflags(file, cmd, arg); + break; + case FS_IOC_FSSETXATTR: - ret = ovl_ioctl_set_flags(file, cmd, arg); + ret = ovl_ioctl_set_fsxflags(file, cmd, arg); break; default: -- cgit