Commit ae0506eb authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: check used space of shortform xattr structures



Make sure that the records used inside a shortform xattr structure do
not overlap.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent 5b02a3e8
Loading
Loading
Loading
Loading
+75 −4
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include "xfs_da_btree.h"
#include "xfs_attr.h"
#include "xfs_attr_leaf.h"
#include "xfs_attr_sf.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/dabtree.h"
@@ -487,6 +488,73 @@ xchk_xattr_rec(
	return error;
}

/* Check space usage of shortform attrs. */
STATIC int
xchk_xattr_check_sf(
	struct xfs_scrub		*sc)
{
	struct xchk_xattr_buf		*ab = sc->buf;
	struct xfs_attr_shortform	*sf;
	struct xfs_attr_sf_entry	*sfe;
	struct xfs_attr_sf_entry	*next;
	struct xfs_ifork		*ifp;
	unsigned char			*end;
	int				i;
	int				error = 0;

	ifp = xfs_ifork_ptr(sc->ip, XFS_ATTR_FORK);

	bitmap_zero(ab->usedmap, ifp->if_bytes);
	sf = (struct xfs_attr_shortform *)sc->ip->i_af.if_u1.if_data;
	end = (unsigned char *)ifp->if_u1.if_data + ifp->if_bytes;
	xchk_xattr_set_map(sc, ab->usedmap, 0, sizeof(sf->hdr));

	sfe = &sf->list[0];
	if ((unsigned char *)sfe > end) {
		xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
		return 0;
	}

	for (i = 0; i < sf->hdr.count; i++) {
		unsigned char		*name = sfe->nameval;
		unsigned char		*value = &sfe->nameval[sfe->namelen];

		if (xchk_should_terminate(sc, &error))
			return error;

		next = xfs_attr_sf_nextentry(sfe);
		if ((unsigned char *)next > end) {
			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
			break;
		}

		if (!xchk_xattr_set_map(sc, ab->usedmap,
				(char *)sfe - (char *)sf,
				sizeof(struct xfs_attr_sf_entry))) {
			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
			break;
		}

		if (!xchk_xattr_set_map(sc, ab->usedmap,
				(char *)name - (char *)sf,
				sfe->namelen)) {
			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
			break;
		}

		if (!xchk_xattr_set_map(sc, ab->usedmap,
				(char *)value - (char *)sf,
				sfe->valuelen)) {
			xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
			break;
		}

		sfe = next;
	}

	return 0;
}

/* Scrub the extended attribute metadata. */
int
xchk_xattr(
@@ -506,8 +574,10 @@ xchk_xattr(
	if (error)
		return error;

	memset(&sx, 0, sizeof(sx));
	/* Check attribute tree structure */
	/* Check the physical structure of the xattr. */
	if (sc->ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)
		error = xchk_xattr_check_sf(sc);
	else
		error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec,
				&last_checked);
	if (error)
@@ -517,6 +587,7 @@ xchk_xattr(
		goto out;

	/* Check that every attr key can also be looked up by hash. */
	memset(&sx, 0, sizeof(sx));
	sx.context.dp = sc->ip;
	sx.context.resynch = 1;
	sx.context.put_listent = xchk_xattr_listent;
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@
 * Temporary storage for online scrub and repair of extended attributes.
 */
struct xchk_xattr_buf {
	/* Bitmap of used space in xattr leaf blocks. */
	/* Bitmap of used space in xattr leaf blocks and shortform forks. */
	unsigned long		*usedmap;

	/* Bitmap of free space in xattr leaf blocks. */