mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2] ocfs2: stabilize the dentry name while tracing reads
@ 2026-10-15 17:36 Cen Zhang
  2026-10-20  2:01 ` Joseph Qi
  0 siblings, 1 reply; 2+ messages in thread
From: Cen Zhang @ 2026-10-15 17:36 UTC (permalink / raw)
  To: mark, jlbec, joseph.qi, heming.zhao, boyu.mt
  Cc: ocfs2-devel, linux-kernel, stable, baijiaju1990, jjzuming, zzzccc427

An open file pins its dentry, but not the external allocation backing
an individual dentry name. ocfs2_file_read_iter() passes d_name.name to
its trace event without holding d_lock or a name snapshot. The event's
__string field needs those bytes for both strlen() sizing and the
subsequent copy.

With the read event enabled, a read of an open long-named file can be
preempted after evaluating the trace argument. A successful concurrent
long-to-short rename of the same dentry can then reclaim that name,
provided no other name snapshot retains it:

  Reader                              Renamer
  ocfs2_file_read_iter()
    load old d_name.name
    <preempted>
                                      vfs_rename()
                                        take_dentry_name_snapshot()
                                        ocfs2_rename()
                                          ocfs2_dentry_move()
                                            d_move()/copy_name()
                                        release_dentry_name_snapshot()
                                      kfree_rcu() reclamation completes
    trace callback calls strlen()
      through the old name pointer

The pointer load precedes the rename, and sizing follows reclamation,
so strlen() reads freed name storage. The tracepoint's SRCU section
protects its probes, and the event callback disables preemption only
after the caller has loaded the name pointer.

Take a dentry name snapshot before tracing and release it after the
trace call. The snapshot copies inline names and pins external name
storage, keeping both the string and its length stable while the event
sizes and copies the name. This avoids holding d_lock across trace
callbacks. Check whether the event is enabled before taking a snapshot
to avoid adding snapshot work to reads when tracing is disabled.

KASAN report as below:

    BUG: KASAN: slab-use-after-free in strlen+0x85/0xa0
    Read of size 1 at addr ffff88810a5b2b18 by task python3/496

    [CPU and host details omitted.]
    Call Trace:
     <TASK>
     dump_stack_lvl+0x93/0xd0
     print_report+0xce/0x630
     ? strlen+0x85/0xa0
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __virt_addr_valid+0x20e/0x420
     ? strlen+0x85/0xa0
     kasan_report+0xe0/0x110
     ? strlen+0x85/0xa0
     strlen+0x85/0xa0
     trace_event_raw_event_ocfs2__file_ops+0xf0/0x480
     ? __pfx_trace_event_raw_event_ocfs2__file_ops+0x10/0x10
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? pmbd_probe_hit_cookie+0xe7/0x1c0
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __pfx_pmbd_probe_hit_cookie+0x10/0x10
     ocfs2_file_read_iter+0x6cd/0xb30
     ? __pfx_ocfs2_file_read_iter+0x10/0x10
     ? selinux_file_permission+0x11d/0x540
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? security_file_permission+0x26/0x80
     vfs_read+0x7d2/0xc20
     ? __pfx_vfs_read+0x10/0x10
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __pfx_do_sys_openat2+0x10/0x10
     ksys_read+0x111/0x200
     ? __pfx_ksys_read+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
    [User register dump omitted.]
     </TASK>

    Allocated by task 489:
     kasan_save_stack+0x33/0x60
     kasan_save_track+0x14/0x30
     __kasan_kmalloc+0xaa/0xb0
     __kmalloc_noprof+0x2de/0x780
     __d_alloc+0x5fc/0x8c0
     d_alloc_parallel+0x106/0x11d0
     lookup_open+0x3ff/0x13e0
     path_openat+0x95e/0x2020
     do_file_open+0x21e/0x470
     do_sys_openat2+0xf4/0x1b0
     __x64_sys_openat+0x136/0x1e0
     do_syscall_64+0x114/0x620
     entry_SYSCALL_64_after_hwframe+0x77/0x7f

    Freed by task 0:
     kasan_save_stack+0x33/0x60
     kasan_save_track+0x14/0x30
     kasan_save_free_info+0x3b/0x60
     __kasan_slab_free+0x5f/0x80
     __rcu_free_sheaf_prepare+0x65/0x2a0
     rcu_free_sheaf_nobarn+0x1d/0x50
     rcu_core+0x6be/0x1ed0
     handle_softirqs+0x1fa/0x8d0
     __irq_exit_rcu+0x113/0x1e0
     irq_exit_rcu+0xe/0x20
     sysvec_apic_timer_interrupt+0x6c/0x80
     asm_sysvec_apic_timer_interrupt+0x1a/0x20

    The buggy address belongs to the object at ffff88810a5b2b00
     which belongs to the cache kmalloc-rcl-192 of size 192
    The buggy address is located 24 bytes inside of
     freed 192-byte region [ffff88810a5b2b00, ffff88810a5b2bc0)

    [Page and shadow-memory dumps omitted.]

Fixes: 468eedde23d6 ("ocfs2: Remove mlog(0) from fs/ocfs2/file.c")
Cc: <stable@vger.kernel.org>
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Assisted-by: LLM

---
Changes in v2:
- Replace d_lock around the tracepoint with a dentry name snapshot so trace
  callbacks run without the caller holding the dentry lock.
- Pass both the name and its length from the snapshot; keep the event
  enabled check and release the snapshot after the trace call.

Link to v1: https://lore.kernel.org/r/pm-ocfs2-objects-candidate-0088-v3-d628f98bfcb6784a0bfb@gmail.com

diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index d6e977ba6565..fc357c227ad8 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -2542,11 +2542,17 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb,
 	int direct_io = iocb->ki_flags & IOCB_DIRECT ? 1 : 0;
 	int nowait = iocb->ki_flags & IOCB_NOWAIT ? 1 : 0;
 
-	trace_ocfs2_file_read_iter(inode, filp, filp->f_path.dentry,
-			(unsigned long long)OCFS2_I(inode)->ip_blkno,
-			filp->f_path.dentry->d_name.len,
-			filp->f_path.dentry->d_name.name,
-			to->nr_segs);	/* GRRRRR */
+	if (trace_ocfs2_file_read_iter_enabled()) {
+		struct dentry *dentry = filp->f_path.dentry;
+		struct name_snapshot name;
+
+		take_dentry_name_snapshot(&name, dentry);
+		trace_ocfs2_file_read_iter(inode, filp, dentry,
+					   OCFS2_I(inode)->ip_blkno,
+					   name.name.len, name.name.name,
+					   to->nr_segs); /* GRRRRR */
+		release_dentry_name_snapshot(&name);
+	}
 
 
 	if (!inode) {

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] ocfs2: stabilize the dentry name while tracing reads
  2026-10-15 17:36 [PATCH v2] ocfs2: stabilize the dentry name while tracing reads Cen Zhang
@ 2026-10-20  2:01 ` Joseph Qi
  0 siblings, 0 replies; 2+ messages in thread
