On Wed, 15 Jul 2026 08:19:37 +0000
Tengda Wu <[email protected]> wrote:

> The trace_parser structure is allocated and initialized when a trace
> file is opened, and is subsequently used in the write handler to parse
> user input. If userspace opens a trace file descriptor and shares it
> across multiple threads, concurrent write calls will race on the
> parser's internal state, specifically the idx, cont, and buffer fields,
> leading to corrupted input or undefined behavior.
> 
> Fix this by embedding a mutex directly in struct trace_parser. The mutex
> is initialized in trace_parser_get_init() and destroyed in
> trace_parser_put(). All write-side users that access parser state
> (trace_get_user() followed by checking trace_parser_loaded() /
> trace_parser_cont() against the buffer) now hold the mutex across the
> full critical section, avoiding any TOCTOU gap between the parse and the
> subsequent consumption of parser->buffer.
> 
> Fixes: e704eff3ff51 ("ftrace: Have set_graph_function handle multiple 
> functions in one write")
> Fixes: 689fd8b65d66 ("tracing: trace parser support for function and graph")
> Cc: [email protected]
> Signed-off-by: Tengda Wu <[email protected]>
> ---
> v2: Add proper lockdep assertions to enforce that the parser lock is
>     held by all callers (Steven).
> v1: 
> https://lore.kernel.org/all/[email protected]/

This triggered a lockdep splat:

[  214.921782] ======================================================
[  214.924654] WARNING: possible circular locking dependency detected
[  214.927502] 7.2.0-rc4-test-00009-gdf004e09835f-dirty #25 Not tainted
[  214.930417] ------------------------------------------------------
[  214.933274] ftrace-test-mco/3440 is trying to acquire lock:
[  214.935879] ffffffff9c3f3a00 (ftrace_lock){+.+.}-{4:4}, at: 
register_ftrace_function_probe+0x46/0x580
[  214.939970] 
[  214.939970] but task is already holding lock:
[  214.942531] ffffffff9c3f3440 (ftrace_cmd_mutex){+.+.}-{4:4}, at: 
ftrace_process_regex.isra.0+0x68/0x120
[  214.946327] 
[  214.946327] which lock already depends on the new lock.
[  214.946327] 
[  214.949491] 
[  214.949491] the existing dependency chain (in reverse order) is:
[  214.952351] 
[  214.952351] -> #2 (ftrace_cmd_mutex){+.+.}-{4:4}:
[  214.954682]        __mutex_lock+0xdb/0x10a0
[  214.956466]        ftrace_process_regex.isra.0+0x68/0x120
[  214.958555]        ftrace_regex_write.part.0.isra.0+0xc5/0x150
[  214.960767]        vfs_write+0xd0/0x5a0
[  214.962311]        ksys_write+0x79/0xf0
[  214.963871]        do_syscall_64+0x7e/0x7d0
[  214.965444]        entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  214.967425] 
[  214.967425] -> #1 (&parser->lock){+.+.}-{4:4}:
[  214.969537]        __mutex_lock+0xdb/0x10a0
[  214.971054]        trace_pid_write+0x105/0x2d0
[  214.972666]        pid_write.isra.0+0xc0/0x360
[  214.974263]        vfs_write+0xd0/0x5a0
[  214.975699]        ksys_write+0x79/0xf0
[  214.977119]        do_syscall_64+0x7e/0x7d0
[  214.978633]        entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  214.980544] 
[  214.980544] -> #0 (ftrace_lock){+.+.}-{4:4}:
[  214.982584]        __lock_acquire+0x1496/0x23b0
[  214.984211]        lock_acquire+0xdb/0x310
[  214.985702]        __mutex_lock+0xdb/0x10a0
[  214.987220]        register_ftrace_function_probe+0x46/0x580
[  214.989169]        ftrace_trace_probe_callback.isra.0+0x76/0xe0
[  214.991168]        ftrace_process_regex.isra.0+0xb2/0x120
[  214.993036]        ftrace_regex_write.part.0.isra.0+0xc5/0x150
[  214.995018]        vfs_write+0xd0/0x5a0
[  214.996448]        ksys_write+0x79/0xf0
[  214.997873]        do_syscall_64+0x7e/0x7d0
[  214.999399]        entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  215.001301] 
[  215.001301] other info that might help us debug this:
[  215.001301] 
[  215.004055] Chain exists of:
[  215.004055]   ftrace_lock --> &parser->lock --> ftrace_cmd_mutex
[  215.004055] 
[  215.007461]  Possible unsafe locking scenario:
[  215.007461] 
[  215.009549]        CPU0                    CPU1
[  215.011160]        ----                    ----
[  215.012790]   lock(ftrace_cmd_mutex);
[  215.014161]                                lock(&parser->lock);
[  215.016174]                                lock(ftrace_cmd_mutex);
[  215.018247]   lock(ftrace_lock);
[  215.019513] 
[  215.019513]  *** DEADLOCK ***
[  215.019513] 
[  215.021679] 3 locks held by ftrace-test-mco/3440:
[  215.023342]  #0: ffff8bf9c16d5450 (sb_writers#14){.+.+}-{0:0}, at: 
ksys_write+0x79/0xf0
[  215.026030]  #1: ffff8bf9c44108b0 (&parser->lock){+.+.}-{4:4}, at: 
ftrace_regex_write.part.0.isra.0+0x47/0x150
[  215.029259]  #2: ffffffff9c3f3440 (ftrace_cmd_mutex){+.+.}-{4:4}, at: 
ftrace_process_regex.isra.0+0x68/0x120
[  215.032448] 
[  215.032448] stack backtrace:
[  215.034100] CPU: 1 UID: 0 PID: 3440 Comm: ftrace-test-mco Not tainted 
7.2.0-rc4-test-00009-gdf004e09835f-dirty #25 PREEMPT(lazy) 
[  215.034104] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 
1.17.0-debian-1.17.0-1 04/01/2014
[  215.034105] Call Trace:
[  215.034107]  <TASK>
[  215.034109]  dump_stack_lvl+0x6e/0xa0
[  215.034118]  print_circular_bug.cold+0x185/0x1d0
[  215.034125]  check_noncircular+0x148/0x170
[  215.034130]  __lock_acquire+0x1496/0x23b0
[  215.034133]  ? save_trace+0x53/0x360
[  215.034140]  lock_acquire+0xdb/0x310
[  215.034142]  ? register_ftrace_function_probe+0x46/0x580
[  215.034149]  __mutex_lock+0xdb/0x10a0
[  215.034153]  ? register_ftrace_function_probe+0x46/0x580
[  215.034156]  ? ftrace_process_regex.isra.0+0x68/0x120
[  215.034159]  ? register_ftrace_function_probe+0x46/0x580
[  215.034162]  ? lock_acquire+0xeb/0x310
[  215.034167]  ? register_ftrace_function_probe+0x46/0x580
[  215.034170]  register_ftrace_function_probe+0x46/0x580
[  215.034174]  ? ftrace_process_regex.isra.0+0x68/0x120
[  215.034176]  ? __might_fault+0x44/0xa0
[  215.034182]  ftrace_trace_probe_callback.isra.0+0x76/0xe0
[  215.034187]  ftrace_process_regex.isra.0+0xb2/0x120
[  215.034191]  ftrace_regex_write.part.0.isra.0+0xc5/0x150
[  215.034195]  vfs_write+0xd0/0x5a0
[  215.034200]  ? rcu_is_watching+0x11/0x50
[  215.034206]  ? fput_close_sync+0x79/0xe0
[  215.034211]  ksys_write+0x79/0xf0
[  215.034214]  do_syscall_64+0x7e/0x7d0
[  215.034218]  ? trace_hardirqs_off+0xd/0x30
[  215.034222]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  215.034224] RIP: 0033:0x7feefb1a17d2
[  215.034228] Code: 18 41 8b 93 08 03 00 00 59 5e 48 83 f8 fc 75 1a 83 e2 39 
83 fa 08 75 12 e8 2b ff ff ff 0f 1f 00 49 89 ca 48 8b 44 24 20 0f 05 <48> 83 c4 
18 c3 66 0f 1f 84 00 00 00 00 00 48 83 ec 10 ff 74 24 18
[  215.034230] RSP: 002b:00007ffe0dae2bc0 EFLAGS: 00000202 ORIG_RAX: 
0000000000000001
[  215.034233] RAX: ffffffffffffffda RBX: 00007feefb2f95c0 RCX: 00007feefb1a17d2
[  215.034235] RDX: 0000000000000014 RSI: 000055d4f7f6d5d0 RDI: 0000000000000001
[  215.034237] RBP: 0000000000000014 R08: 0000000000000000 R09: 0000000000000000
[  215.034238] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000014
[  215.034240] R13: 000055d4f7f6d5d0 R14: 000055d4f7f6f3f0 R15: 0000000000000008
[  215.034245]  </TASK>

Your original patch doesn't have this issue. But I still hate the fact
that the lock is part of the parser. I would say let's make a global
mutex for the parsing in the ftrace code (parser_lock) and replace all
the mutex_lock(&parser->lock) with mutex_lock(&parser_lock).

Also add a comment to explain what the lock is protecting by the
declaration of the lock.

Thanks,

-- Steve

Reply via email to