Commit 63580f66 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull overlayfs updates from Amir Goldstein:

 - add verification feature needed by composefs (Alexander Larsson)

 - improve integration of overlayfs and fanotify (Amir Goldstein)

 - fortify some overlayfs code (Andrea Righi)

* tag 'ovl-update-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: validate superblock in OVL_FS()
  ovl: make consistent use of OVL_FS()
  ovl: Kconfig: introduce CONFIG_OVERLAY_FS_DEBUG
  ovl: auto generate uuid for new overlay filesystems
  ovl: store persistent uuid/fsid with uuid=on
  ovl: add support for unique fsid per instance
  ovl: support encoding non-decodable file handles
  ovl: Handle verity during copy-up
  ovl: Validate verity xattr when resolving lowerdata
  ovl: Add versioned header for overlay.metacopy xattr
  ovl: Add framework for verity support
parents 1687d8ac adcd459f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -326,6 +326,8 @@ the file has fs-verity enabled. This can perform better than
FS_IOC_GETFLAGS and FS_IOC_MEASURE_VERITY because it doesn't require
opening the file, and opening verity files can be expensive.

.. _accessing_verity_files:

Accessing verity files
======================

+72 −0
Original line number Diff line number Diff line
@@ -405,6 +405,53 @@ 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.


fs-verity support
----------------------

During metadata copy up of a lower file, if the source file has
fs-verity enabled and overlay verity support is enabled, then the
digest of the lower file is added to the "trusted.overlay.metacopy"
xattr. This is then used to verify the content of the lower file
each the time the metacopy file is opened.

When a layer containing verity xattrs is used, it means that any such
metacopy file in the upper layer is guaranteed to match the content
that was in the lower at the time of the copy-up. If at any time
(during a mount, after a remount, etc) such a file in the lower is
replaced or modified in any way, access to the corresponding file in
overlayfs will result in EIO errors (either on open, due to overlayfs
digest check, or from a later read due to fs-verity) and a detailed
error is printed to the kernel logs. For more details of how fs-verity
file access works, see :ref:`Documentation/filesystems/fsverity.rst
<accessing_verity_files>`.

Verity can be used as a general robustness check to detect accidental
changes in the overlayfs directories in use. But, with additional care
it can also give more powerful guarantees. For example, if the upper
layer is fully trusted (by using dm-verity or something similar), then
an untrusted lower layer can be used to supply validated file content
for all metacopy files.  If additionally the untrusted lower
directories are specified as "Data-only", then they can only supply
such file content, and the entire mount can be trusted to match the
upper layer.

This feature is controlled by the "verity" mount option, which
supports these values:

- "off":
    The metacopy digest is never generated or used. This is the
    default if verity option is not specified.
- "on":
    Whenever a metacopy files specifies an expected digest, the
    corresponding data file must match the specified digest. When
    generating a metacopy file the verity digest will be set in it
    based on the source file (if it has one).
- "require":
    Same as "on", but additionally all metacopy files must specify a
    digest (or EIO is returned on open). This means metadata copy up
    will only be used if the data file has fs-verity enabled,
    otherwise a full copy-up is used.

Sharing and copying layers
--------------------------

@@ -610,6 +657,31 @@ can be useful in case the underlying disk is copied and the UUID of this copy
is changed. This is only applicable if all lower/upper/work directories are on
the same filesystem, otherwise it will fallback to normal behaviour.


UUID and fsid
-------------

The UUID of overlayfs instance itself and the fsid reported by statfs(2) are
controlled by the "uuid" mount option, which supports these values:

- "null":
    UUID of overlayfs is null. fsid is taken from upper most filesystem.
- "off":
    UUID of overlayfs is null. fsid is taken from upper most filesystem.
    UUID of underlying layers is ignored.
- "on":
    UUID of overlayfs is generated and used to report a unique fsid.
    UUID is stored in xattr "trusted.overlay.uuid", making overlayfs fsid
    unique and persistent.  This option requires an overlayfs with upper
    filesystem that supports xattrs.
- "auto": (default)
    UUID is taken from xattr "trusted.overlay.uuid" if it exists.
    Upgrade to "uuid=on" on first time mount of new overlay filesystem that
    meets the prerequites.
    Downgrade to "uuid=null" for existing overlay filesystems that were never
    mounted with "uuid=on".


