Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszer...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 21 Jul 2017 23:24:22 +0000 (16:24 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 21 Jul 2017 23:24:22 +0000 (16:24 -0700)
Pull overlayfs fixes from Miklos Szeredi:
 "This fixes a crash with SELinux and several other old and new bugs"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: check for bad and whiteout index on lookup
  ovl: do not cleanup directory and whiteout index entries
  ovl: fix xattr get and set with selinux
  ovl: remove unneeded check for IS_ERR()
  ovl: fix origin verification of index dir
  ovl: mark parent impure on ovl_link()
  ovl: fix random return value on mount

fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/namei.c
fs/overlayfs/overlayfs.h
fs/overlayfs/readdir.c
fs/overlayfs/super.c
fs/overlayfs/util.c

index 641d9ee..48b70e6 100644 (file)
@@ -481,17 +481,30 @@ out_cleanup:
 }
 
 static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
-                             struct cattr *attr, struct dentry *hardlink)
+                             struct cattr *attr, struct dentry *hardlink,
+                             bool origin)
 {
        int err;
        const struct cred *old_cred;
        struct cred *override_cred;
+       struct dentry *parent = dentry->d_parent;
 
-       err = ovl_copy_up(dentry->d_parent);
+       err = ovl_copy_up(parent);
        if (err)
                return err;
 
        old_cred = ovl_override_creds(dentry->d_sb);
+
+       /*
+        * When linking a file with copy up origin into a new parent, mark the
+        * new parent dir "impure".
+        */
+       if (origin) {
+               err = ovl_set_impure(parent, ovl_dentry_upper(parent));
+               if (err)
+                       goto out_revert_creds;
+       }
+
        err = -ENOMEM;
        override_cred = prepare_creds();
        if (override_cred) {
@@ -550,7 +563,7 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
        inode_init_owner(inode, dentry->d_parent->d_inode, mode);
        attr.mode = inode->i_mode;
 
-       err = ovl_create_or_link(dentry, inode, &attr, NULL);
+       err = ovl_create_or_link(dentry, inode, &attr, NULL, false);
        if (err)
                iput(inode);
 
@@ -609,7 +622,8 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
        inode = d_inode(old);
        ihold(inode);
 
-       err = ovl_create_or_link(new, inode, NULL, ovl_dentry_upper(old));
+       err = ovl_create_or_link(new, inode, NULL, ovl_dentry_upper(old),
+                                ovl_type_origin(old));
        if (err)
                iput(inode);
 
index 69f4fc2..5bc7164 100644 (file)
@@ -202,37 +202,38 @@ bool ovl_is_private_xattr(const char *name)
                       sizeof(OVL_XATTR_PREFIX) - 1) == 0;
 }
 
