xfs: validate dabtree node buffer owners
authorDarrick J. Wong <djwong@kernel.org>
Mon, 15 Apr 2024 21:54:38 +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 dabtree node 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_da_btree.h
fs/xfs/xfs_attr_list.c

index 65eef87751871cde699ba8b1463cdc36443a356c..e6c28bccdbc07db436b7765106ae929cc78f04b6 100644 (file)
@@ -252,6 +252,26 @@ xfs_da3_node_verify(
        return NULL;
 }
 
+xfs_failaddr_t
+xfs_da3_node_header_check(
+       struct xfs_buf          *bp,
+       xfs_ino_t               owner)
+{
+       struct xfs_mount        *mp = bp->b_mount;
+
+       if (xfs_has_crc(mp)) {
+               struct xfs_da3_blkinfo *hdr3 = bp->b_addr;
+
+               if (hdr3->hdr.magic != cpu_to_be16(XFS_DA3_NODE_MAGIC))
+                       return __this_address;
+
+               if (be64_to_cpu(hdr3->owner) != owner)
+                       return __this_address;
+       }
+
+       return NULL;
+}
+
 xfs_failaddr_t
 xfs_da3_header_check(
        struct xfs_buf          *bp,
@@ -266,6 +286,8 @@ xfs_da3_header_check(
        switch (hdr->magic) {
        case cpu_to_be16(XFS_ATTR3_LEAF_MAGIC):
                return xfs_attr3_leaf_header_check(bp, owner);
+       case cpu_to_be16(XFS_DA3_NODE_MAGIC):
+               return xfs_da3_node_header_check(bp, owner);
        }
 
        return NULL;
@@ -1218,6 +1240,7 @@ xfs_da3_root_join(
        struct xfs_da3_icnode_hdr oldroothdr;
        int                     error;
        struct xfs_inode        *dp = state->args->dp;
+       xfs_failaddr_t          fa;
 
        trace_xfs_da_root_join(state->args);
 
@@ -1244,6 +1267,13 @@ xfs_da3_root_join(
        error = xfs_da3_node_read(args->trans, dp, child, &bp, args->whichfork);
        if (error)
                return error;
+       fa = xfs_da3_header_check(bp, args->owner);
+       if (fa) {
+               __xfs_buf_mark_corrupt(bp, fa);
+               xfs_trans_brelse(args->trans, bp);
+               xfs_da_mark_sick(args);
+               return -EFSCORRUPTED;
+       }
        xfs_da_blkinfo_onlychild_validate(bp->b_addr, oldroothdr.level);
 
        /*
@@ -1278,6 +1308,7 @@ xfs_da3_node_toosmall(
        struct xfs_da_blkinfo   *info;
        xfs_dablk_t             blkno;
        struct xfs_buf          *bp;
+       xfs_failaddr_t          fa;
        struct xfs_da3_icnode_hdr nodehdr;
        int                     count;
        int                     forward;
@@ -1352,6 +1383,13 @@ xfs_da3_node_toosmall(
                                state->args->whichfork);
                if (error)
                        return error;
+               fa = xfs_da3_node_header_check(bp, state->args->owner);
+               if (fa) {
+                       __xfs_buf_mark_corrupt(bp, fa);
+                       xfs_trans_brelse(state->args->trans, bp);
+                       xfs_da_mark_sick(state->args);
+                       return -EFSCORRUPTED;
+               }
 
                node = bp->b_addr;
                xfs_da3_node_hdr_from_disk(dp->i_mount, &thdr, node);
@@ -1674,6 +1712,13 @@ xfs_da3_node_lookup_int(
                        return -EFSCORRUPTED;
                }
 
+               fa = xfs_da3_node_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_DA_NODE_MAGIC;
 
                /*
@@ -1846,6 +1891,7 @@ xfs_da3_blk_link(
        struct xfs_da_blkinfo   *tmp_info;
        struct xfs_da_args      *args;
        struct xfs_buf          *bp;
+       xfs_failaddr_t          fa;
        int                     before = 0;
        int                     error;
        struct xfs_inode        *dp = state->args->dp;
@@ -1889,6 +1935,13 @@ xfs_da3_blk_link(
                                                &bp, args->whichfork);
                        if (error)
                                return error;
+                       fa = xfs_da3_header_check(bp, args->owner);
+                       if (fa) {
+                               __xfs_buf_mark_corrupt(bp, fa);
+                               xfs_trans_brelse(args->trans, bp);
+                               xfs_da_mark_sick(args);
+                               return -EFSCORRUPTED;
+                       }
                        ASSERT(bp != NULL);
                        tmp_info = bp->b_addr;
                        ASSERT(tmp_info->magic == old_info->magic);
@@ -1910,6 +1963,13 @@ xfs_da3_blk_link(
                                                &bp, args->whichfork);
                        if (error)
                                return error;
+                       fa = xfs_da3_header_check(bp, args->owner);
+                       if (fa) {
+                               __xfs_buf_mark_corrupt(bp, fa);
+                               xfs_trans_brelse(args->trans, bp);
+                               xfs_da_mark_sick(args);
+                               return -EFSCORRUPTED;
+                       }
                        ASSERT(bp != NULL);
                        tmp_info = bp->b_addr;
                        ASSERT(tmp_info->magic == old_info->magic);
@@ -1939,6 +1999,7 @@ xfs_da3_blk_unlink(
        struct xfs_da_blkinfo   *tmp_info;
        struct xfs_da_args      *args;
        struct xfs_buf          *bp;
+       xfs_failaddr_t          fa;
        int                     error;
 
        /*
@@ -1969,6 +2030,13 @@ xfs_da3_blk_unlink(
                                                &bp, args->whichfork);
                        if (error)
                                return error;
+                       fa = xfs_da3_header_check(bp, args->owner);
+                       if (fa) {
+                               __xfs_buf_mark_corrupt(bp, fa);
+                               xfs_trans_brelse(args->trans, bp);
+                               xfs_da_mark_sick(args);
+                               return -EFSCORRUPTED;
+                       }
                        ASSERT(bp != NULL);
                        tmp_info = bp->b_addr;
                        ASSERT(tmp_info->magic == save_info->magic);
@@ -1986,6 +2054,13 @@ xfs_da3_blk_unlink(
                                                &bp, args->whichfork);
                        if (error)
                                return error;
+                       fa = xfs_da3_header_check(bp, args->owner);
+                       if (fa) {
+                               __xfs_buf_mark_corrupt(bp, fa);
+                               xfs_trans_brelse(args->trans, bp);
+                               xfs_da_mark_sick(args);
+                               return -EFSCORRUPTED;
+                       }
                        ASSERT(bp != NULL);
                        tmp_info = bp->b_addr;
                        ASSERT(tmp_info->magic == save_info->magic);
@@ -2101,6 +2176,12 @@ xfs_da3_path_shift(
                switch (be16_to_cpu(info->magic)) {
                case XFS_DA_NODE_MAGIC:
                case XFS_DA3_NODE_MAGIC:
+                       fa = xfs_da3_node_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_DA_NODE_MAGIC;
                        xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr,
                                                   bp->b_addr);
@@ -2406,6 +2487,13 @@ xfs_da3_swap_lastblock(
                error = xfs_da3_node_read(tp, dp, sib_blkno, &sib_buf, w);
                if (error)
                        goto done;
+               fa = xfs_da3_header_check(sib_buf, args->owner);
+               if (fa) {
+                       __xfs_buf_mark_corrupt(sib_buf, fa);
+                       xfs_da_mark_sick(args);
+                       error = -EFSCORRUPTED;
+                       goto done;
+               }
                sib_info = sib_buf->b_addr;
                if (XFS_IS_CORRUPT(mp,
                                   be32_to_cpu(sib_info->forw) != last_blkno ||
@@ -2427,6 +2515,13 @@ xfs_da3_swap_lastblock(
                error = xfs_da3_node_read(tp, dp, sib_blkno, &sib_buf, w);
                if (error)
                        goto done;
+               fa = xfs_da3_header_check(sib_buf, args->owner);
+               if (fa) {
+                       __xfs_buf_mark_corrupt(sib_buf, fa);
+                       xfs_da_mark_sick(args);
+                       error = -EFSCORRUPTED;
+                       goto done;
+               }
                sib_info = sib_buf->b_addr;
                if (XFS_IS_CORRUPT(mp,
                                   be32_to_cpu(sib_info->back) != last_blkno ||
@@ -2450,6 +2545,13 @@ xfs_da3_swap_lastblock(
                error = xfs_da3_node_read(tp, dp, par_blkno, &par_buf, w);
                if (error)
                        goto done;
+               fa = xfs_da3_node_header_check(par_buf, args->owner);
+               if (fa) {
+                       __xfs_buf_mark_corrupt(par_buf, fa);
+                       xfs_da_mark_sick(args);
+                       error = -EFSCORRUPTED;
+                       goto done;
+               }
                par_node = par_buf->b_addr;
                xfs_da3_node_hdr_from_disk(dp->i_mount, &par_hdr, par_node);
                if (XFS_IS_CORRUPT(mp,
@@ -2499,6 +2601,13 @@ xfs_da3_swap_lastblock(
                error = xfs_da3_node_read(tp, dp, par_blkno, &par_buf, w);
                if (error)
                        goto done;
+               fa = xfs_da3_node_header_check(par_buf, args->owner);
+               if (fa) {
+                       __xfs_buf_mark_corrupt(par_buf, fa);
+                       xfs_da_mark_sick(args);
+                       error = -EFSCORRUPTED;
+                       goto done;
+               }
                par_node = par_buf->b_addr;
                xfs_da3_node_hdr_from_disk(dp->i_mount, &par_hdr, par_node);
                if (XFS_IS_CORRUPT(mp, par_hdr.level != level)) {
index 99618e0c8a72b7f4ae8ccda4447030d9f07e1ceb..7a004786ee0a2fdd5ecdba07231e0b0911aac02a 100644 (file)
@@ -237,6 +237,7 @@ void        xfs_da3_node_hdr_from_disk(struct xfs_mount *mp,
 void   xfs_da3_node_hdr_to_disk(struct xfs_mount *mp,
                struct xfs_da_intnode *to, struct xfs_da3_icnode_hdr *from);
 xfs_failaddr_t xfs_da3_header_check(struct xfs_buf *bp, xfs_ino_t owner);
+xfs_failaddr_t xfs_da3_node_header_check(struct xfs_buf *bp, xfs_ino_t owner);
 
 extern struct kmem_cache       *xfs_da_state_cache;
 
index f6496e33ff91b49455eb143ca9d34d548d3d0fca..6a621f016f0401f87ac2ef0218fb9079a7f2dcf4 100644 (file)
@@ -239,6 +239,10 @@ xfs_attr_node_list_lookup(
                        goto out_corruptbuf;
                }
 
+               fa = xfs_da3_node_header_check(bp, dp->i_ino);
+               if (fa)
+                       goto out_corruptbuf;
+
                xfs_da3_node_hdr_from_disk(mp, &nodehdr, node);
 
                /* Tree taller than we can handle; bail out! */
@@ -335,6 +339,11 @@ xfs_attr_node_list(
                case XFS_DA_NODE_MAGIC:
                case XFS_DA3_NODE_MAGIC:
                        trace_xfs_attr_list_wrong_blk(context);
+                       fa = xfs_da3_node_header_check(bp, dp->i_ino);
+                       if (fa) {
+                               __xfs_buf_mark_corrupt(bp, fa);
+                               xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
+                       }
                        xfs_trans_brelse(context->tp, bp);
                        bp = NULL;
                        break;