Volatile mount
--------------

+9 −0
Original line number Diff line number Diff line
@@ -124,3 +124,12 @@ config OVERLAY_FS_METACOPY
	  that doesn't support this feature will have unexpected results.

	  If unsure, say N.

config OVERLAY_FS_DEBUG
	bool "Overlayfs: turn on extra debugging checks"
	default n
	depends on OVERLAY_FS
	help
	  Say Y here to enable extra debugging checks in overlayfs.

	  If unsure, say N.
+47 −7
Original line number Diff line number Diff line
@@ -416,7 +416,7 @@ struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
	if (is_upper)
		fh->fb.flags |= OVL_FH_FLAG_PATH_UPPER;
	fh->fb.len = sizeof(fh->fb) + buflen;
	if (ofs->config.uuid)
	if (ovl_origin_uuid(ofs))
		fh->fb.uuid = *uuid;

	return fh;
@@ -544,6 +544,7 @@ struct ovl_copy_up_ctx {
	bool origin;
	bool indexed;
	bool metacopy;
	bool metacopy_digest;
};

static int ovl_link_up(struct ovl_copy_up_ctx *c)
@@ -641,8 +642,20 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
	}

	if (c->metacopy) {
		err = ovl_check_setxattr(ofs, temp, OVL_XATTR_METACOPY,
					 NULL, 0, -EOPNOTSUPP);
		struct path lowerdatapath;
		struct ovl_metacopy metacopy_data = OVL_METACOPY_INIT;

		ovl_path_lowerdata(c->dentry, &lowerdatapath);
		if (WARN_ON_ONCE(lowerdatapath.dentry == NULL))
			return -EIO;
		err = ovl_get_verity_digest(ofs, &lowerdatapath, &metacopy_data);
		if (err)
			return err;

		if (metacopy_data.digest_algo)
			c->metacopy_digest = true;

		err = ovl_set_metacopy_xattr(ofs, temp, &metacopy_data);
		if (err)
			return err;
	}
@@ -751,9 +764,15 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
	if (err)
		goto cleanup;

	if (!c->metacopy)
		ovl_set_upperdata(d_inode(c->dentry));
	inode = d_inode(c->dentry);
	if (c->metacopy_digest)
		ovl_set_flag(OVL_HAS_DIGEST, inode);
	else
		ovl_clear_flag(OVL_HAS_DIGEST, inode);
	ovl_clear_flag(OVL_VERIFIED_DIGEST, inode);

	if (!c->metacopy)
		ovl_set_upperdata(inode);
	ovl_inode_update(inode, temp);
	if (S_ISDIR(inode->i_mode))
		ovl_set_flag(OVL_WHITEOUTS, inode);
@@ -813,6 +832,12 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
	if (err)
		goto out_fput;

	if (c->metacopy_digest)
		ovl_set_flag(OVL_HAS_DIGEST, d_inode(c->dentry));
	else
		ovl_clear_flag(OVL_HAS_DIGEST, d_inode(c->dentry));
	ovl_clear_flag(OVL_VERIFIED_DIGEST, d_inode(c->dentry));

	if (!c->metacopy)
		ovl_set_upperdata(d_inode(c->dentry));
	ovl_inode_update(d_inode(c->dentry), dget(temp));
@@ -907,7 +932,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
				  int flags)
{
	struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
	struct ovl_fs *ofs = OVL_FS(dentry->d_sb);

	if (!ofs->config.metacopy)
		return false;
@@ -918,6 +943,19 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
	if (flags && ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)))
		return false;

	/* Fall back to full copy if no fsverity on source data and we require verity */
	if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) {
		struct path lowerdata;

		ovl_path_lowerdata(dentry, &lowerdata);

		if (WARN_ON_ONCE(lowerdata.dentry == NULL) ||
		    ovl_ensure_verity_loaded(&lowerdata) ||
		    !fsverity_active(d_inode(lowerdata.dentry))) {
			return false;
		}
	}

	return true;
}

@@ -984,6 +1022,8 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
	if (err)
		goto out_free;

	ovl_clear_flag(OVL_HAS_DIGEST, d_inode(c->dentry));
	ovl_clear_flag(OVL_VERIFIED_DIGEST, d_inode(c->dentry));
	ovl_set_upperdata(d_inode(c->dentry));
