Commit 28fb80f0 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull overlayfs update from Miklos Szeredi:

 - Fix a regression in getting the path of an open file (e.g. in
   /proc/PID/maps) for a nested overlayfs setup (André Almeida)

 - Support data-only layers and verity in a user namespace (unprivileged
   composefs use case)

 - Fix a gcc warning (Kees)

 - Cleanups

* tag 'ovl-update-v2-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: Annotate struct ovl_entry with __counted_by()
  ovl: Replace offsetof() with struct_size() in ovl_stack_free()
  ovl: Replace offsetof() with struct_size() in ovl_cache_entry_new()
  ovl: Check for NULL d_inode() in ovl_dentry_upper()
  ovl: Use str_on_off() helper in ovl_show_options()
  ovl: don't require "metacopy=on" for "verity"
  ovl: relax redirect/metacopy requirements for lower -> data redirect
  ovl: make redirect/metacopy rejection consistent
  ovl: Fix nested backing file paths
parents 7a912d04 6f9ccdad
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -443,6 +443,13 @@ Only the data of the files in the "data-only" lower layers may be visible
when a "metacopy" file in one of the lower layers above it, has a "redirect"
to the absolute path of the "lower data" file in the "data-only" lower layer.

Instead of explicitly enabling "metacopy=on" it is sufficient to specify at
least one data-only layer to enable redirection of data to a data-only layer.
In this case other forms of metacopy are rejected.  Note: this way data-only
layers may be used toghether with "userxattr", in which case careful attention
must be given to privileges needed to change the "user.overlay.redirect" xattr
to prevent misuse.

Since kernel version v6.8, "data-only" lower layers can also be added using
the "datadir+" mount options and the fsconfig syscall from new mount api.
For example::
+2 −2
Original line number Diff line number Diff line
@@ -48,8 +48,8 @@ static struct file *ovl_open_realfile(const struct file *file,
		if (!inode_owner_or_capable(real_idmap, realinode))
			flags &= ~O_NOATIME;

		realfile = backing_file_open(&file->f_path, flags, realpath,
					     current_cred());
		realfile = backing_file_open(file_user_path((struct file *) file),
					     flags, realpath, current_cred());
	}
	ovl_revert_creds(old_cred);

+60 −38
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

struct ovl_lookup_data {
	struct super_block *sb;
	struct dentry *dentry;
	const struct ovl_layer *layer;
	struct qstr name;
	bool is_dir;
@@ -24,6 +25,7 @@ struct ovl_lookup_data {
	bool stop;
	bool last;
	char *redirect;
	char *upperredirect;
	int metacopy;
	/* Referring to last redirect xattr */
	bool absolute_redirect;
@@ -1024,6 +1026,31 @@ int ovl_verify_lowerdata(struct dentry *dentry)
	return ovl_maybe_validate_verity(dentry);
}

/*
 * Following redirects/metacopy can have security consequences: it's like a
 * symlink into the lower layer without the permission checks.
 *
 * This is only a problem if the upper layer is untrusted (e.g comes from an USB
 * drive).  This can allow a non-readable file or directory to become readable.
 *
 * Only following redirects when redirects are enabled disables this attack
 * vector when not necessary.
 */
static bool ovl_check_follow_redirect(struct ovl_lookup_data *d)
{
	struct ovl_fs *ofs = OVL_FS(d->sb);

	if (d->metacopy && !ofs->config.metacopy) {
		pr_warn_ratelimited("refusing to follow metacopy origin for (%pd2)\n", d->dentry);
		return false;
	}
	if ((d->redirect || d->upperredirect) && !ovl_redirect_follow(ofs)) {
		pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n", d->dentry);
		return false;
	}
	return true;
}

struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
			  unsigned int flags)
{
@@ -1039,7 +1066,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
	unsigned int ctr = 0;
	struct inode *inode = NULL;
	bool upperopaque = false;
	char *upperredirect = NULL;
	bool check_redirect = (ovl_redirect_follow(ofs) || ofs->numdatalayer);
	struct dentry *this;
	unsigned int i;
	int err;
@@ -1047,12 +1074,14 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
	int metacopy_size = 0;
	struct ovl_lookup_data d = {
		.sb = dentry->d_sb,
		.dentry = dentry,
		.name = dentry->d_name,
		.is_dir = false,
		.opaque = false,
		.stop = false,
		.last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe),
		.last = check_redirect ? false : !ovl_numlower(poe),
		.redirect = NULL,
		.upperredirect = NULL,
		.metacopy = 0,
	};

