On 2026/7/23 8:52, Steven Rostedt wrote:
> 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

I took a closer look at the lockdep report and confirmed the deadlock
indeed exists:

Path 1 (write to set_ftrace_pid):
ftrace_pid_write
        pid_write
                guard(mutex)(&ftrace_lock);
                trace_pid_write
                        mutex_lock(&parser.lock);

Path 2 (write to set_ftrace_filter):
ftrace_regex_write
        guard(mutex)(&parser->lock);
        ftrace_process_regex
                p->func
                        register_ftrace_function_probe
                                mutex_lock(&ftrace_lock);

So Path 1 takes ftrace_lock then parser->lock, while Path 2 takes
parser->lock then ftrace_lock. Classic AB-BA deadlock.

The original v1 patch did not have this issue because it did not add
parser->lock in the trace_pid_write path.

OK, I will revert to the v1 approach and rework it based on your
suggestion: introduce a global parser_lock for the ftrace parsing code,
replace all mutex_lock(&parser->lock) with mutex_lock(&parser_lock),
and add a comment explaining what the lock protects.

Will send v2 shortly.

Thanks,
Tengda


Reply via email to