-int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value,
-                 size_t size, int flags)
+int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
+                 const void *value, size_t size, int flags)
 {
        int err;
-       struct path realpath;
-       enum ovl_path_type type = ovl_path_real(dentry, &realpath);
+       struct dentry *upperdentry = ovl_i_dentry_upper(inode);
+       struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
        const struct cred *old_cred;
 
        err = ovl_want_write(dentry);
        if (err)
                goto out;
 
-       if (!value && !OVL_TYPE_UPPER(type)) {
-               err = vfs_getxattr(realpath.dentry, name, NULL, 0);
+       if (!value && !upperdentry) {
+               err = vfs_getxattr(realdentry, name, NULL, 0);
                if (err < 0)
                        goto out_drop_write;
        }
 
-       err = ovl_copy_up(dentry);
-       if (err)
-               goto out_drop_write;
+       if (!upperdentry) {
+               err = ovl_copy_up(dentry);
+               if (err)
+                       goto out_drop_write;
 
-       if (!OVL_TYPE_UPPER(type))
-               ovl_path_upper(dentry, &realpath);
+               realdentry = ovl_dentry_upper(dentry);
+       }
 
        old_cred = ovl_override_creds(dentry->d_sb);
        if (value)
-               err = vfs_setxattr(realpath.dentry, name, value, size, flags);
+               err = vfs_setxattr(realdentry, name, value, size, flags);
        else {
                WARN_ON(flags != XATTR_REPLACE);
-               err = vfs_removexattr(realpath.dentry, name);
+               err = vfs_removexattr(realdentry, name);
        }
        revert_creds(old_cred);
 
@@ -242,12 +243,13 @@ out:
        return err;
 }
 
-int ovl_xattr_get(struct dentry *dentry, const char *name,
+int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
                  void *value, size_t size)
 {
-       struct dentry *realdentry = ovl_dentry_real(dentry);
        ssize_t res;
        const struct cred *old_cred;
+       struct dentry *realdentry =
+               ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry);
 
        old_cred = ovl_override_creds(dentry->d_sb);
        res = vfs_getxattr(realdentry, name, value, size);
index 9bc0e58..8aef2b3 100644 (file)
@@ -397,8 +397,19 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack,
        if (!d_inode(index))
                return 0;
 
-       err = -EISDIR;
-       if (d_is_dir(index))
+       /*
+        * Directory index entries are going to be used for looking up
+        * redirected upper dirs by lower dir fh when decoding an overlay
+        * file handle of a merge dir. Whiteout index entries are going to be
+        * used as an indication that an exported overlay file handle should
+        * be treated as stale (i.e. after unlink of the overlay inode).
+        * We don't know the verification rules for directory and whiteout
+        * index entries, because they have not been implemented yet, so return
+        * EROFS if those entries are found to avoid corrupting an index that
+        * was created by a newer kernel.
+        */
+       err = -EROFS;
+       if (d_is_dir(index) || ovl_is_whiteout(index))
                goto fail;
 
        err = -EINVAL;
@@ -436,8 +447,8 @@ out:
        return err;
 
 fail:
-       pr_warn_ratelimited("overlayfs: failed to verify index (%pd2, err=%i)\n",
-                           index, err);
+       pr_warn_ratelimited("overlayfs: failed to verify index (%pd2, ftype=%x, err=%i)\n",
+                           index, d_inode(index)->i_mode & S_IFMT, err);
        goto out;
 }
 
@@ -502,6 +513,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
                goto out;
        }
 
+       inode = d_inode(index);
        if (d_is_negative(index)) {
                if (upper && d_inode(origin)->i_nlink > 1) {
                        pr_warn_ratelimited("overlayfs: hard link with origin but no index (ino=%lu).\n",
@@ -511,11 +523,22 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
 
                dput(index);
                index = NULL;
-       } else if (upper && d_inode(index) != d_inode(upper)) {
-               inode = d_inode(index);
-               pr_warn_ratelimited("overlayfs: wrong index found (index ino: %lu, upper ino: %lu).\n",
-                                   d_inode(index)->i_ino,
-                                   d_inode(upper)->i_ino);
+       } else if (upper && d_inode(upper) != inode) {
+               pr_warn_ratelimited("overlayfs: wrong index found (index=%pd2, ino=%lu, upper ino=%lu).\n",
+                                   index, inode->i_ino, d_inode(upper)->i_ino);
+               goto fail;
+       } else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
+                  ((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
+               /*
+                * Index should always be of the same file type as origin
+                * except for the case of a whiteout index. A whiteout
+                * index should only exist if all lower aliases have been
+                * unlinked, which means that finding a lower origin on lookup
+                * whose index is a whiteout should be treated as an error.
+                */
+               pr_warn_ratelimited("overlayfs: bad index found (index=%pd2, ftype=%x, origin ftype=%x).\n",
+                                   index, d_inode(index)->i_mode & S_IFMT,
+                                   d_inode(origin)->i_mode & S_IFMT);
                goto fail;
        }
 
index 60d2660..e927a62 100644 (file)
@@ -47,7 +47,8 @@ enum ovl_flag {
 /* Is the real inode encoded in fid an upper inode? */
 #define OVL_FH_FLAG_PATH_UPPER (1 << 2)
 
-#define OVL_FH_FLAG_ALL (OVL_FH_FLAG_BIG_ENDIAN | OVL_FH_FLAG_ANY_ENDIAN)
+#define OVL_FH_FLAG_ALL (OVL_FH_FLAG_BIG_ENDIAN | OVL_FH_FLAG_ANY_ENDIAN | \
+                        OVL_FH_FLAG_PATH_UPPER)
 
 #if defined(__LITTLE_ENDIAN)
 #define OVL_FH_FLAG_CPU_ENDIAN 0
@@ -199,6 +200,7 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
 struct dentry *ovl_dentry_upper(struct dentry *dentry);
 struct dentry *ovl_dentry_lower(struct dentry *dentry);
 struct dentry *ovl_dentry_real(struct dentry *dentry);
+struct dentry *ovl_i_dentry_upper(struct inode *inode);
 struct inode *ovl_inode_upper(struct inode *inode);
 struct inode *ovl_inode_lower(struct inode *inode);
 struct inode *ovl_inode_real(struct inode *inode);
@@ -270,9 +272,9 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr);
 int ovl_getattr(const struct path *path, struct kstat *stat,
                u32 request_mask, unsigned int flags);
 int ovl_permission(struct inode *inode, int mask);
-int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value,
-                 size_t size, int flags);
-int ovl_xattr_get(struct dentry *dentry, const char *name,
+int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
+                 const void *value, size_t size, int flags);
+int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
                  void *value, size_t size);
 ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
 struct posix_acl *ovl_get_acl(struct inode *inode, int type);
