@@ -18,6 +18,7 @@
#include "xfs_attr_leaf.h"
#include "xfs_attr_sf.h"
#include "xfs_parent.h"
+#include "xfs_verity.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/dabtree.h"
@@ -126,6 +127,47 @@ xchk_setup_xattr_buf(
return 0;
}
+#ifdef CONFIG_FS_VERITY
+/*
+ * Obtain merkle tree geometry information for a verity file so that we can
+ * perform sanity checks of the fsverity xattrs.
+ */
+STATIC int
+xchk_xattr_setup_verity(
+ struct xfs_scrub *sc)
+{
+ struct xchk_xattr_buf *ab;
+ int error;
+
+ /*
+ * Drop the ILOCK and the transaction because loading the fsverity
+ * metadata will call into the xattr code. S_VERITY is enabled with
+ * IOLOCK_EXCL held, so it should not change here.
+ */
+ xchk_iunlock(sc, XFS_ILOCK_EXCL);
+ xchk_trans_cancel(sc);
+
+ error = xchk_setup_xattr_buf(sc, 0);
+ if (error)
+ return error;
+
+ ab = sc->buf;
+ error = xchk_inode_setup_verity(sc, &ab->merkle_blocksize,
+ &ab->merkle_tree_size);
+ if (error)
+ return error;
+
+ error = xchk_trans_alloc(sc, 0);
+ if (error)
+ return error;
+
+ xchk_ilock(sc, XFS_ILOCK_EXCL);
+ return 0;
+}
+#else
+# define xchk_xattr_setup_verity(...) (0)
+#endif /* CONFIG_FS_VERITY */
+
/* Set us up to scrub an inode's extended attributes. */
int
xchk_setup_xattr(
@@ -150,9 +192,89 @@ xchk_setup_xattr(
return error;
}
- return xchk_setup_inode_contents(sc, 0);
+ error = xchk_setup_inode_contents(sc, 0);
+ if (error)
+ return error;
+
+ if (IS_VERITY(VFS_I(sc->ip))) {
+ error = xchk_xattr_setup_verity(sc);
+ if (error)
+ return error;
+ }
+
+ return error;
}
+#ifdef CONFIG_FS_VERITY
+/* Check the merkle tree xattrs. */
+STATIC void
+xchk_xattr_verity(
+ struct xfs_scrub *sc,
+ xfs_dablk_t blkno,
+ const unsigned char *name,
+ unsigned int namelen,
+ unsigned int valuelen)
+{
+ struct xchk_xattr_buf *ab = sc->buf;
+
+ /* Non-verity filesystems should never have verity xattrs. */
+ if (!xfs_has_verity(sc->mp)) {
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, blkno);
+ return;
+ }
+
+ /*
+ * Any verity metadata on a non-verity file are leftovers from a
+ * previous attempt to enable verity.
+ */
+ if (!IS_VERITY(VFS_I(sc->ip))) {
+ xchk_ino_set_preen(sc, sc->ip->i_ino);
+ return;
+ }
+
+ /* Zero blocksize occurs if we couldn't load the merkle tree data. */
+ if (ab->merkle_blocksize == 0)
+ return;
+
+ switch (namelen) {
+ case sizeof(struct xfs_merkle_key):
+ /* Oversized blocks are not allowed */
+ if (valuelen > ab->merkle_blocksize) {
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, blkno);
+ return;
+ }
+ break;
+ case XFS_VERITY_DESCRIPTOR_NAME_LEN:
+ /* Has to match the descriptor xattr name */
+ if (memcmp(name, XFS_VERITY_DESCRIPTOR_NAME, namelen))
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, blkno);
+ return;
+ default:
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, blkno);
+ return;
+ }
+
+ /*
+ * Merkle tree blocks beyond the end of the tree are leftovers from
+ * a previous failed attempt to enable verity.
+ */
+ if (xfs_merkle_key_from_disk(name, namelen) >= ab->merkle_tree_size)
+ xchk_ino_set_preen(sc, sc->ip->i_ino);
+}
+#else
+static void
+xchk_xattr_verity(
+ struct xfs_scrub *sc,
+ xfs_dablk_t blkno,
+ const unsigned char *name,
+ unsigned int namelen,
+ unsigned int valuelen)
+{
+ /* Should never see verity xattrs when verity is not enabled. */
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, blkno);
+}
+#endif /* CONFIG_FS_VERITY */
+
/* Extended Attributes */
/*
@@ -216,6 +338,13 @@ xchk_xattr_actor(
return -ECANCELED;
}
+ /* Check verity xattr geometry */
+ if (attr_flags & XFS_ATTR_VERITY) {
+ xchk_xattr_verity(sc, args.blkno, name, namelen, valuelen);
+ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ return -ECANCELED;
+ }
+
/*
* Try to allocate enough memory to extract the attr value. If that
* doesn't work, return -EDEADLOCK as a signal to try again with a
@@ -653,6 +782,13 @@ xchk_xattr(
if (xchk_inode_verity_broken(sc->ip))
xchk_set_incomplete(sc);
+ /*
+ * If this is a verity file that won't activate, we cannot check the
+ * merkle tree geometry.
+ */
+ if (xchk_inode_verity_broken(sc->ip))
+ xchk_set_incomplete(sc);
+
/* Allocate memory for xattr checking. */
error = xchk_setup_xattr_buf(sc, 0);
if (error == -ENOMEM)
@@ -22,6 +22,12 @@ struct xchk_xattr_buf {
/* Memory buffer used to extract xattr values. */
void *value;
size_t value_sz;
+
+#ifdef CONFIG_FS_VERITY
+ /* Geometry of the merkle tree attached to this verity file. */
+ u64 merkle_tree_size;
+ unsigned int merkle_blocksize;
+#endif
};
bool xchk_xattr_set_map(struct xfs_scrub *sc, unsigned long *map,
@@ -29,6 +29,7 @@
#include "xfs_exchrange.h"
#include "xfs_acl.h"
#include "xfs_parent.h"
+#include "xfs_verity.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
@@ -155,6 +156,44 @@ xrep_setup_xattr(
return xrep_tempfile_create(sc, S_IFREG);
}
+#ifdef CONFIG_FS_VERITY
+static int
+xrep_xattr_want_salvage_verity(
+ struct xrep_xattr *rx,
+ const void *name,
+ int namelen,
+ int valuelen)
+{
+ struct xchk_xattr_buf *ab = rx->sc->buf;
+
+ if (!xfs_has_verity(rx->sc->mp))
+ return false;
+ if (!IS_VERITY(VFS_I(rx->sc->ip)))
+ return false;
+
+ switch (namelen) {
+ case sizeof(struct xfs_merkle_key):
+ /* Oversized blocks are not allowed */
+ if (valuelen > ab->merkle_blocksize)
+ return false;
+ break;
+ case XFS_VERITY_DESCRIPTOR_NAME_LEN:
+ /* Has to match the descriptor xattr name */
+ return !memcmp(name, XFS_VERITY_DESCRIPTOR_NAME, namelen);
+ default:
+ return false;
+ }
+
+ /*
+ * Merkle tree blocks beyond the end of the tree are leftovers from
+ * a previous failed attempt to enable verity.
+ */
+ return xfs_merkle_key_from_disk(name, namelen) < ab->merkle_tree_size;
+}
+#else
+# define xrep_xattr_want_salvage_verity(...) (false)
+#endif /* CONFIG_FS_VERITY */
+
/*
* Decide if we want to salvage this attribute. We don't bother with
* incomplete or oversized keys or values. The @value parameter can be null
@@ -179,6 +218,9 @@ xrep_xattr_want_salvage(
return false;
if (attr_flags & XFS_ATTR_PARENT)
return xfs_parent_valuecheck(rx->sc->mp, value, valuelen);
+ if (attr_flags & XFS_ATTR_VERITY)
+ return xrep_xattr_want_salvage_verity(rx, name, namelen,
+ valuelen);
return true;
}
@@ -212,6 +254,11 @@ xrep_xattr_salvage_key(
trace_xrep_xattr_salvage_pptr(rx->sc->ip, flags, name,
key.namelen, value, valuelen);
+ } else if (flags & XFS_ATTR_VERITY) {
+ key.namelen = namelen;
+
+ trace_xrep_xattr_salvage_verity(rx->sc->ip, flags, name,
+ key.namelen, value, valuelen);
} else {
while (i < namelen && name[i] != 0)
i++;
@@ -663,6 +710,10 @@ xrep_xattr_insert_rec(
ab->name, key->namelen, ab->value,
key->valuelen);
args.op_flags |= XFS_DA_OP_LOGGED;
+ } else if (key->flags & XFS_ATTR_VERITY) {
+ trace_xrep_xattr_insert_verity(rx->sc->ip, key->flags,
+ ab->name, key->namelen, ab->value,
+ key->valuelen);
} else {
trace_xrep_xattr_insert_rec(rx->sc->tempip, key->flags,
ab->name, key->namelen, key->valuelen);
@@ -22,6 +22,7 @@
#include "xfs_parent.h"
#include "xfs_imeta.h"
#include "xfs_rtgroup.h"
+#include "xfs_verity.h"
#include "scrub/scrub.h"
#include "scrub/xfile.h"
#include "scrub/xfarray.h"
@@ -3072,6 +3072,37 @@ DEFINE_EVENT(xrep_pptr_salvage_class, name, \
DEFINE_XREP_PPTR_SALVAGE_EVENT(xrep_xattr_salvage_pptr);
DEFINE_XREP_PPTR_SALVAGE_EVENT(xrep_xattr_insert_pptr);
+DECLARE_EVENT_CLASS(xrep_verity_salvage_class,
+ TP_PROTO(struct xfs_inode *ip, unsigned int flags, const void *name,
+ unsigned int namelen, const void *value, unsigned int valuelen),
+ TP_ARGS(ip, flags, name, namelen, value, valuelen),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(unsigned long long, merkle_off)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = ip->i_ino;
+ if (namelen == sizeof(struct xfs_merkle_key))
+ __entry->merkle_off = xfs_merkle_key_from_disk(name,
+ namelen);
+ else
+ __entry->merkle_off = -1ULL;
+ ),
+ TP_printk("dev %d:%d ino 0x%llx merkle_off 0x%llx",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->merkle_off)
+)
+#define DEFINE_XREP_VERITY_SALVAGE_EVENT(name) \
+DEFINE_EVENT(xrep_verity_salvage_class, name, \
+ TP_PROTO(struct xfs_inode *ip, unsigned int flags, const void *name, \
+ unsigned int namelen, const void *value, unsigned int valuelen), \
+ TP_ARGS(ip, flags, name, namelen, value, valuelen))
+DEFINE_XREP_VERITY_SALVAGE_EVENT(xrep_xattr_salvage_verity);
+DEFINE_XREP_VERITY_SALVAGE_EVENT(xrep_xattr_insert_verity);
+
TRACE_EVENT(xrep_xattr_class,
TP_PROTO(struct xfs_inode *ip, struct xfs_inode *arg_ip),
TP_ARGS(ip, arg_ip),