@@ -1094,8 +1123,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,

		if (d.redirect) {
			err = -ENOMEM;
			upperredirect = kstrdup(d.redirect, GFP_KERNEL);
			if (!upperredirect)
			d.upperredirect = kstrdup(d.redirect, GFP_KERNEL);
			if (!d.upperredirect)
				goto out_put_upper;
			if (d.redirect[0] == '/')
				poe = roe;
@@ -1113,7 +1142,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
	for (i = 0; !d.stop && i < ovl_numlower(poe); i++) {
		struct ovl_path lower = ovl_lowerstack(poe)[i];

		if (!ovl_redirect_follow(ofs))
		if (!ovl_check_follow_redirect(&d)) {
			err = -EPERM;
			goto out_put;
		}

		if (!check_redirect)
			d.last = i == ovl_numlower(poe) - 1;
		else if (d.is_dir || !ofs->numdatalayer)
			d.last = lower.layer->idx == ovl_numlower(roe);
@@ -1126,13 +1160,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
		if (!this)
			continue;

		if ((uppermetacopy || d.metacopy) && !ofs->config.metacopy) {
			dput(this);
			err = -EPERM;
			pr_warn_ratelimited("refusing to follow metacopy origin for (%pd2)\n", dentry);
			goto out_put;
		}

		/*
		 * If no origin fh is stored in upper of a merge dir, store fh
		 * of lower dir and set upper parent "impure".
@@ -1185,23 +1212,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
			ctr++;
		}

		/*
		 * Following redirects can have security consequences: it's like
		 * a symlink into the lower layer without the permission checks.
		 * This is only a problem if the upper layer is untrusted (e.g
		 * comes from an USB drive).  This can allow a non-readable file
		 * or directory to become readable.
		 *
		 * Only following redirects when redirects are enabled disables
		 * this attack vector when not necessary.
		 */
		err = -EPERM;
		if (d.redirect && !ovl_redirect_follow(ofs)) {
			pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n",
					    dentry);
			goto out_put;
		}

		if (d.stop)
			break;

@@ -1212,10 +1222,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
		}
	}

	/* Defer lookup of lowerdata in data-only layers to first access */
	/*
	 * Defer lookup of lowerdata in data-only layers to first access.
	 * Don't require redirect=follow and metacopy=on in this case.
	 */
	if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
		d.metacopy = 0;
		ctr++;
	} else if (!ovl_check_follow_redirect(&d)) {
		err = -EPERM;
		goto out_put;
	}

	/*
@@ -1298,20 +1314,26 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,

		/*
		 * It's safe to assign upperredirect here: the previous
		 * assignment of happens only if upperdentry is non-NULL, and
		 * assignment happens only if upperdentry is non-NULL, and
		 * this one only if upperdentry is NULL.
		 */
		upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
		if (IS_ERR(upperredirect)) {
			err = PTR_ERR(upperredirect);
			upperredirect = NULL;
		d.upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
		if (IS_ERR(d.upperredirect)) {
			err = PTR_ERR(d.upperredirect);
			d.upperredirect = NULL;
			goto out_free_oe;
		}

		err = ovl_check_metacopy_xattr(ofs, &upperpath, NULL);
		if (err < 0)
			goto out_free_oe;
		uppermetacopy = err;
		d.metacopy = uppermetacopy = err;
		metacopy_size = err;

		if (!ovl_check_follow_redirect(&d)) {
			err = -EPERM;
			goto out_free_oe;
		}
	}

	if (upperdentry || ctr) {
@@ -1319,7 +1341,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
			.upperdentry = upperdentry,
			.oe = oe,
			.index = index,
			.redirect = upperredirect,
			.redirect = d.upperredirect,
		};

		/* Store lowerdata redirect for lazy lookup */
