| From 2de89e2735de6dd110440cb9a075ce0c07c4ad36 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 15 Nov 2019 21:15:08 -0800 |
| Subject: xfs: fix attr leaf header freemap.size underflow |
| |
| From: Brian Foster <bfoster@redhat.com> |
| |
| [ Upstream commit 2a2b5932db67586bacc560cc065d62faece5b996 ] |
| |
| The leaf format xattr addition helper xfs_attr3_leaf_add_work() |
| adjusts the block freemap in a couple places. The first update drops |
| the size of the freemap that the caller had already selected to |
| place the xattr name/value data. Before the function returns, it |
| also checks whether the entries array has encroached on a freemap |
| range by virtue of the new entry addition. This is necessary because |
| the entries array grows from the start of the block (but end of the |
| block header) towards the end of the block while the name/value data |
| grows from the end of the block in the opposite direction. If the |
| associated freemap is already empty, however, size is zero and the |
| subtraction underflows the field and causes corruption. |
| |
| This is reproduced rarely by generic/070. The observed behavior is |
| that a smaller sized freemap is aligned to the end of the entries |
| list, several subsequent xattr additions land in larger freemaps and |
| the entries list expands into the smaller freemap until it is fully |
| consumed and then underflows. Note that it is not otherwise a |
| corruption for the entries array to consume an empty freemap because |
| the nameval list (i.e. the firstused pointer in the xattr header) |
| starts beyond the end of the corrupted freemap. |
| |
| Update the freemap size modification to account for the fact that |
| the freemap entry can be empty and thus stale. |
| |
| Signed-off-by: Brian Foster <bfoster@redhat.com> |
| Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/xfs/libxfs/xfs_attr_leaf.c | 4 +++- |
| 1 file changed, 3 insertions(+), 1 deletion(-) |
| |
| diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c |
| index f943c77133dcd..de33efc9b4f94 100644 |
| --- a/fs/xfs/libxfs/xfs_attr_leaf.c |
| +++ b/fs/xfs/libxfs/xfs_attr_leaf.c |
| @@ -1451,7 +1451,9 @@ xfs_attr3_leaf_add_work( |
| for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) { |
| if (ichdr->freemap[i].base == tmp) { |
| ichdr->freemap[i].base += sizeof(xfs_attr_leaf_entry_t); |
| - ichdr->freemap[i].size -= sizeof(xfs_attr_leaf_entry_t); |
| + ichdr->freemap[i].size -= |
| + min_t(uint16_t, ichdr->freemap[i].size, |
| + sizeof(xfs_attr_leaf_entry_t)); |
| } |
| } |
| ichdr->usedbytes += xfs_attr_leaf_entsize(leaf, args->index); |
| -- |
| 2.25.1 |
| |