From: Joseph Qi <joseph.qi@linux.alibaba.com>
To: Cen Zhang <zzzccc427@gmail.com>,
Andrew Morton <akpm@linux-foundation.org>
Cc: mark@fasheh.com, jlbec@evilplan.org, heming.zhao@suse.com,
ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org,
stable@vger.kernel.org, baijiaju1990@gmail.com,
jjzuming@gmail.com
Subject: Re: [PATCH] ocfs2: Pin refcount trees before dropping the lookup lock
Date: Mon, 19 Oct 2026 19:06:57 +0800 [thread overview]
Message-ID: <6cb5a64a-02cd-4230-a49b-08c704d10871@linux.alibaba.com> (raw)
In-Reply-To: <pm-ocfs2-objects-candidate-0037-v4-9f1a261dfbc341721689@gmail.com>
On 10/14/26 11:47 AM, Cen Zhang wrote:
> A refcount tree returned to ocfs2_lock_refcount_tree() must stay alive
> until that caller releases its operation reference. However,
> ocfs2_get_refcount_tree() returns the cached pointer after dropping
> osb_lock, and the caller only takes its kref afterwards. The registry
> reference does not protect this gap from concurrent removal.
>
> This can happen when another node frees a refcount root and reuses its
> block with a different generation while this mount retains the old
> cached tree. Two local operations on different inodes sharing the new
> root, such as data CoW and an external xattr update, can then look up
> the same stale tree. Their inode locks are independent, and both the
> REFCOUNT DLM lock and rf_sem are acquired after the first operation
> reference.
>
> The following ordering is possible, with no other operation references:
>
> Data CoW Xattr update
> -------- ------------
> Look up the old tree under
> osb_lock, then drop osb_lock
> Look up the same tree and take a kref
> Acquire the tree locks and read the
> new on-disk generation
> Erase the old tree under osb_lock
> Unlock and drop the operation kref
> Drop the registry kref, freeing tree
> Take a kref through the
> freed tree pointer
>
> The final kref_get() accesses freed rf_getcnt storage, and subsequent
> accesses in ocfs2_lock_refcount_tree() also use the freed allocation.
>
> Take the operation reference in ocfs2_get_refcount_tree() before
> releasing osb_lock, including when an insertion loses to another lookup.
> Removal must acquire the same lock, so it cannot drop the registry
> reference before the lookup has pinned the tree. Keep the existing
> unlock and error path puts. The two callers that already hold the
> refcount tree lock keep their borrowed lookup behavior and do not
> acquire an extra reference.
>
> KASAN report as below:
>
> BUG: KASAN: slab-use-after-free in ocfs2_lock_refcount_tree+0x94a/0x9b0
> Read of size 4 at addr ffff888117212820 by task python3/497
>
> [Kernel and hardware identification omitted.]
>
> Call Trace:
> <TASK>
> dump_stack_lvl+0x93/0xd0
> print_report+0xce/0x630
> ? ocfs2_lock_refcount_tree+0x94a/0x9b0
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? __virt_addr_valid+0x20e/0x420
> ? ocfs2_lock_refcount_tree+0x94a/0x9b0
> kasan_report+0xe0/0x110
> ? ocfs2_lock_refcount_tree+0x94a/0x9b0
> ocfs2_lock_refcount_tree+0x94a/0x9b0
> ? __pfx_ocfs2_lock_refcount_tree+0x10/0x10
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? ocfs2_refcount_cow+0x3b6/0xd90
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? __kmalloc_cache_noprof+0x28d/0x610
> ? ocfs2_refcount_cow+0x3b6/0xd90
> ocfs2_refcount_cow+0x410/0xd90
> ? __pfx_ocfs2_refcount_cow+0x10/0x10
> ? __pfx___might_resched+0x10/0x10
> ? down_write+0x11a/0x1f0
> ? __pfx_down_write+0x10/0x10
> ? ocfs2_inode_unlock+0x1a9/0x260
> ? __pfx_ocfs2_inode_unlock+0x10/0x10
> ? ocfs2_file_write_iter+0x1413/0x1d30
> ocfs2_file_write_iter+0x157b/0x1d30
> ? __pfx_ocfs2_file_write_iter+0x10/0x10
> ? lock_acquire+0x190/0x300
> ? __x64_sys_pwrite64+0x1b7/0x220
> ? __pfx___might_resched+0x10/0x10
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? security_file_permission+0x26/0x80
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? lock_is_held_type+0x8f/0x100
> vfs_write+0x639/0x1010
> ? __pfx_ocfs2_file_write_iter+0x10/0x10
> ? lock_vma_under_rcu+0x154/0x4a0
> ? __pfx_vfs_write+0x10/0x10
> ? lock_release+0x121/0x290
> ? srso_alias_return_thunk+0x5/0xfbef5
> __x64_sys_pwrite64+0x1b7/0x220
> ? __pfx___x64_sys_pwrite64+0x10/0x10
> ? irqentry_exit+0xbe/0x820
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? trace_hardirqs_on_prepare+0x11d/0x150
> ? srso_alias_return_thunk+0x5/0xfbef5
> do_syscall_64+0x114/0x620
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> [Register dump omitted.]
>
> </TASK>
>
> Allocated by task 491:
> kasan_save_stack+0x33/0x60
> kasan_save_track+0x14/0x30
> __kasan_kmalloc+0xaa/0xb0
> __kmalloc_cache_noprof+0x28d/0x610
> ocfs2_allocate_refcount_tree+0x49/0x1a0
> ocfs2_create_refcount_tree+0x5d7/0x1630
> ocfs2_attach_refcount_tree+0x57d/0x1100
> __ocfs2_reflink.isra.0+0x1c4/0x1940
> ocfs2_reflink+0x501/0x9b0
> ocfs2_reflink_ioctl+0x6b2/0x920
> ocfs2_ioctl+0x538/0x640
> __x64_sys_ioctl+0x163/0x1d0
> do_syscall_64+0x114/0x620
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> Freed by task 498:
> kasan_save_stack+0x33/0x60
> kasan_save_track+0x14/0x30
> kasan_save_free_info+0x3b/0x60
> __kasan_slab_free+0x5f/0x80
> kfree+0x308/0x580
> ocfs2_lock_refcount_tree+0x672/0x9b0
> ocfs2_xattr_set+0x72c/0x2530
> ocfs2_xattr_user_set+0xaf/0xf0
> __vfs_setxattr+0x143/0x1b0
> __vfs_setxattr_noperm+0x112/0x5b0
> __vfs_setxattr_locked+0xfe/0x2b0
> vfs_setxattr+0x13c/0x370
> do_setxattr+0x128/0x170
> filename_setxattr+0x147/0x190
> path_setxattrat+0x1ac/0x260
> __x64_sys_setxattr+0xcb/0x140
> do_syscall_64+0x114/0x620
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> The buggy address belongs to the object at ffff888117212800
> which belongs to the cache kmalloc-1k of size 1024
> The buggy address is located 32 bytes inside of
> freed 1024-byte region [ffff888117212800, ffff888117212c00)
>
> [Page and memory dumps omitted.]
>
> Fixes: 374a263e790c ("ocfs2: Add refcount tree lock mechanism.")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
> Assisted-by: LLM
>
Looks fine.
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> ---
> diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c
> index 3e9cccf06e48..4f13b5a6faa7 100644
> --- a/fs/ocfs2/refcounttree.c
> +++ b/fs/ocfs2/refcounttree.c
> @@ -375,8 +375,10 @@ ocfs2_allocate_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno)
> return new;
> }
>
> +/* Callers without the refcount tree lock must request a reference. */
> static int ocfs2_get_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno,
> - struct ocfs2_refcount_tree **ret_tree)
> + struct ocfs2_refcount_tree **ret_tree,
> + bool get_ref)
> {
> int ret = 0;
> struct ocfs2_refcount_tree *tree, *new = NULL;
> @@ -433,6 +435,8 @@ static int ocfs2_get_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno,
> new = NULL;
>
> out:
> + if (get_ref)
> + ocfs2_refcount_tree_get(tree);
> *ret_tree = tree;
>
> osb->osb_ref_tree_lru = tree;
> @@ -506,14 +510,12 @@ int ocfs2_lock_refcount_tree(struct ocfs2_super *osb,
> struct ocfs2_refcount_block *rb;
>
> again:
> - ret = ocfs2_get_refcount_tree(osb, ref_blkno, &tree);
> + ret = ocfs2_get_refcount_tree(osb, ref_blkno, &tree, true);
> if (ret) {
> mlog_errno(ret);
> return ret;
> }
>
> - ocfs2_refcount_tree_get(tree);
> -
> ret = __ocfs2_lock_refcount_tree(osb, tree, rw);
> if (ret) {
> mlog_errno(ret);
> @@ -2355,7 +2357,8 @@ int ocfs2_decrease_refcount(struct inode *inode,
> goto out;
> }
>
> - ret = ocfs2_get_refcount_tree(OCFS2_SB(inode->i_sb), ref_blkno, &tree);
> + ret = ocfs2_get_refcount_tree(OCFS2_SB(inode->i_sb), ref_blkno,
> + &tree, false);
> if (ret) {
> mlog_errno(ret);
> goto out;
> @@ -2590,7 +2593,7 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode,
> BUG_ON(!ocfs2_is_refcount_inode(inode));
>
> ret = ocfs2_get_refcount_tree(OCFS2_SB(inode->i_sb),
> - refcount_loc, &tree);
> + refcount_loc, &tree, false);
> if (ret) {
> mlog_errno(ret);
> goto out;
prev parent reply other threads:[~2026-10-19 11:07 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 3:47 Cen Zhang
2026-10-19 11:06 ` Joseph Qi [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6cb5a64a-02cd-4230-a49b-08c704d10871@linux.alibaba.com \
--to=joseph.qi@linux.alibaba.com \
--cc=akpm@linux-foundation.org \
--cc=baijiaju1990@gmail.com \
--cc=heming.zhao@suse.com \
--cc=jjzuming@gmail.com \
--cc=jlbec@evilplan.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark@fasheh.com \
--cc=ocfs2-devel@lists.linux.dev \
--cc=stable@vger.kernel.org \
--cc=zzzccc427@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®