@@ -1361,7 +1383,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
		kfree(origin_path);
	}
	dput(upperdentry);
	kfree(upperredirect);
	kfree(d.upperredirect);
out:
	kfree(d.redirect);
	ovl_revert_creds(old_cred);
+1 −1
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ struct ovl_path {

struct ovl_entry {
	unsigned int __numlower;
	struct ovl_path __lowerstack[];
	struct ovl_path __lowerstack[] __counted_by(__numlower);
};

/* private information held for overlayfs's superblock */
+6 −34
Original line number Diff line number Diff line
@@ -871,18 +871,6 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
		config->uuid = OVL_UUID_NULL;
	}

	/* Resolve verity -> metacopy dependency */
	if (config->verity_mode && !config->metacopy) {
		/* Don't allow explicit specified conflicting combinations */
		if (set.metacopy) {
			pr_err("conflicting options: metacopy=off,verity=%s\n",
			       ovl_verity_mode(config));
			return -EINVAL;
		}
		/* Otherwise automatically enable metacopy. */
		config->metacopy = true;
	}

	/*
	 * This is to make the logic below simpler.  It doesn't make any other
	 * difference, since redirect_dir=on is only used for upper.
@@ -890,18 +878,13 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
	if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW)
		config->redirect_mode = OVL_REDIRECT_ON;

	/* Resolve verity -> metacopy -> redirect_dir dependency */
	/* metacopy -> redirect_dir dependency */
	if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) {
		if (set.metacopy && set.redirect) {
			pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
			       ovl_redirect_mode(config));
			return -EINVAL;
		}
		if (config->verity_mode && set.redirect) {
			pr_err("conflicting options: verity=%s,redirect_dir=%s\n",
			       ovl_verity_mode(config), ovl_redirect_mode(config));
			return -EINVAL;
		}
		if (set.redirect) {
			/*
			 * There was an explicit redirect_dir=... that resulted
@@ -970,7 +953,7 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
	}


	/* Resolve userxattr -> !redirect && !metacopy && !verity dependency */
	/* Resolve userxattr -> !redirect && !metacopy dependency */
	if (config->userxattr) {
		if (set.redirect &&
		    config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
@@ -982,11 +965,6 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
			pr_err("conflicting options: userxattr,metacopy=on\n");
			return -EINVAL;
		}
		if (config->verity_mode) {
			pr_err("conflicting options: userxattr,verity=%s\n",
			       ovl_verity_mode(config));
			return -EINVAL;
		}
		/*
		 * Silently disable default setting of redirect and metacopy.
		 * This shall be the default in the future as well: these
@@ -1025,11 +1003,6 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
		 */
	}

	if (ctx->nr_data > 0 && !config->metacopy) {
		pr_err("lower data-only dirs require metacopy support.\n");
		return -EINVAL;
	}

	return 0;
}

@@ -1078,17 +1051,16 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
		seq_printf(m, ",redirect_dir=%s",
			   ovl_redirect_mode(&ofs->config));
	if (ofs->config.index != ovl_index_def)
		seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
		seq_printf(m, ",index=%s", str_on_off(ofs->config.index));
	if (ofs->config.uuid != ovl_uuid_def())
		seq_printf(m, ",uuid=%s", ovl_uuid_mode(&ofs->config));
	if (ofs->config.nfs_export != ovl_nfs_export_def)
		seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
						"on" : "off");
		seq_printf(m, ",nfs_export=%s",
			   str_on_off(ofs->config.nfs_export));
	if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(ofs))
		seq_printf(m, ",xino=%s", ovl_xino_mode(&ofs->config));
	if (ofs->config.metacopy != ovl_metacopy_def)
		seq_printf(m, ",metacopy=%s",
			   ofs->config.metacopy ? "on" : "off");
		seq_printf(m, ",metacopy=%s", str_on_off(ofs->config.metacopy));
	if (ofs->config.ovl_volatile)
		seq_puts(m, ",volatile");
	if (ofs->config.userxattr)
Loading