Re: [PATCH] ocfs2/dlm: require a ref for locking_state debugfs open
Joseph Qi <[email protected]> Mon, 1 Jun 2026 20:45:18 +0800
| Newsgroups | dev.linux.lists.ocfs2-devel |
|---|---|
| Message-ID | <[email protected]> |
On 5/31/26 12:47 PM, Zhang Cen wrote:
> debug_lockres_open() copies inode->i_private into struct debug_lockres and
> debug_lockres_release() later drops that pointer with dlm_put(). That only
> works if open successfully pins the struct dlm_ctxt.
>
> Today open calls dlm_grab(dlm) but ignores its return value. Once the last
> domain unregister has removed the context from dlm_domains, dlm_grab()
> returns NULL, yet open still stores the raw pointer and returns success.
> The later release path is outside the debugfs removal barrier, so it can
> call dlm_put() after dlm_free_ctxt_mem() has freed the context. KASAN
> reports this as a slab-use-after-free in dlm_put() called from
> debug_lockres_release().
>
> Fail the open when dlm_grab() cannot acquire the reference and unwind the
> seq_file private state before returning. That keeps locking_state from
> handing out a file descriptor whose release path does not own the dlm_ctxt.
>
> The buggy scenario involves two paths, with each column showing the order
> within that path:
>
> locking_state debugfs open: last domain unregister:
> 1. debug_lockres_open() reads 1. dlm_unregister_domain() calls
> inode->i_private. dlm_complete_dlm_shutdown().
> 2. debug_lockres_open() calls 2. shutdown removes the dlm_ctxt from
> dlm_grab(dlm) and gets NULL. dlm_domains.
> 3. open still stores the raw dlm 3. final teardown reaches
> pointer in dl->dl_ctxt and dlm_free_ctxt_mem() and frees it.
> returns success.
> 4. debug_lockres_release() later
> calls dlm_put(dl->dl_ctxt).
>
> Validation reproduced this kernel report:
> KASAN slab-use-after-free in dlm_put+0x82/0x200
> RIP: 0033:0x7f4d349bc9e0
> The buggy address belongs to the object at ffff888103a3c000 which belongs
> to the cache kmalloc-2k of size 2048
> The buggy address is located 816 bytes inside of freed 2048-byte region
> [ffff888103a3c000, ffff888103a3c800)
> Write of size 4
> Call trace:
> dump_stack_lvl+0x66/0xa0 (?:?)
> print_report+0xd0/0x630 (?:?)
> dlm_put+0x82/0x200 (?:?)
> srso_alias_return_thunk+0x5/0xfbef5 (?:?)
> __virt_addr_valid+0x188/0x2f0 (?:?)
> kasan_report+0xe4/0x120 (?:?)
> kasan_check_range+0x105/0x1b0 (?:?)
> debug_lockres_release+0x53/0x80 (fs/ocfs2/dlm/dlmdebug.c:587)
> dlm_put+0x9/0x200 (?:?)
> debug_lockres_release+0x5c/0x80 (fs/ocfs2/dlm/dlmdebug.c:587)
> full_proxy_release+0x67/0x90 (?:?)
> __fput+0x1df/0x4b0 (?:?)
> do_raw_spin_lock+0x10f/0x1b0 (?:?)
> fput_close_sync+0xd2/0x170 (?:?)
> __x64_sys_close+0x55/0x90 (?:?)
> do_syscall_64+0x10c/0x640 (arch/x86/entry/syscall_64.c:87)
> irqentry_exit+0xac/0x6e0 (?:?)
> entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?)
> Freed by task stack:
> kasan_save_stack+0x33/0x60 (?:?)
> kasan_save_track+0x14/0x30 (?:?)
> kasan_save_free_info+0x3b/0x60 (?:?)
> __kasan_slab_free+0x5f/0x80 (?:?)
> kfree+0x30f/0x580 (?:?)
> dlm_put+0x1ce/0x200 (?:?)
> dlm_unregister_domain+0xf6/0xb30 (?:?)
> o2cb_cluster_disconnect+0x6b/0x90 (?:?)
> ocfs2_cluster_disconnect+0x41/0x70 (?:?)
> ocfs2_dlm_shutdown+0x1c4/0x220 (?:?)
> ocfs2_dismount_volume+0x38a/0x550 (?:?)
> generic_shutdown_super+0xc3/0x220 (?:?)
> kill_block_super+0x29/0x60 (?:?)
> deactivate_locked_super+0x66/0xe0 (?:?)
> cleanup_mnt+0x13d/0x210 (?:?)
> task_work_run+0xfa/0x170 (?:?)
> exit_to_user_mode_loop+0xd6/0x430 (?:?)
> do_syscall_64+0x3cb/0x640 (arch/x86/entry/syscall_64.c:87)
> entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?)
>
> Fixes: 4e3d24ed1a12 ("ocfs2/dlm: Dumps the lockres' into a debugfs file")
> Assisted-by: Codex:gpt-5.5
> Signed-off-by: Zhang Cen <[email protected]>
Looks good.
Reviewed-by: Joseph Qi <[email protected]>
> ---
> fs/ocfs2/dlm/dlmdebug.c | 16 ++++++++++++----
> 1 file changed, 12 insertions(+), 4 deletions(-)
>
> diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c
> index fe4fdd09bae3..564567358620 100644
> --- a/fs/ocfs2/dlm/dlmdebug.c
> +++ b/fs/ocfs2/dlm/dlmdebug.c
> @@ -560,6 +560,7 @@ static int debug_lockres_open(struct inode *inode, struct file *file)
> struct dlm_ctxt *dlm = inode->i_private;
> struct debug_lockres *dl;
> void *buf;
> + int status = -ENOMEM;
>
> buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
> if (!buf)
> @@ -572,16 +573,23 @@ static int debug_lockres_open(struct inode *inode, struct file *file)
> dl->dl_len = PAGE_SIZE;
> dl->dl_buf = buf;
>
> - dlm_grab(dlm);
> - dl->dl_ctxt = dlm;
> + /* ->release uses dl_ctxt after open, so it needs a real pin. */
> + dl->dl_ctxt = dlm_grab(dlm);
> + if (!dl->dl_ctxt) {
> + status = -ENOENT;
> + goto bailseq;
> + }
>
> return 0;
>
> +bailseq:
> + seq_release_private(inode, file);
> bailfree:
> kfree(buf);
> bail:
> - mlog_errno(-ENOMEM);
> - return -ENOMEM;
> + if (status != -ENOENT)
> + mlog_errno(status);
> + return status;
> }
>
> static int debug_lockres_release(struct inode *inode, struct file *file)