Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99313414 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 0151ef00 0e082555
Loading
Loading
Loading
Loading
+18 −4
Original line number Diff line number Diff line
@@ -481,17 +481,30 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
}

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);

+17 −15
Original line number Diff line number Diff line
@@ -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;
	}

	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 @@ int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value,
	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);
+32 −9
Original line number Diff line number Diff line
@@ -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 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack,
	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;
	}

+6 −4
Original line number Diff line number Diff line
@@ -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);
+4 −1
Original line number Diff line number Diff line
@@ -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;
Loading