[PATCH 0/6] ocfs2: fix stale xattr entry pointers and empty header reads
From: Joseph Qi
Date: Sat Oct 24 2026 - 11:10:49 EST
Five defects in the ocfs2 extended attribute code, plus one robustness
measure that belongs with them, all on the paths that move entries
between a block, a bucket and an index tree. They fall into two groups.
Stale entry pointers (patches 1 to 3)
ocfs2_xa_set() writes through a pointer into xh_entries[] that two of
its callers invalidate first. Defragmenting a bucket ends in a sort()
on xe_name_hash, and lib/sort.c is an unstable heapsort, so entries that
share a hash can trade slots. Copying a block into a bucket changes the
order outright, since a block holds its entries in insertion order while
a bucket holds them by hash, yet ocfs2_xattr_update_xattr_search()
relocates the search by the index the entry had in the block. Neither
path compares the entry's name with the one being set, so setxattr() can
replace a neighbouring attribute's value and still report success, or
trip ocfs2_xa_reuse_entry()'s BUG_ON. Patch 1 takes the hash before the
sort; patches 2 and 3 look the entry up again afterwards.
Reads of an empty entry array (patches 4 to 6)
A block and a bucket may both hold no entries: ocfs2_xa_remove_entry()
keeps the last slot when it removes the only one, so an empty bucket
still carries the hash it is keyed by. Three sites index xh_entries[]
without checking xh_count and so read xh_entries[-1], which lands on the
header and comes back as xh_count | xh_free_start << 16. Nothing
faults, but the value is acted on: the collision check reports no
collision for an empty bucket, the divide commits a sentinel hash that
hides part of the key space from ocfs2_xattr_bucket_find(), and the
block to bucket copy claims storage the bucket does not hold. Patches 4
to 6 guard each site and keep the one read of xh_entries[0] that
ocfs2_xa_remove_entry() documents as intentional.
Joseph Qi (6):
ocfs2: read the xattr entry hash before defragmenting the bucket
ocfs2: find the xattr entry again after defragmenting a bucket
ocfs2: find the xattr entry again after converting a block to a bucket
ocfs2: do not read the entry array of an empty xattr bucket
ocfs2: do not index before the entry array when dividing an empty
bucket
ocfs2: do not read the entry array of an empty xattr block
fs/ocfs2/xattr.c | 146 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 123 insertions(+), 23 deletions(-)
--
2.39.3