Offering: HULK hulk inclusion category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/8353 -------------------------------- [BUG] A KASAN out-of-bounds issue was discovered during syzkaller testing: BUG: unable to handle page fault for address: ffffed101c3f4de7 ... ...... Call Trace: xfs_dir2_leaf_addname+0xc70/0x1060 xfs_dir_createname+0x3a6/0x4a0 xfs_create+0x6ff/0x8b0 ...... [CAUSE] The root cause is mounting a corrupted XFS image where the physical block of a data block overlaps with the physical block of a leaf block. The process is as follows: 1) User called getdents. Then xfs_readdir() is called to pre-read the xfs_buf corresponding to the data block. 2) Then, when creating a file in the corresponding directory, the leaf reuses the previous xfs_buf because of the same value of startblock and blockcnt in record. 3) In xfs_dir2_leaf_addname(), "bestsp" is calculated by subtracting the ltp->bestcount size from an address. At this point, bestcount is actually xfs_dir2_data_unused->tag from the data block, which represents the offset of the current unused space, making it easy to trigger an out-of-bounds access on bestsp. Subsequent accesses to bestsp lead to this issue. [FIX] The xfs_dir3_leaf_read() function does not perform further validation on the acquired xfs_buf. Following the logic in xfs_dir3_data_read() and linux-mainline, apply the validation to leaf/leafn read to detect errors early and prevent subsequent KASAN issues. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Zizhi Wo <wozizhi@huawei.com> --- fs/xfs/libxfs/xfs_dir2_leaf.c | 51 +++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c index bb18323fd1ed..553ca8526c89 100644 --- a/fs/xfs/libxfs/xfs_dir2_leaf.c +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c @@ -203,6 +203,29 @@ xfs_dir3_leaf_verify( return xfs_dir3_leaf_check_int(mp, &leafhdr, bp->b_addr); } +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) @@ -269,11 +292,23 @@ xfs_dir3_leaf_read( 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, dp->i_ino); + if (fa) { + __xfs_buf_mark_corrupt(*bpp, fa); + xfs_trans_brelse(tp, *bpp); + *bpp = NULL; + return -EFSCORRUPTED; + } + + if (tp) xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAF1_BUF); return err; } @@ -285,11 +320,23 @@ xfs_dir3_leafn_read( 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, dp->i_ino); + if (fa) { + __xfs_buf_mark_corrupt(*bpp, fa); + xfs_trans_brelse(tp, *bpp); + *bpp = NULL; + return -EFSCORRUPTED; + } + + if (tp) xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAFN_BUF); return err; } -- 2.39.2