The da-node verifier uses the larger of the directory and attribute geometry limits, so an attribute node can pass verification with more entries than its buffer holds. xfs_da3_path_shift() can load such a node without passing through the lookup path's geometry check, then index btree[] beyond the buffer. Bound the count against args->geo->node_ents before indexing the entries and report filesystem corruption if it exceeds that limit. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Shengzhuo Wei --- fs/xfs/libxfs/xfs_da_btree.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c index faf88488265c..88788ece69c6 100644 --- a/fs/xfs/libxfs/xfs_da_btree.c +++ b/fs/xfs/libxfs/xfs_da_btree.c @@ -2242,6 +2242,11 @@ xfs_da3_path_shift( xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, bp->b_addr); btree = nodehdr.btree; + if (nodehdr.count > args->geo->node_ents) { + xfs_buf_mark_corrupt(blk->bp); + xfs_da_mark_sick(args); + return -EFSCORRUPTED; + } blk->hashval = be32_to_cpu(btree[nodehdr.count - 1].hashval); if (forward) blk->index = 0; -- 2.47.3 The da-node verifier can accept an attribute node count that only fits a larger directory block. Inactivation bypasses the lookup path's geometry check, so __xfs_da3_node_remove() can read and write beyond the attribute buffer. For a v5 filesystem with 4 KiB blocks and 64 KiB directory blocks, count 8184 causes a 65464-byte memmove. Reject counts above mp->m_attr_geo->node_ents before removing entries. This also bounds subsequent removals, since the count only decreases. Fixes: b854e1c4eff3 ("xfs: close crash window in attr dabtree inactivation") Cc: stable@vger.kernel.org Signed-off-by: Shengzhuo Wei --- fs/xfs/xfs_attr_inactive.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c index a5b69c0fbfd05..b8230fa458fff 100644 --- a/fs/xfs/xfs_attr_inactive.c +++ b/fs/xfs/xfs_attr_inactive.c @@ -153,6 +153,14 @@ xfs_attr3_node_inactive( } xfs_da3_node_hdr_from_disk(mp, &ichdr, bp->b_addr); + + if (ichdr.count > mp->m_attr_geo->node_ents) { + xfs_buf_mark_corrupt(bp); + xfs_trans_brelse(*trans, bp); + xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK); + return -EFSCORRUPTED; + } + parent_blkno = xfs_buf_daddr(bp); if (!ichdr.count) { xfs_trans_brelse(*trans, bp); -- 2.47.3 xfs_attr_node_list_lookup() walks the attribute dabtree with the on-disk node count and indexes btree[] without a fork-specific geometry check. The node verifier only bounds the count against the larger of the directory and attribute geometries, so a crafted attr node can pass verification with more entries than its buffer holds. Reject counts above mp->m_attr_geo->node_ents before the btree[] walk. Use the existing corruption and sick reporting path. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Shengzhuo Wei --- fs/xfs/xfs_attr_list.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c index f751edb930b6..667740e37c83 100644 --- a/fs/xfs/xfs_attr_list.c +++ b/fs/xfs/xfs_attr_list.c @@ -258,6 +258,9 @@ xfs_attr_node_list_lookup( if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) goto out_corruptbuf; + if (nodehdr.count > mp->m_attr_geo->node_ents) + goto out_corruptbuf; + /* Check the level from the root node. */ if (cursor->blkno == 0) expected_level = nodehdr.level - 1; -- 2.47.3