xfs: validate directory leaf buffer owners
authorDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:54:39 +0000 (14:54 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:58:51 +0000 (14:58 -0700)
Check the owner field of directory leaf blocks.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_da_btree.c
fs/xfs/libxfs/xfs_dir2.h
fs/xfs/libxfs/xfs_dir2_leaf.c
fs/xfs/libxfs/xfs_dir2_node.c
fs/xfs/libxfs/xfs_dir2_priv.h
fs/xfs/scrub/dir.c

index e6c28bccdbc07db436b7765106ae929cc78f04b6..b13796629e22134abbf5d76ccb79973dd77ddcb4 100644 (file)
@@ -288,8 +288,12 @@ xfs_da3_header_check(
                return xfs_attr3_leaf_header_check(bp, owner);
        case cpu_to_be16(XFS_DA3_NODE_MAGIC):
                return xfs_da3_node_header_check(bp, owner);
+       case cpu_to_be16(XFS_DIR3_LEAF1_MAGIC):
+       case cpu_to_be16(XFS_DIR3_LEAFN_MAGIC):
+               return xfs_dir3_leaf_header_check(bp, owner);
        }
 
+       ASSERT(0);
        return NULL;
 }
 
@@ -1700,6 +1704,12 @@ xfs_da3_node_lookup_int(
 
                if (magic == XFS_DIR2_LEAFN_MAGIC ||
                    magic == XFS_DIR3_LEAFN_MAGIC) {
+                       fa = xfs_dir3_leaf_header_check(blk->bp, args->owner);
+                       if (fa) {
+                               __xfs_buf_mark_corrupt(blk->bp, fa);
+                               xfs_da_mark_sick(args);
+                               return -EFSCORRUPTED;
+                       }
                        blk->magic = XFS_DIR2_LEAFN_MAGIC;
                        blk->hashval = xfs_dir2_leaf_lasthash(args->dp,
                                                              blk->bp, NULL);
@@ -2208,6 +2218,12 @@ xfs_da3_path_shift(
                        break;
                case XFS_DIR2_LEAFN_MAGIC:
                case XFS_DIR3_LEAFN_MAGIC:
+                       fa = xfs_dir3_leaf_header_check(blk->bp, args->owner);
+                       if (fa) {
+                               __xfs_buf_mark_corrupt(blk->bp, fa);
+                               xfs_da_mark_sick(args);
+                               return -EFSCORRUPTED;
+                       }
                        blk->magic = XFS_DIR2_LEAFN_MAGIC;
                        ASSERT(level == path->active-1);
                        blk->index = 0;
index 8497d041f3163b1de31e0262fd0d2941a8be611c..2f728c26a4162493ee90004afe66f47a23919f9b 100644 (file)
@@ -101,6 +101,8 @@ extern struct xfs_dir2_data_free *xfs_dir2_data_freefind(
 
 extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
 
+xfs_failaddr_t xfs_dir3_leaf_header_check(struct xfs_buf *bp, xfs_ino_t owner);
+
 extern const struct xfs_buf_ops xfs_dir3_block_buf_ops;
 extern const struct xfs_buf_ops xfs_dir3_leafn_buf_ops;
 extern const struct xfs_buf_ops xfs_dir3_leaf1_buf_ops;
index 20ce057d12e828c286597fbacec67acf727f3bbe..53b808e2a5f07b26a734e0dcbe514d857e5e5110 100644 (file)
@@ -208,6 +208,29 @@ xfs_dir3_leaf_verify(
        return xfs_dir3_leaf_check_int(mp, &leafhdr, bp->b_addr, true);
 }
 
+xfs_failaddr_t
+xfs_dir3_leaf_header_check(
+       struct xfs_buf          *bp,
+       xfs_ino_t               owner)
+{
+       struct xfs_mount        *mp = bp->b_mount;
+
+       if (xfs_has_crc(mp)) {
+               struct xfs_dir3_leaf *hdr3 = bp->b_addr;
+
+               if (hdr3->hdr.info.hdr.magic !=
+                                       cpu_to_be16(XFS_DIR3_LEAF1_MAGIC) &&
+                   hdr3->hdr.info.hdr.magic !=
+                                       cpu_to_be16(XFS_DIR3_LEAFN_MAGIC))
+                       return __this_address;
+
+               if (be64_to_cpu(hdr3->hdr.info.owner) != owner)
+                       return __this_address;
+       }
+
+       return NULL;
+}
+
 static void
 xfs_dir3_leaf_read_verify(
        struct xfs_buf  *bp)
@@ -271,32 +294,60 @@ int
 xfs_dir3_leaf_read(
        struct xfs_trans        *tp,
        struct xfs_inode        *dp,
+       xfs_ino_t               owner,
        xfs_dablk_t             fbno,
        struct xfs_buf          **bpp)
 {
+       xfs_failaddr_t          fa;
        int                     err;
 
        err = xfs_da_read_buf(tp, dp, fbno, 0, bpp, XFS_DATA_FORK,
                        &xfs_dir3_leaf1_buf_ops);
-       if (!err && tp && *bpp)
+       if (err || !(*bpp))
+               return err;
+
+       fa = xfs_dir3_leaf_header_check(*bpp, owner);
+       if (fa) {
+               __xfs_buf_mark_corrupt(*bpp, fa);
+               xfs_trans_brelse(tp, *bpp);
+               *bpp = NULL;
+               xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
+               return -EFSCORRUPTED;
+       }
+
+       if (tp)
                xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAF1_BUF);
-       return err;
+       return 0;
 }
 
 int
 xfs_dir3_leafn_read(
        struct xfs_trans        *tp,
        struct xfs_inode        *dp,
+       xfs_ino_t               owner,
        xfs_dablk_t             fbno,
        struct xfs_buf          **bpp)
 {
+       xfs_failaddr_t          fa;
        int                     err;
 
        err = xfs_da_read_buf(tp, dp, fbno, 0, bpp, XFS_DATA_FORK,
                        &xfs_dir3_leafn_buf_ops);
-       if (!err && tp && *bpp)
+       if (err || !(*bpp))
+               return err;
+
+       fa = xfs_dir3_leaf_header_check(*bpp, owner);
+       if (fa) {
+               __xfs_buf_mark_corrupt(*bpp, fa);
+               xfs_trans_brelse(tp, *bpp);
+               *bpp = NULL;
+               xfs_dirattr_mark_sick(dp, XFS_DATA_FORK);
+               return -EFSCORRUPTED;
+       }
+
+       if (tp)
                xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAFN_BUF);
-       return err;
+       return 0;
 }
 
 /*
@@ -646,7 +697,8 @@ xfs_dir2_leaf_addname(
 
        trace_xfs_dir2_leaf_addname(args);
 
-       error = xfs_dir3_leaf_read(tp, dp, args->geo->leafblk, &lbp);
+       error = xfs_dir3_leaf_read(tp, dp, args->owner, args->geo->leafblk,
+                       &lbp);
        if (error)
                return error;
 
@@ -1237,7 +1289,8 @@ xfs_dir2_leaf_lookup_int(
        tp = args->trans;
        mp = dp->i_mount;
 
-       error = xfs_dir3_leaf_read(tp, dp, args->geo->leafblk, &lbp);
+       error = xfs_dir3_leaf_read(tp, dp, args->owner, args->geo->leafblk,
+                       &lbp);
        if (error)
                return error;
 
index 1ad7405f9c389e2e298544be35aa190fa30dd8a3..e21965788188b41b5404a5637715da47fe4102aa 100644 (file)
@@ -1562,7 +1562,8 @@ xfs_dir2_leafn_toosmall(
                /*
                 * Read the sibling leaf block.
                 */
-               error = xfs_dir3_leafn_read(state->args->trans, dp, blkno, &bp);
+               error = xfs_dir3_leafn_read(state->args->trans, dp,
+                               state->args->owner, blkno, &bp);
                if (error)
                        return error;
 
index 1db2e60ba827fe649cb1fd56172ed88518827928..2f0e3ad47b3713e825db6a6dc61151652f25f2d2 100644 (file)
@@ -95,9 +95,9 @@ void xfs_dir2_leaf_hdr_from_disk(struct xfs_mount *mp,
 void xfs_dir2_leaf_hdr_to_disk(struct xfs_mount *mp, struct xfs_dir2_leaf *to,
                struct xfs_dir3_icleaf_hdr *from);
 int xfs_dir3_leaf_read(struct xfs_trans *tp, struct xfs_inode *dp,
-               xfs_dablk_t fbno, struct xfs_buf **bpp);
+               xfs_ino_t owner, xfs_dablk_t fbno, struct xfs_buf **bpp);
 int xfs_dir3_leafn_read(struct xfs_trans *tp, struct xfs_inode *dp,
-               xfs_dablk_t fbno, struct xfs_buf **bpp);
+               xfs_ino_t owner, xfs_dablk_t fbno, struct xfs_buf **bpp);
 extern int xfs_dir2_block_to_leaf(struct xfs_da_args *args,
                struct xfs_buf *dbp);
 extern int xfs_dir2_leaf_addname(struct xfs_da_args *args);
index 042e28547e0447ceb4633ceaae455f7fefe33bcf..d94e265a8e1f2f97314457ef0c1023369a723398 100644 (file)
@@ -470,7 +470,7 @@ xchk_directory_leaf1_bestfree(
        int                             error;
 
        /* Read the free space block. */
-       error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, &bp);
+       error = xfs_dir3_leaf_read(sc->tp, sc->ip, sc->ip->i_ino, lblk, &bp);
        if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
                return error;
        xchk_buffer_recheck(sc, bp);