out_free:
	kfree(capability);
@@ -1078,7 +1118,7 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
	 * not very important to optimize this case, so do lazy lowerdata lookup
	 * before any copy up, so we can do it before taking ovl_inode_lock().
	 */
	err = ovl_maybe_lookup_lowerdata(dentry);
	err = ovl_verify_lowerdata(dentry);
	if (err)
		return err;

+25 −11
Original line number Diff line number Diff line
@@ -174,28 +174,37 @@ static int ovl_connect_layer(struct dentry *dentry)
 * U = upper file handle
 * L = lower file handle
 *
 * (*) Connecting an overlay dir from real lower dentry is not always
 * (*) Decoding a connected overlay dir from real lower dentry is not always
 * possible when there are redirects in lower layers and non-indexed merge dirs.
 * To mitigate those case, we may copy up the lower dir ancestor before encode
 * a lower dir file handle.
 * of a decodable file handle for non-upper dir.
 *
 * Return 0 for upper file handle, > 0 for lower file handle or < 0 on error.
 */
static int ovl_check_encode_origin(struct dentry *dentry)
{
	struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
	struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
	bool decodable = ofs->config.nfs_export;

	/* Lower file handle for non-upper non-decodable */
	if (!ovl_dentry_upper(dentry) && !decodable)
		return 0;

	/* Upper file handle for pure upper */
	if (!ovl_dentry_lower(dentry))
		return 0;

	/*
	 * Upper file handle for non-indexed upper.
	 *
	 * Root is never indexed, so if there's an upper layer, encode upper for
	 * root.
	 */
	if (ovl_dentry_upper(dentry) &&
	if (dentry == dentry->d_sb->s_root)
		return 0;

	/*
	 * Upper decodable file handle for non-indexed upper.
	 */
	if (ovl_dentry_upper(dentry) && decodable &&
	    !ovl_test_flag(OVL_INDEX, d_inode(dentry)))
		return 0;

@@ -205,7 +214,7 @@ static int ovl_check_encode_origin(struct dentry *dentry)
	 * ovl_connect_layer() will try to make origin's layer "connected" by
	 * copying up a "connectable" ancestor.
	 */
	if (d_is_dir(dentry) && ovl_upper_mnt(ofs))
	if (d_is_dir(dentry) && ovl_upper_mnt(ofs) && decodable)
		return ovl_connect_layer(dentry);

	/* Lower file handle for indexed and non-upper dir/non-dir */
@@ -435,7 +444,7 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb,
					    struct dentry *real,
					    const struct ovl_layer *layer)
{
	struct ovl_fs *ofs = sb->s_fs_info;
	struct ovl_fs *ofs = OVL_FS(sb);
	struct dentry *index = NULL;
	struct dentry *this = NULL;
	struct inode *inode;
@@ -656,7 +665,7 @@ static struct dentry *ovl_get_dentry(struct super_block *sb,
				     struct ovl_path *lowerpath,
				     struct dentry *index)
{
	struct ovl_fs *ofs = sb->s_fs_info;
	struct ovl_fs *ofs = OVL_FS(sb);
	const struct ovl_layer *layer = upper ? &ofs->layers[0] : lowerpath->layer;
	struct dentry *real = upper ?: (index ?: lowerpath->dentry);

@@ -681,7 +690,7 @@ static struct dentry *ovl_get_dentry(struct super_block *sb,
static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
					struct ovl_fh *fh)
{
	struct ovl_fs *ofs = sb->s_fs_info;
	struct ovl_fs *ofs = OVL_FS(sb);
	struct dentry *dentry;
	struct dentry *upper;

@@ -701,7 +710,7 @@ static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
					struct ovl_fh *fh)
{
	struct ovl_fs *ofs = sb->s_fs_info;
	struct ovl_fs *ofs = OVL_FS(sb);
	struct ovl_path origin = { };
	struct ovl_path *stack = &origin;
	struct dentry *dentry = NULL;
@@ -876,3 +885,8 @@ const struct export_operations ovl_export_operations = {
	.get_name	= ovl_get_name,
	.get_parent	= ovl_get_parent,
};

/* encode_fh() encodes non-decodable file handles with nfs_export=off */
const struct export_operations ovl_export_fid_operations = {
	.encode_fh	= ovl_encode_fh,
};
Loading