From: Joseph Qi @ 2026-10-20  2:01 UTC (permalink / raw)
  To: Cen Zhang
  Cc: mark, jlbec, heming.zhao, ocfs2-devel, linux-kernel, stable,
	baijiaju1990, jjzuming



On 10/16/26 1:36 AM, Cen Zhang wrote:
> An open file pins its dentry, but not the external allocation backing
> an individual dentry name. ocfs2_file_read_iter() passes d_name.name to
> its trace event without holding d_lock or a name snapshot. The event's
> __string field needs those bytes for both strlen() sizing and the
> subsequent copy.
> 
> With the read event enabled, a read of an open long-named file can be
> preempted after evaluating the trace argument. A successful concurrent
> long-to-short rename of the same dentry can then reclaim that name,
> provided no other name snapshot retains it:
> 
>   Reader                              Renamer
>   ocfs2_file_read_iter()
>     load old d_name.name
>     <preempted>
>                                       vfs_rename()
>                                         take_dentry_name_snapshot()
>                                         ocfs2_rename()
>                                           ocfs2_dentry_move()
>                                             d_move()/copy_name()
>                                         release_dentry_name_snapshot()
>                                       kfree_rcu() reclamation completes
>     trace callback calls strlen()
>       through the old name pointer
> 
> The pointer load precedes the rename, and sizing follows reclamation,
> so strlen() reads freed name storage. The tracepoint's SRCU section
> protects its probes, and the event callback disables preemption only
> after the caller has loaded the name pointer.
> 
> Take a dentry name snapshot before tracing and release it after the
> trace call. The snapshot copies inline names and pins external name
> storage, keeping both the string and its length stable while the event
> sizes and copies the name. This avoids holding d_lock across trace
> callbacks. Check whether the event is enabled before taking a snapshot
> to avoid adding snapshot work to reads when tracing is disabled.
> 
> KASAN report as below:
> 
>     BUG: KASAN: slab-use-after-free in strlen+0x85/0xa0
>     Read of size 1 at addr ffff88810a5b2b18 by task python3/496
> 
>     [CPU and host details omitted.]
>     Call Trace:
>      <TASK>
>      dump_stack_lvl+0x93/0xd0
>      print_report+0xce/0x630
>      ? strlen+0x85/0xa0
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? __virt_addr_valid+0x20e/0x420
>      ? strlen+0x85/0xa0
>      kasan_report+0xe0/0x110
>      ? strlen+0x85/0xa0
>      strlen+0x85/0xa0
>      trace_event_raw_event_ocfs2__file_ops+0xf0/0x480
>      ? __pfx_trace_event_raw_event_ocfs2__file_ops+0x10/0x10
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? pmbd_probe_hit_cookie+0xe7/0x1c0
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? __pfx_pmbd_probe_hit_cookie+0x10/0x10
>      ocfs2_file_read_iter+0x6cd/0xb30
>      ? __pfx_ocfs2_file_read_iter+0x10/0x10
>      ? selinux_file_permission+0x11d/0x540
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? security_file_permission+0x26/0x80
>      vfs_read+0x7d2/0xc20
>      ? __pfx_vfs_read+0x10/0x10
>      ? srso_alias_return_thunk+0x5/0xfbef5
>      ? __pfx_do_sys_openat2+0x10/0x10
>      ksys_read+0x111/0x200
>      ? __pfx_ksys_read+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
>     [User register dump omitted.]
>      </TASK>
> 
>     Allocated by task 489:
>      kasan_save_stack+0x33/0x60
>      kasan_save_track+0x14/0x30
>      __kasan_kmalloc+0xaa/0xb0
>      __kmalloc_noprof+0x2de/0x780
>      __d_alloc+0x5fc/0x8c0
>      d_alloc_parallel+0x106/0x11d0
>      lookup_open+0x3ff/0x13e0
>      path_openat+0x95e/0x2020
>      do_file_open+0x21e/0x470
>      do_sys_openat2+0xf4/0x1b0
>      __x64_sys_openat+0x136/0x1e0
>      do_syscall_64+0x114/0x620
>      entry_SYSCALL_64_after_hwframe+0x77/0x7f
> 
>     Freed by task 0:
>      kasan_save_stack+0x33/0x60
>      kasan_save_track+0x14/0x30
>      kasan_save_free_info+0x3b/0x60
>      __kasan_slab_free+0x5f/0x80
>      __rcu_free_sheaf_prepare+0x65/0x2a0
>      rcu_free_sheaf_nobarn+0x1d/0x50
>      rcu_core+0x6be/0x1ed0
>      handle_softirqs+0x1fa/0x8d0
>      __irq_exit_rcu+0x113/0x1e0
>      irq_exit_rcu+0xe/0x20
>      sysvec_apic_timer_interrupt+0x6c/0x80
>      asm_sysvec_apic_timer_interrupt+0x1a/0x20
> 
>     The buggy address belongs to the object at ffff88810a5b2b00
>      which belongs to the cache kmalloc-rcl-192 of size 192
>     The buggy address is located 24 bytes inside of
>      freed 192-byte region [ffff88810a5b2b00, ffff88810a5b2bc0)
> 
>     [Page and shadow-memory dumps omitted.]
> 
> Fixes: 468eedde23d6 ("ocfs2: Remove mlog(0) from fs/ocfs2/file.c")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
> Assisted-by: LLM
> 
> ---
> Changes in v2:
> - Replace d_lock around the tracepoint with a dentry name snapshot so trace
>   callbacks run without the caller holding the dentry lock.
> - Pass both the name and its length from the snapshot; keep the event
>   enabled check and release the snapshot after the trace call.
> 
> Link to v1: https://lore.kernel.org/r/pm-ocfs2-objects-candidate-0088-v3-d628f98bfcb6784a0bfb@gmail.com
> 
> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> index d6e977ba6565..fc357c227ad8 100644
> --- a/fs/ocfs2/file.c
> +++ b/fs/ocfs2/file.c
> @@ -2542,11 +2542,17 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb,
>  	int direct_io = iocb->ki_flags & IOCB_DIRECT ? 1 : 0;
>  	int nowait = iocb->ki_flags & IOCB_NOWAIT ? 1 : 0;
>  
> -	trace_ocfs2_file_read_iter(inode, filp, filp->f_path.dentry,
> -			(unsigned long long)OCFS2_I(inode)->ip_blkno,
> -			filp->f_path.dentry->d_name.len,
> -			filp->f_path.dentry->d_name.name,
> -			to->nr_segs);	/* GRRRRR */
> +	if (trace_ocfs2_file_read_iter_enabled()) {
> +		struct dentry *dentry = filp->f_path.dentry;
> +		struct name_snapshot name;
> +
> +		take_dentry_name_snapshot(&name, dentry);
> +		trace_ocfs2_file_read_iter(inode, filp, dentry,
> +					   OCFS2_I(inode)->ip_blkno,
> +					   name.name.len, name.name.name,
> +					   to->nr_segs); /* GRRRRR */
> +		release_dentry_name_snapshot(&name);
> +	}
>  
>  
>  	if (!inode) {

The change looks fine.
Six call sites feed the same ocfs2__file_ops class, which has the single
__string(d_name, d_name), so it seems we'd better send a series to fix
all of them:
  trace_ocfs2_file_open
  trace_ocfs2_file_release
  trace_ocfs2_sync_file
  trace_ocfs2_file_read_iter
  trace_ocfs2_file_write_iter
  trace_ocfs2_file_splice_read

Thanks,
Joseph

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-20  2:01 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-15 17:36 [PATCH v2] ocfs2: stabilize the dentry name while tracing reads Cen Zhang
2026-10-20  2:01 ` Joseph Qi

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®