]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: directory scrubber must walk through data block to offset
authorDarrick J. Wong <darrick.wong@oracle.com>
Tue, 27 Feb 2018 04:43:18 +0000 (22:43 -0600)
committerEric Sandeen <sandeen@redhat.com>
Tue, 27 Feb 2018 04:43:18 +0000 (22:43 -0600)
Source kernel commit: ce92d29ddf9908d397895c46b7c78e9db8df414d

In xfs_scrub_dir_rec, we must walk through the directory block entries
to arrive at the offset given by the hash structure.  If we blindly
trust the hash address, we can end up midway into a directory entry and
stray outside the block.  Found by lastbit fuzzing lents[3].address in
xfs/390 with KASAN enabled.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
libxfs/xfs_dir2.h
libxfs/xfs_dir2_data.c
libxfs/xfs_dir2_sf.c

index 1a8f2cf977caa28812ab1372055dd4dc62409e5a..388d67c5c9036926a7bcf9e8bd9badfbdf5efa89 100644 (file)
@@ -340,5 +340,7 @@ xfs_dir2_leaf_tail_p(struct xfs_da_geometry *geo, struct xfs_dir2_leaf *lp)
 #define XFS_READDIR_BUFSIZE    (32768)
 
 unsigned char xfs_dir3_get_dtype(struct xfs_mount *mp, uint8_t filetype);
+void *xfs_dir3_data_endp(struct xfs_da_geometry *geo,
+               struct xfs_dir2_data_hdr *hdr);
 
 #endif /* __XFS_DIR2_H__ */
index 1f46115f42a16c6a6f0bee417c75d83dd9007d4e..175c119e4bd9bd99e9be211ace47450f7edcdc8b 100644 (file)
@@ -86,7 +86,6 @@ __xfs_dir3_data_check(
        case cpu_to_be32(XFS_DIR2_BLOCK_MAGIC):
                btp = xfs_dir2_block_tail_p(geo, hdr);
                lep = xfs_dir2_block_leaf_p(btp);
-               endp = (char *)lep;
 
                /*
                 * The number of leaf entries is limited by the size of the
@@ -101,11 +100,13 @@ __xfs_dir3_data_check(
                break;
        case cpu_to_be32(XFS_DIR3_DATA_MAGIC):
        case cpu_to_be32(XFS_DIR2_DATA_MAGIC):
-               endp = (char *)hdr + geo->blksize;
                break;
        default:
                return __this_address;
        }
+       endp = xfs_dir3_data_endp(geo, hdr);
+       if (!endp)
+               return __this_address;
 
        /*
         * Account for zero bestfree entries.
@@ -543,7 +544,6 @@ xfs_dir2_data_freescan_int(
        struct xfs_dir2_data_hdr *hdr,
        int                     *loghead)
 {
-       xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* active data entry */
        xfs_dir2_data_unused_t  *dup;           /* unused data entry */
        struct xfs_dir2_data_free *bf;
@@ -565,12 +565,7 @@ xfs_dir2_data_freescan_int(
         * Set up pointers.
         */
        p = (char *)ops->data_entry_p(hdr);
-       if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
-           hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC)) {
-               btp = xfs_dir2_block_tail_p(geo, hdr);
-               endp = (char *)xfs_dir2_block_leaf_p(btp);
-       } else
-               endp = (char *)hdr + geo->blksize;
+       endp = xfs_dir3_data_endp(geo, hdr);
        /*
         * Loop over the block's entries.
         */
@@ -783,17 +778,9 @@ xfs_dir2_data_make_free(
        /*
         * Figure out where the end of the data area is.
         */
-       if (hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
-           hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC))
-               endptr = (char *)hdr + args->geo->blksize;
-       else {
-               xfs_dir2_block_tail_t   *btp;   /* block tail */
+       endptr = xfs_dir3_data_endp(args->geo, hdr);
+       ASSERT(endptr != NULL);
 
-               ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
-                       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
-               btp = xfs_dir2_block_tail_p(args->geo, hdr);
-               endptr = (char *)xfs_dir2_block_leaf_p(btp);
-       }
        /*
         * If this isn't the start of the block, then back up to
         * the previous entry and see if it's free.
@@ -1095,3 +1082,21 @@ xfs_dir2_data_use_free(
        }
        *needscanp = needscan;
 }
+
+/* Find the end of the entry data in a data/block format dir block. */
+void *
+xfs_dir3_data_endp(
+       struct xfs_da_geometry          *geo,
+       struct xfs_dir2_data_hdr        *hdr)
+{
+       switch (hdr->magic) {
+       case cpu_to_be32(XFS_DIR3_BLOCK_MAGIC):
+       case cpu_to_be32(XFS_DIR2_BLOCK_MAGIC):
+               return xfs_dir2_block_leaf_p(xfs_dir2_block_tail_p(geo, hdr));
+       case cpu_to_be32(XFS_DIR3_DATA_MAGIC):
+       case cpu_to_be32(XFS_DIR2_DATA_MAGIC):
+               return (char *)hdr + geo->blksize;
+       default:
+               return NULL;
+       }
+}
index cf352b6c6ea8c1010c92496e648aca1b59b23cfb..fbb9482172726414988dab5d6e6f665d9abb1f29 100644 (file)
@@ -154,7 +154,6 @@ xfs_dir2_block_to_sf(
        xfs_dir2_sf_hdr_t       *sfhp)          /* shortform directory hdr */
 {
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
-       xfs_dir2_block_tail_t   *btp;           /* block tail pointer */
        xfs_dir2_data_entry_t   *dep;           /* data entry pointer */
        xfs_inode_t             *dp;            /* incore directory inode */
        xfs_dir2_data_unused_t  *dup;           /* unused data pointer */
@@ -190,9 +189,8 @@ xfs_dir2_block_to_sf(
        /*
         * Set up to loop over the block's entries.
         */
-       btp = xfs_dir2_block_tail_p(args->geo, hdr);
        ptr = (char *)dp->d_ops->data_entry_p(hdr);
-       endptr = (char *)xfs_dir2_block_leaf_p(btp);
+       endptr = xfs_dir3_data_endp(args->geo, hdr);
        sfep = xfs_dir2_sf_firstentry(sfp);
        /*
         * Loop over the active and unused entries.