From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1792863566; b=aRr8dRLLaUIY1PEjY75uXqC4AheZRnV9Hg96GIYlio8E6OrogrpHhLnbI/TqiRXHBeu/ uTjK2t0YpHl28tt439G+HWrYs02zRt7OnH9642tbIHvm5vNWJtf570Ac/XeCXHKsnV6V7 X/w+88rmHpWlWTe3RGerhWb3jWC8M/fQC8C67S3ZywA1rFwY2u12FpNk+BxmbgzdEz55b TlzSoBZ3k2hSYmtqz6SySp3yhruJ8l3YiabtZCTbNG3NyM5CLZOP4OFudmyFsaziYRXH7 o1oyxL+hYkt6RXrkPqwQXli3JRfendj+waw4ue+lmG3kbD9Dle85DkOK2KOMe3Ag03g== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792863566; h=Received:DKIM-Signature:Date:To:From:Subject:Message-Id; bh=iyBdgaTXDdpR/5dbtV8pKAOSnT8u/TBILVwnlUhvdAY=; b=u7M8lttSJeZBfyO/u3jWhxoJS3G0QbsWkxU9PB9Gr/+18/FV6NKa4DKQHKNfiKVTau+h yKjFhdP8z0sc/prrk/uKPHJ1yzR69h33LSjGLZMICs+1mM/0J/70OtM3pmNCbFi997VlQ x51fk2U+ikDgJqJD23Sa9KIoMfSKWWiWt1b8mqV7ImJbyzUyrO7rbiJ26dAs0xu7qkwDa Hwhiy3V0GR9yQ06rZTxfKaR6CzGELR4Jx2XyzGmL4VZkZpthMKl50/HxoZZROX8moDGv0 KS4CtXo0HOmm11HGGfQKFmtIqyC0dako6RAy9N2ClrbiiXTPXB9LOEgM+psnV9gOxjg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linux-foundation.org header.i=@linux-foundation.org header.a=rsa-sha256 header.s=korg header.b=s2IcfYhR; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C39AB606A31A for ; Sat, 24 Oct 2026 17:39:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux-foundation.org header.i=@linux-foundation.org header.a=rsa-sha256 header.s=korg header.b=s2IcfYhR Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2F6671F01825; Sat, 24 Oct 2026 17:39:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux-foundation.org; s=korg; t=1792863565; bh=iyBdgaTXDdpR/5dbtV8pKAOSnT8u/TBILVwnlUhvdAY=; h=Date:To:From:Subject; b=s2IcfYhR+Tb2zudO3fPBlRtDPdE/v5N707dHwVKPzX4Fo5o3UoOGA4Tran6p54DbJ nDcVuOaz41/GxdW4QTop4zLKqwWskys2RVHNKRlffGGz3xngjzkZJ/f7weW8YbLoPj iBfWhd2oaWQT2eKK+8pX2nL657VT2Yz/re8vMO0g= Date: Sat, 24 Oct 2026 10:39:24 -0700 To: mm-commits@vger.kernel.org,mark@fasheh.com,jlbec@evilplan.org, heming.zhao@suse.com,joseph.qi@linux.alibaba.com,akpm@linux-foundation.org From: Andrew Morton Subject: + ocfs2-read-the-xattr-entry-hash-before-defragmenting-the-bucket.patch added to mm-nonmm-unstable branch Message-Id: <20261024173925.2F6671F01825@smtp.kernel.org> X-Mailing-List: mm-commits@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list The patch titled Subject: ocfs2: read the xattr entry hash before defragmenting the bucket has been added to the -mm mm-nonmm-unstable branch. Its filename is ocfs2-read-the-xattr-entry-hash-before-defragmenting-the-bucket.patch This patch will shortly appear at https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/ocfs2-read-the-xattr-entry-hash-before-defragmenting-the-bucket.patch This patch will later appear in the mm-nonmm-unstable branch at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm Before you just go and hit "reply", please: a) Consider who else should be cc'ed b) Prefer to cc a suitable mailing list as well c) Ideally: find the original patch on the mailing list and do a reply-to-all to that, adding suitable additional cc's *** Remember to use Documentation/process/submit-checklist.rst when testing your code *** The -mm tree is included into linux-next via various branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm and is updated there most days ------------------------------------------------------ From: Joseph Qi Subject: ocfs2: read the xattr entry hash before defragmenting the bucket Date: Sat, 24 Oct 2026 23:10:07 +0800 Patch series "ocfs2: fix stale xattr entry pointers and empty header reads", v1. 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. This patch (of 6): ocfs2_xattr_set_entry_index_block() takes the hash a bucket split has to work with out of xs->here, but only after ocfs2_xattr_set_entry_bucket() has tried and failed to fit the entry. That call defragments the bucket when it runs out of room, and ocfs2_defrag_xattr_bucket() finishes by sorting the entry array by name hash. lib/sort.c is not stable, so two entries sharing a hash may trade slots and xs->here is left naming an entry other than the one the search found. The hash value itself survives the permutation -- the array is already sorted, so a slot can only exchange with an equal-hash neighbour -- but there is no reason to depend on that. Pick the hash up before the set, where xs->here is unambiguously the entry the search landed on. No functional change intended. Link: https://lore.kernel.org/20261024151012.1059683-1-joseph.qi@linux.alibaba.com Link: https://lore.kernel.org/20261024151012.1059683-2-joseph.qi@linux.alibaba.com Fixes: e39baff18b85 ("ocfs2: use the stored hash when checking xattr bucket collision") Signed-off-by: Joseph Qi Signed-off-by: Andrew Morton Cc: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker --- fs/ocfs2/xattr.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) --- a/fs/ocfs2/xattr.c~ocfs2-read-the-xattr-entry-hash-before-defragmenting-the-bucket +++ a/fs/ocfs2/xattr.c @@ -6100,6 +6100,22 @@ static int ocfs2_xattr_set_entry_index_b trace_ocfs2_xattr_set_entry_index_block(xi->xi_name); + /* + * An existing entry keeps the hash it was stored under, and that is + * the hash a split has to work with. A new entry is stored under the + * unsigned one, which is what ocfs2_xa_add_entry() will write. + * + * Read it before the set below. Filling the bucket may defragment it, + * and ocfs2_defrag_xattr_bucket() re-sorts the entry array, so + * afterwards xs->here only denotes whichever entry the sort left in + * that slot. + */ + if (xs->not_found) + name_hash = ocfs2_xattr_name_hash(inode, xi->xi_name, + xi->xi_name_len); + else + name_hash = le32_to_cpu(xs->here->xe_name_hash); + ret = ocfs2_xattr_set_entry_bucket(inode, xi, xs, ctxt); if (!ret) goto out; @@ -6115,17 +6131,7 @@ static int ocfs2_xattr_set_entry_index_b * the maximum number of collisions we will allow for then is * one bucket's worth, so check it here whether we need to * add a new bucket for the insert. - * - * An existing entry keeps the hash it was stored under, and that is - * the hash a split has to work with. A new entry is stored under the - * unsigned one, which is what ocfs2_xa_add_entry() will write. */ - if (xs->not_found) - name_hash = ocfs2_xattr_name_hash(inode, xi->xi_name, - xi->xi_name_len); - else - name_hash = le32_to_cpu(xs->here->xe_name_hash); - ret = ocfs2_check_xattr_bucket_collision(xs->bucket, name_hash); if (ret) { mlog_errno(ret); _ Patches currently in -mm which might be from joseph.qi@linux.alibaba.com are ocfs2-read-the-xattr-entry-hash-before-defragmenting-the-bucket.patch ocfs2-find-the-xattr-entry-again-after-defragmenting-a-bucket.patch ocfs2-find-the-xattr-entry-again-after-converting-a-block-to-a-bucket.patch ocfs2-do-not-read-the-entry-array-of-an-empty-xattr-bucket.patch ocfs2-do-not-index-before-the-entry-array-when-dividing-an-empty-bucket.patch ocfs2-do-not-read-the-entry-array-of-an-empty-xattr-block.patch