index 0298463..3d424a5 100644 (file)
@@ -703,7 +703,10 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
                        err = PTR_ERR(index);
                        break;
                }
-               if (ovl_verify_index(index, lowerstack, numlower)) {
+               err = ovl_verify_index(index, lowerstack, numlower);
+               if (err) {
+                       if (err == -EROFS)
+                               break;
                        err = ovl_cleanup(dir, index);
                        if (err)
                                break;
index 44dc2d6..d86e89f 100644 (file)
@@ -692,7 +692,7 @@ ovl_posix_acl_xattr_get(const struct xattr_handler *handler,
                        struct dentry *dentry, struct inode *inode,
                        const char *name, void *buffer, size_t size)
 {
-       return ovl_xattr_get(dentry, handler->name, buffer, size);
+       return ovl_xattr_get(dentry, inode, handler->name, buffer, size);
 }
 
 static int __maybe_unused
@@ -742,7 +742,7 @@ ovl_posix_acl_xattr_set(const struct xattr_handler *handler,
                        return err;
        }
 
-       err = ovl_xattr_set(dentry, handler->name, value, size, flags);
+       err = ovl_xattr_set(dentry, inode, handler->name, value, size, flags);
        if (!err)
                ovl_copyattr(ovl_inode_real(inode), inode);
 
@@ -772,7 +772,7 @@ static int ovl_other_xattr_get(const struct xattr_handler *handler,
                               struct dentry *dentry, struct inode *inode,
                               const char *name, void *buffer, size_t size)
 {
-       return ovl_xattr_get(dentry, name, buffer, size);
+       return ovl_xattr_get(dentry, inode, name, buffer, size);
 }
 
 static int ovl_other_xattr_set(const struct xattr_handler *handler,
@@ -780,7 +780,7 @@ static int ovl_other_xattr_set(const struct xattr_handler *handler,
                               const char *name, const void *value,
                               size_t size, int flags)
 {
-       return ovl_xattr_set(dentry, name, value, size, flags);
+       return ovl_xattr_set(dentry, inode, name, value, size, flags);
 }
 
 static const struct xattr_handler __maybe_unused
@@ -1058,10 +1058,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 
                ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry,
                                                   OVL_INDEXDIR_NAME, true);
-               err = PTR_ERR(ufs->indexdir);
-               if (IS_ERR(ufs->indexdir))
-                       goto out_put_lower_mnt;
-
                if (ufs->indexdir) {
                        /* Verify upper root is index dir origin */
                        err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt,
@@ -1090,6 +1086,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        else
                sb->s_d_op = &ovl_dentry_operations;
 
+       err = -ENOMEM;
        ufs->creator_cred = cred = prepare_creds();
        if (!cred)
                goto out_put_indexdir;
index c492ba7..f46ad75 100644 (file)
@@ -157,9 +157,14 @@ struct dentry *ovl_dentry_real(struct dentry *dentry)
        return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry);
 }
 
+struct dentry *ovl_i_dentry_upper(struct inode *inode)
+{
+       return ovl_upperdentry_dereference(OVL_I(inode));
+}
+
 struct inode *ovl_inode_upper(struct inode *inode)
 {
-       struct dentry *upperdentry = ovl_upperdentry_dereference(OVL_I(inode));
+       struct dentry *upperdentry = ovl_i_dentry_upper(inode);
 
        return upperdentry ? d_inode(upperdentry) : NULL;
 }